diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md index fed16a8d28acd..ea82a9a28642d 100644 --- a/.github/ISSUE_TEMPLATE/crash.md +++ b/.github/ISSUE_TEMPLATE/crash.md @@ -15,7 +15,7 @@ labels: "crash" **Traceback** -``` +```python-traceback (Insert traceback and other messages from mypy here -- use `--show-traceback`.) ``` @@ -25,6 +25,11 @@ labels: "crash" appreciated. We also very much appreciate it if you try to narrow the source down to a small stand-alone example.) +```python +# Ideally, a small sample program that demonstrates the problem. +# Or even better, a reproducible playground link https://mypy-play.net/ (use the "Gist" button) +``` + **Your Environment** diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 8055cfd241800..dae4937d5081d 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -6,7 +6,7 @@ on: tags: ['*'] permissions: - contents: write + contents: read jobs: build-wheels: @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: '3.11' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c6ed3cf1a08de..66e7c997f4fad 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,6 +12,9 @@ on: # so it's important to do the docs build on all PRs touching mypy/errorcodes.py # in case somebody's adding a new error code without any docs - 'mypy/errorcodes.py' + # Part of the documentation is automatically generated from the options + # definitions in mypy/main.py + - 'mypy/main.py' - 'mypyc/doc/**' - '**/*.rst' - '**/*.md' @@ -28,17 +31,20 @@ concurrency: jobs: docs: runs-on: ubuntu-latest + timeout-minutes: 10 env: TOXENV: docs TOX_SKIP_MISSING_INTERPRETERS: False VERIFY_MYPY_ERROR_CODES: 1 steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install tox - run: pip install tox==4.11.0 + run: pip install tox==4.26.0 - 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 0c77d3a255d83..8a04c75b0e2e5 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -26,20 +26,20 @@ jobs: mypy_primer: name: Run mypy_primer runs-on: ubuntu-latest - permissions: - contents: read strategy: matrix: - shard-index: [0, 1, 2, 3, 4] + shard-index: [0, 1, 2, 3, 4, 5] fail-fast: false + timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: path: mypy_to_test fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install dependencies run: | python -m pip install -U pip @@ -63,24 +63,40 @@ jobs: mypy_primer \ --repo mypy_to_test \ --new $GITHUB_SHA --old base_commit \ - --num-shards 5 --shard-index ${{ matrix.shard-index }} \ + --num-shards 6 --shard-index ${{ matrix.shard-index }} \ --debug \ --additional-flags="--debug-serialize" \ --output concise \ + --mypy-install-librt \ | 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 + - name: Upload mypy_primer diff + PR number + uses: actions/upload-artifact@v4 + if: ${{ matrix.shard-index == 0 }} + with: + name: mypy_primer_diffs-${{ matrix.shard-index }} + path: | + diff_${{ matrix.shard-index }}.txt + pr_number.txt + - name: Upload mypy_primer diff + uses: actions/upload-artifact@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] + 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..21f1222a5b893 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -1,24 +1,25 @@ name: Comment with mypy_primer diff -on: +on: # zizmor: ignore[dangerous-triggers] workflow_run: workflows: - Run mypy_primer types: - completed -permissions: - contents: read - pull-requests: write +permissions: {} jobs: comment: name: Comment PR from mypy_primer runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write 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'); @@ -48,7 +49,7 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const MAX_CHARACTERS = 30000 + const MAX_CHARACTERS = 50000 const MAX_CHARACTERS_PER_PROJECT = MAX_CHARACTERS / 3 const fs = require('fs') diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index b545e7b0662b2..2d5361a5919ce 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -5,19 +5,22 @@ on: schedule: - cron: "0 0 1,15 * *" -permissions: - contents: write - pull-requests: write +permissions: {} jobs: sync_typeshed: name: Sync typeshed if: github.repository == 'python/mypy' runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + timeout-minutes: 10 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: true # needed to `git push` the PR branch # TODO: use whatever solution ends up working for # https://github.com/python/typeshed/issues/8434 - uses: actions/setup-python@v5 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39fbb14bd3b7f..6fe8257480733 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,85 +31,82 @@ jobs: include: # Make sure to run mypyc compiled unit tests for both # the oldest and newest supported Python versions - - name: Test suite with py38-ubuntu, mypyc-compiled - python: '3.8' - arch: x64 - os: ubuntu-latest - toxenv: py - tox_extra_args: "-n 4" - test_mypyc: true - - name: Test suite with py38-windows-64 - python: '3.8' - arch: x64 - os: windows-latest - toxenv: py38 - tox_extra_args: "-n 4" - - name: Test suite with py39-ubuntu + - name: Test suite with py39-ubuntu, mypyc-compiled python: '3.9' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" + test_mypyc: true - name: Test suite with py310-ubuntu python: '3.10' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" - - name: Test suite with py311-ubuntu, mypyc-compiled + - name: Test suite with py311-ubuntu python: '3.11' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" - test_mypyc: true - name: Test suite with py312-ubuntu, mypyc-compiled python: '3.12' - arch: x64 - os: ubuntu-latest + os: ubuntu-24.04-arm toxenv: py tox_extra_args: "-n 4" test_mypyc: true - - - name: Test suite with py313-dev-ubuntu, mypyc-compiled - python: '3.13-dev' - arch: x64 - os: ubuntu-latest + - name: Test suite with py313-ubuntu, mypyc-compiled + python: '3.13' + os: ubuntu-24.04-arm 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 + - name: Test suite with py314-ubuntu, mypyc-compiled + python: '3.14' + os: ubuntu-24.04-arm + toxenv: py + tox_extra_args: "-n 4" + test_mypyc: true + - name: Test suite with py314-windows-64 + python: '3.14' + os: windows-latest + toxenv: py + tox_extra_args: "-n 4" + + # - name: Test suite with py315-dev-ubuntu + # python: '3.15-dev' + # os: ubuntu-24.04-arm # toxenv: py # tox_extra_args: "-n 4" - # allow_failure: true + # # allow_failure: true # test_mypyc: true - - name: mypyc runtime tests with py39-macos - python: '3.9.18' - arch: x64 - # TODO: macos-13 is the last one to support Python 3.9, change it to macos-latest when updating the Python version - os: macos-13 + - name: mypyc runtime tests with py313-macos + python: '3.13' + os: macos-latest 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 + + - name: mypyc runtime tests with py310-ubuntu + python: '3.10' os: ubuntu-latest toxenv: py - tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" - debug_build: true + tox_extra_args: "-n 3 mypyc/test/test_run.py mypyc/test/test_external.py" - - name: Type check our own code (py38-ubuntu) - python: '3.8' - arch: x64 + # 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.9.21' + # 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 (py39-ubuntu) + python: '3.9' os: ubuntu-latest toxenv: type - - name: Type check our own code (py38-windows-64) - python: '3.8' - arch: x64 + - name: Type check our own code (py39-windows-64) + python: '3.9' os: windows-latest toxenv: type @@ -118,17 +115,19 @@ jobs: # to ensure the tox env works as expected - name: Formatting and code style with Black + ruff python: '3.10' - arch: x64 os: ubuntu-latest 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 + # Python -- Disable argparse help colors (3.14+) + PYTHON_COLORS: 0 # Mypy (see https://github.com/python/mypy/issues/7771) TERM: xterm-color MYPY_FORCE_COLOR: 1 @@ -138,6 +137,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Debug build if: ${{ matrix.debug_build }} @@ -148,17 +149,17 @@ jobs: ./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 + - name: Latest dev build if: ${{ endsWith(matrix.python, '-dev') }} run: | - 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 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 @@ -169,7 +170,6 @@ jobs: if: ${{ !(matrix.debug_build || endsWith(matrix.python, '-dev')) }} with: python-version: ${{ matrix.python }} - architecture: ${{ matrix.arch }} - name: Install tox run: | @@ -180,7 +180,7 @@ jobs: 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==68.2.2 tox==4.11.0 + pip install setuptools==75.1.0 tox==4.26.0 - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} @@ -190,7 +190,7 @@ jobs: - name: Setup tox environment run: | - tox run -e ${{ matrix.toxenv }} --notes + tox run -e ${{ matrix.toxenv }} --notest - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} continue-on-error: ${{ matrix.allow_failure == 'true' }} @@ -202,6 +202,7 @@ jobs: 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) @@ -218,6 +219,8 @@ jobs: CC: i686-linux-gnu-gcc steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install 32-bit build dependencies run: | sudo dpkg --add-architecture i386 && \ @@ -240,7 +243,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.26.0 - 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..6cf3cb71c3fff 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -11,7 +11,7 @@ on: - 'mypy/stubgenc.py' - 'mypy/stubdoc.py' - 'mypy/stubutil.py' - - 'test-data/stubgen/**' + - 'test-data/pybind11_fixtures/**' permissions: contents: read @@ -25,14 +25,17 @@ 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 + with: + persist-credentials: false - - name: Setup 🐍 3.8 + - name: Setup 🐍 3.9 uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: 3.9 - name: Test stubgenc run: misc/test-stubgenc.sh diff --git a/.gitignore b/.gitignore index 6c35e3d89342d..9c325f3e29f8a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ venv/ test-data/packages/.pip_lock dmypy.json .dmypy.json +/.mypyc_test_output # Packages *.egg diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7ff48051aad7..1466c8e0fda47 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,64 @@ 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: v5.0.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: 25.9.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.14.3 hooks: - - id: ruff + - id: ruff-check args: [--exit-non-zero-on-fix] + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.32.1 + hooks: + - id: check-github-workflows + - id: check-github-actions + - id: check-readthedocs + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + args: + - --ignore-words-list=HAX,ccompiler,ot,statics,whet,zar + exclude: ^(mypy/test/|mypy/typeshed/|mypyc/test-data/|test-data/).+$ + - repo: https://github.com/rhysd/actionlint + rev: v1.7.7 + hooks: + - id: actionlint + args: [ + -ignore=property "debug_build" is not defined, + -ignore=property "allow_failure" is not defined, + -ignore=SC2(046|086), + ] + additional_dependencies: + # actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions + # and checks these with shellcheck. This is arguably its most useful feature, + # but the integration only works if shellcheck is installed + - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.11.1" + - repo: https://github.com/woodruffw/zizmor-pre-commit + rev: v1.5.2 + hooks: + - id: zizmor + - repo: local + hooks: + - id: bad-pr-link + name: Bad PR link + description: Detect PR links text that don't match their URL + language: pygrep + entry: '\[(\d+)\]\(https://github.com/python/mypy/pull/(?!\1/?\))\d+/?\)' + files: CHANGELOG.md + # Should be the last one: + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes + ci: autoupdate_schedule: quarterly diff --git a/CHANGELOG.md b/CHANGELOG.md index 9632cb39a8b1b..ed5d947cb8292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,2022 @@ # Mypy Release Notes -## Next release +## Next Release + +## Mypy 1.19 + +We’ve just uploaded mypy 1.19.0 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). + +### Performance Improvements +- Switch to a more dynamic SCC processing logic (Ivan Levkivskyi, PR [20053](https://github.com/python/mypy/pull/20053)) +- Speed up type aliases (Ivan Levkivskyi, PR [19810](https://github.com/python/mypy/pull/19810)) + +### Fixed‑Format Cache Improvements + +Mypy uses a cache by default to speed up incremental runs by reusing partial results +from earlier runs. Mypy 1.18 added a new binary fixed-format cache representation as +an experimental feature. The feature is no longer experimental, and we are planning +to enable it by default in a future mypy release (possibly 1.20), since it's faster +and uses less space than the original, JSON-based cache format. Use +`--fixed-format-cache` to enable the fixed-format cache. + +Mypy now has an extra dependency on the `librt` PyPI package, as it's needed for +cache serialization and deserialization. + +Mypy ships with a tool to convert fixed-format cache files to the old JSON format. +Example of how to use this: +``` +$ python -m mypy.exportjson .mypy_cache/.../my_module.data.ff +``` + +This way existing use cases that parse JSON cache files can be supported when using +the new format, though an extra conversion step is needed. + +This release includes these improvements: + +- Force-discard cache if cache format changed (Ivan Levkivskyi, PR [20152](https://github.com/python/mypy/pull/20152)) +- Add tool to convert binary cache files to JSON (Jukka Lehtosalo, PR [20071](https://github.com/python/mypy/pull/20071)) +- Use more efficient serialization format for long integers in cache files (Jukka Lehtosalo, PR [20151](https://github.com/python/mypy/pull/20151)) +- More robust packing of floats in fixed-format cache (Ivan Levkivskyi, PR [20150](https://github.com/python/mypy/pull/20150)) +- Use self-descriptive cache with type tags (Ivan Levkivskyi, PR [20137](https://github.com/python/mypy/pull/20137)) +- Use fixed format for cache metas (Ivan Levkivskyi, PR [20088](https://github.com/python/mypy/pull/20088)) +- Make metas more compact; fix indirect suppression (Ivan Levkivskyi, PR [20075](https://github.com/python/mypy/pull/20075)) +- Use dedicated tags for most common cached instances (Ivan Levkivskyi, PR [19762](https://github.com/python/mypy/pull/19762)) + +### PEP 747: Annotating Type Forms + +Mypy now recognizes `TypeForm[T]` as a type and implements +[PEP 747](https://peps.python.org/pep-0747/). The feature is still experimental, +and it's disabled by default. Use `--enable-incomplete-feature=TypeForm` to +enable type forms. A type form object captures the type information provided by a +runtime type expression. Example: + +```python +from typing_extensions import TypeForm + +def trycast[T](typx: TypeForm[T], value: object) -> T | None: ... + +def example(o: object) -> None: + # 'int | str' below is an expression that represents a type. + # Unlike type[T], TypeForm[T] can be used with all kinds of types, + # including union types. + x = trycast(int | str, o) + if x is not None: + # Type of 'x' is 'int | str' here + ... +``` + +This feature was contributed by David Foster (PR [19596](https://github.com/python/mypy/pull/19596)). + +### Fixes to Crashes +- Do not push partial types to the binder (Stanislav Terliakov, PR [20202](https://github.com/python/mypy/pull/20202)) +- Fix crash on recursive tuple with Hashable (Ivan Levkivskyi, PR [20232](https://github.com/python/mypy/pull/20232)) +- Fix crash related to decorated functions (Stanislav Terliakov, PR [20203](https://github.com/python/mypy/pull/20203)) +- Do not abort constructing TypeAlias if only type parameters hold us back (Stanislav Terliakov, PR [20162](https://github.com/python/mypy/pull/20162)) +- Use the fallback for `ModuleSpec` early if it can never be resolved (Stanislav Terliakov, PR [20167](https://github.com/python/mypy/pull/20167)) +- Do not store deferred NamedTuple fields as redefinitions (Stanislav Terliakov, PR [20147](https://github.com/python/mypy/pull/20147)) +- Discard partial types remaining after inference failure (Stanislav Terliakov, PR [20126](https://github.com/python/mypy/pull/20126)) +- Fix an infinite recursion bug (Stanislav Terliakov, PR [20127](https://github.com/python/mypy/pull/20127)) +- Fix IsADirectoryError for namespace packages when using --linecoverage-report (wyattscarpenter, PR [20109](https://github.com/python/mypy/pull/20109)) +- Fix an internal error when creating cobertura output for namespace package (wyattscarpenter, PR [20112](https://github.com/python/mypy/pull/20112)) +- Allow type parameters reusing the name missing from current module (Stanislav Terliakov, PR [20081](https://github.com/python/mypy/pull/20081)) +- Prevent TypeGuardedType leak from narrowing declared type as part of type variable bound (Stanislav Terliakov, PR [20046](https://github.com/python/mypy/pull/20046)) +- Fix crash on invalid unpack in base class (Ivan Levkivskyi, PR [19962](https://github.com/python/mypy/pull/19962)) +- Traverse ParamSpec prefix where we should (Ivan Levkivskyi, PR [19800](https://github.com/python/mypy/pull/19800)) +- Fix daemon crash related to imports (Ivan Levkivskyi, PR [20271](https://github.com/python/mypy/pull/20271)) + +### Mypyc: Support for `__getattr__`, `__setattr__`, and `__delattr__` + +Mypyc now has partial support for `__getattr__`, `__setattr__` and +`__delattr__` methods in native classes. + +Note that native attributes are not stored using `__dict__`. Setting attributes +directly while bypassing `__setattr__` is possible by using +`super().__setattr__(...)` or `object.__setattr__(...)`, but not via `__dict__`. + +Example: +```python +class Demo: + _data: dict[str, str] + + def __init__(self) -> None: + # Initialize data dict without calling our __setattr__ + super().__setattr__("_data", {}) + + def __setattr__(self, name: str, value: str) -> None: + print(f"Setting {name} = {value!r}") + + if name == "_data": + raise AttributeError("'_data' cannot be set") + + self._data[name] = value + + def __getattr__(self, name: str) -> str: + print(f"Getting {name}") + + try: + return self._data[name] + except KeyError: + raise AttributeError(name) + +d = Demo() +d.x = "hello" +d.y = "world" + +print(d.x) +print(d.y) +``` + +Related PRs: +- Generate `__setattr__` wrapper (Piotr Sawicki, PR [19937](https://github.com/python/mypy/pull/19937)) +- Generate `__getattr__` wrapper (Piotr Sawicki, PR [19909](https://github.com/python/mypy/pull/19909)) +- Support deleting attributes in `__setattr__` wrapper (Piotr Sawicki, PR [19997](https://github.com/python/mypy/pull/19997)) + +### Miscellaneous Mypyc Improvements +- Fix `__new__` in native classes with inheritance (Piotr Sawicki, PR [20302](https://github.com/python/mypy/pull/20302)) +- Fix crash on `super` in generator (Ivan Levkivskyi, PR [20291](https://github.com/python/mypy/pull/20291)) +- Fix calling base class async method using `super()` (Jukka Lehtosalo, PR [20254](https://github.com/python/mypy/pull/20254)) +- Fix async or generator methods in traits (Jukka Lehtosalo, PR [20246](https://github.com/python/mypy/pull/20246)) +- Optimize equality check with string literals (BobTheBuidler, PR [19883](https://github.com/python/mypy/pull/19883)) +- Fix inheritance of async defs (Jukka Lehtosalo, PR [20044](https://github.com/python/mypy/pull/20044)) +- Reject invalid `mypyc_attr` args (BobTheBuidler, PR [19963](https://github.com/python/mypy/pull/19963)) +- Optimize `isinstance` with tuple of primitive types (BobTheBuidler, PR [19949](https://github.com/python/mypy/pull/19949)) +- Optimize away first index check in for loops if length > 1 (BobTheBuidler, PR [19933](https://github.com/python/mypy/pull/19933)) +- Fix broken exception/cancellation handling in async def (Jukka Lehtosalo, PR [19951](https://github.com/python/mypy/pull/19951)) +- Transform `object.__new__` inside `__new__` (Piotr Sawicki, PR [19866](https://github.com/python/mypy/pull/19866)) +- Fix crash with NewType and other non-class types in incremental builds (Jukka Lehtosalo, PR [19837](https://github.com/python/mypy/pull/19837)) +- Optimize container creation from expressions with length known at compile time (BobTheBuidler, PR [19503](https://github.com/python/mypy/pull/19503)) +- Allow per-class free list to be used with inheritance (Jukka Lehtosalo, PR [19790](https://github.com/python/mypy/pull/19790)) +- Fix object finalization (Marc Mueller, PR [19749](https://github.com/python/mypy/pull/19749)) +- Allow defining a single-item free "list" for a native class (Jukka Lehtosalo, PR [19785](https://github.com/python/mypy/pull/19785)) +- Speed up unary "not" (Jukka Lehtosalo, PR [19774](https://github.com/python/mypy/pull/19774)) + +### Stubtest Improvements +- Check `_value_` for ellipsis-valued stub enum members (Stanislav Terliakov, PR [19760](https://github.com/python/mypy/pull/19760)) +- Include function name in overload assertion messages (Joren Hammudoglu, PR [20063](https://github.com/python/mypy/pull/20063)) +- Fix special case in analyzing function signature (iap, PR [19822](https://github.com/python/mypy/pull/19822)) +- Improve `allowlist` docs with better example (sobolevn, PR [20007](https://github.com/python/mypy/pull/20007)) + +### Documentation Updates +- Update duck type compatibility: mention strict-bytes and mypy 2.0 (wyattscarpenter, PR [20121](https://github.com/python/mypy/pull/20121)) +- Document `--enable-incomplete-feature TypeForm` (wyattscarpenter, PR [20173](https://github.com/python/mypy/pull/20173)) +- Change the inline TypedDict example (wyattscarpenter, PR [20172](https://github.com/python/mypy/pull/20172)) +- Replace `List` with built‑in `list` (PEP 585) (Thiago J. Barbalho, PR [20000](https://github.com/python/mypy/pull/20000)) +- Improve junit documentation (wyattscarpenter, PR [19867](https://github.com/python/mypy/pull/19867)) + +### Other Notable Fixes and Improvements +- Fix annotated with function as type keyword list parameter (KarelKenens, PR [20094](https://github.com/python/mypy/pull/20094)) +- Fix errors for raise NotImplemented (Shantanu, PR [20168](https://github.com/python/mypy/pull/20168)) +- Don't let help formatter line-wrap URLs (Frank Dana, PR [19825](https://github.com/python/mypy/pull/19825)) +- Do not cache fast container types inside lambdas (Stanislav Terliakov, PR [20166](https://github.com/python/mypy/pull/20166)) +- Respect force-union-syntax flag in error hint (Marc Mueller, PR [20165](https://github.com/python/mypy/pull/20165)) +- Fix type checking of dict type aliases (Shantanu, PR [20170](https://github.com/python/mypy/pull/20170)) +- Use pretty callable formatting more often for callable expressions (Theodore Ando, PR [20128](https://github.com/python/mypy/pull/20128)) +- Use dummy concrete type instead of `Any` when checking protocol variance (bzoracler, PR [20110](https://github.com/python/mypy/pull/20110)) +- PEP 696: Fix swapping TypeVars with defaults (Randolf Scholz, PR [19449](https://github.com/python/mypy/pull/19449)) +- Fix narrowing of class pattern with union type (Randolf Scholz, PR [19517](https://github.com/python/mypy/pull/19517)) +- Do not emit unreachable warnings for lines that return `NotImplemented` (Christoph Tyralla, PR [20083](https://github.com/python/mypy/pull/20083)) +- Fix matching against `typing.Callable` and `Protocol` types (Randolf Scholz, PR [19471](https://github.com/python/mypy/pull/19471)) +- Make `--pretty` work better on multi-line issues (A5rocks, PR [20056](https://github.com/python/mypy/pull/20056)) +- More precise return types for `TypedDict.get` (Randolf Scholz, PR [19897](https://github.com/python/mypy/pull/19897)) +- Prevent false unreachable warnings for `@final` instances that occur when strict optional checking is disabled (Christoph Tyralla, PR [20045](https://github.com/python/mypy/pull/20045)) +- Check class references to catch non-existent classes in match cases (A5rocks, PR [20042](https://github.com/python/mypy/pull/20042)) +- Do not sort unused error codes in unused error codes warning (wyattscarpenter, PR [20036](https://github.com/python/mypy/pull/20036)) +- Fix `[name-defined]` false positive in `class A[X, Y=X]:` case (sobolevn, PR [20021](https://github.com/python/mypy/pull/20021)) +- Filter SyntaxWarnings during AST parsing (Marc Mueller, PR [20023](https://github.com/python/mypy/pull/20023)) +- Make untyped decorator its own error code (wyattscarpenter, PR [19911](https://github.com/python/mypy/pull/19911)) +- Support error codes from plugins in options (Sigve Sebastian Farstad, PR [19719](https://github.com/python/mypy/pull/19719)) +- Allow returning Literals in `__new__` (James Hilton-Balfe, PR [15687](https://github.com/python/mypy/pull/15687)) +- Inverse interface freshness logic (Ivan Levkivskyi, PR [19809](https://github.com/python/mypy/pull/19809)) +- Do not report exhaustive-match after deferral (Stanislav Terliakov, PR [19804](https://github.com/python/mypy/pull/19804)) +- Make `untyped_calls_exclude` invalidate cache (Ivan Levkivskyi, PR [19801](https://github.com/python/mypy/pull/19801)) +- Add await to empty context hack (Stanislav Terliakov, PR [19777](https://github.com/python/mypy/pull/19777)) +- Consider non-empty enums assignable to Self (Stanislav Terliakov, PR [19779](https://github.com/python/mypy/pull/19779)) + +### Typeshed updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=ebce8d766b41fbf4d83cf47c1297563a9508ff60+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + +### Mypy 1.19.1 + +- Fix noncommutative joins with bounded TypeVars (Shantanu, PR [20345](https://github.com/python/mypy/pull/20345)) +- Respect output format for cached runs by serializing raw errors in cache metas (Ivan Levkivskyi, PR [20372](https://github.com/python/mypy/pull/20372)) +- Allow `types.NoneType` in match cases (A5rocks, PR [20383](https://github.com/python/mypy/pull/20383)) +- Fix mypyc generator regression with empty tuple (BobTheBuidler, PR [20371](https://github.com/python/mypy/pull/20371)) +- Fix crash involving Unpack-ed TypeVarTuple (Shantanu, PR [20323](https://github.com/python/mypy/pull/20323)) +- Fix crash on star import of redefinition (Ivan Levkivskyi, PR [20333](https://github.com/python/mypy/pull/20333)) +- Fix crash on typevar with forward ref used in other module (Ivan Levkivskyi, PR [20334](https://github.com/python/mypy/pull/20334)) +- Fail with an explicit error on PyPy (Ivan Levkivskyi, PR [20389](https://github.com/python/mypy/pull/20389)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: +- A5rocks +- BobTheBuidler +- bzoracler +- Chainfire +- Christoph Tyralla +- David Foster +- Frank Dana +- Guo Ci +- iap +- Ivan Levkivskyi +- James Hilton-Balfe +- jhance +- Joren Hammudoglu +- Jukka Lehtosalo +- KarelKenens +- Kevin Kannammalil +- Marc Mueller +- Michael Carlstrom +- Michael J. Sullivan +- Piotr Sawicki +- Randolf Scholz +- Shantanu +- Sigve Sebastian Farstad +- sobolevn +- Stanislav Terliakov +- Stephen Morton +- Theodore Ando +- Thiago J. Barbalho +- wyattscarpenter + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +## Mypy 1.18 + +We’ve just uploaded mypy 1.18.1 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). + +### Mypy Performance Improvements + +Mypy 1.18.1 includes numerous performance improvements, resulting in about 40% speedup +compared to 1.17 when type checking mypy itself. In extreme cases, the improvement +can be 10x or higher. The list below is an overview of the various mypy optimizations. +Many mypyc improvements (discussed in a separate section below) also improve performance. + +Type caching optimizations have a small risk of causing regressions. When +reporting issues with unexpected inferred types, please also check if +`--disable-expression-cache` will work around the issue, as it turns off some of +these optimizations. + +- Improve self check performance by 1.8% (Jukka Lehtosalo, PR [19768](https://github.com/python/mypy/pull/19768), [19769](https://github.com/python/mypy/pull/19769), [19770](https://github.com/python/mypy/pull/19770)) +- Optimize fixed-format deserialization (Ivan Levkivskyi, PR [19765](https://github.com/python/mypy/pull/19765)) +- Use macros to optimize fixed-format deserialization (Ivan Levkivskyi, PR [19757](https://github.com/python/mypy/pull/19757)) +- Two additional micro‑optimizations (Ivan Levkivskyi, PR [19627](https://github.com/python/mypy/pull/19627)) +- Another set of micro‑optimizations (Ivan Levkivskyi, PR [19633](https://github.com/python/mypy/pull/19633)) +- Cache common types (Ivan Levkivskyi, PR [19621](https://github.com/python/mypy/pull/19621)) +- Skip more method bodies in third‑party libraries for speed (Ivan Levkivskyi, PR [19586](https://github.com/python/mypy/pull/19586)) +- Simplify the representation of callable types (Ivan Levkivskyi, PR [19580](https://github.com/python/mypy/pull/19580)) +- Add cache for types of some expressions (Ivan Levkivskyi, PR [19505](https://github.com/python/mypy/pull/19505)) +- Use cache for dictionary expressions (Ivan Levkivskyi, PR [19536](https://github.com/python/mypy/pull/19536)) +- Use cache for binary operations (Ivan Levkivskyi, PR [19523](https://github.com/python/mypy/pull/19523)) +- Cache types of type objects (Ivan Levkivskyi, PR [19514](https://github.com/python/mypy/pull/19514)) +- Avoid duplicate work when checking boolean operations (Ivan Levkivskyi, PR [19515](https://github.com/python/mypy/pull/19515)) +- Optimize generic inference passes (Ivan Levkivskyi, PR [19501](https://github.com/python/mypy/pull/19501)) +- Speed up the default plugin (Jukka Lehtosalo, PRs [19385](https://github.com/python/mypy/pull/19385) and [19462](https://github.com/python/mypy/pull/19462)) +- Remove nested imports from the default plugin (Ivan Levkivskyi, PR [19388](https://github.com/python/mypy/pull/19388)) +- Micro‑optimize type expansion (Jukka Lehtosalo, PR [19461](https://github.com/python/mypy/pull/19461)) +- Micro‑optimize type indirection (Jukka Lehtosalo, PR [19460](https://github.com/python/mypy/pull/19460)) +- Micro‑optimize the plugin framework (Jukka Lehtosalo, PR [19464](https://github.com/python/mypy/pull/19464)) +- Avoid temporary set creation in subtype checking (Jukka Lehtosalo, PR [19463](https://github.com/python/mypy/pull/19463)) +- Subtype checking micro‑optimization (Jukka Lehtosalo, PR [19384](https://github.com/python/mypy/pull/19384)) +- Return early where possible in subtype check (Stanislav Terliakov, PR [19400](https://github.com/python/mypy/pull/19400)) +- Deduplicate some types before joining (Stanislav Terliakov, PR [19409](https://github.com/python/mypy/pull/19409)) +- Speed up type checking by caching argument inference context (Jukka Lehtosalo, PR [19323](https://github.com/python/mypy/pull/19323)) +- Optimize binding method self argument type and deprecation checks (Ivan Levkivskyi, PR [19556](https://github.com/python/mypy/pull/19556)) +- Keep trivial instance types/aliases during expansion (Ivan Levkivskyi, PR [19543](https://github.com/python/mypy/pull/19543)) + +### Fixed‑Format Cache (Experimental) + +Mypy now supports a new cache format used for faster incremental builds. It makes +incremental builds up to twice as fast. The feature is experimental and +currently only supported when using a compiled version of mypy. Use `--fixed-format-cache` +to enable the new format, or `fixed_format_cache = True` in a configuration file. + +We plan to enable this by default in a future mypy release, and we'll eventually +deprecate and remove support for the original JSON-based format. + +Unlike the JSON-based cache format, the new binary format is currently +not easy to parse and inspect by mypy users. We are planning to provide a tool to +convert fixed-format cache files to JSON, but details of the output JSON may be +different from the current JSON format. If you rely on being able to inspect +mypy cache files, we recommend creating a GitHub issue and explaining your use +case, so that we can more likely provide support for it. (Using +`MypyFile.read(binary_data)` to inspect cache data may be sufficient to support +some use cases.) + +This feature was contributed by Ivan Levkivskyi (PR [19668](https://github.com/python/mypy/pull/19668), [19735](https://github.com/python/mypy/pull/19735), [19750](https://github.com/python/mypy/pull/19750), [19681](https://github.com/python/mypy/pull/19681), [19752](https://github.com/python/mypy/pull/19752), [19815](https://github.com/python/mypy/pull/19815)). + +### Flexible Variable Definitions: Update + +Mypy 1.16.0 introduced `--allow-redefinition-new`, which allows redefining variables +with different types, and inferring union types for variables from multiple assignments. +The feature is now documented in the `--help` output, but the feature is still experimental. + +We are planning to enable this by default in mypy 2.0, and we will also deprecate the +older `--allow-redefinition` flag. Since the new behavior differs significantly from +the older flag, we encourage users of `--allow-redefinition` to experiment with +`--allow-redefinition-new` and create a GitHub issue if the new functionality doesn't +support some important use cases. + +This feature was contributed by Jukka Lehtosalo. + +### Inferred Type for Bare ClassVar + +A ClassVar without an explicit type annotation now causes the type of the variable +to be inferred from the initializer: + + +```python +from typing import ClassVar + +class Item: + # Type of 'next_id' is now 'int' (it was 'Any') + next_id: ClassVar = 1 + + ... +``` + +This feature was contributed by Ivan Levkivskyi (PR [19573](https://github.com/python/mypy/pull/19573)). + +### Disjoint Base Classes (@disjoint_base, PEP 800) + +Mypy now understands disjoint bases (PEP 800): it recognizes the `@disjoint_base` +decorator, and rejects class definitions that combine mutually incompatible base classes, +and takes advantage of the fact that such classes cannot exist in reachability and +narrowing logic. + +This class definition will now generate an error: + +```python +# Error: Class "Bad" has incompatible disjoint bases +class Bad(str, Exception): + ... +``` + +This feature was contributed by Jelle Zijlstra (PR [19678](https://github.com/python/mypy/pull/19678)). + +### Miscellaneous New Mypy Features + +- Add `--strict-equality-for-none` to flag non-overlapping comparisons involving None (Christoph Tyralla, PR [19718](https://github.com/python/mypy/pull/19718)) +- Don’t show import‑related errors after a module‑level assert such as `assert sys.platform == "linux"` that is always false (Stanislav Terliakov, PR [19347](https://github.com/python/mypy/pull/19347)) + +### Improvements to Match Statements + +- Add temporary named expressions for match subjects (Stanislav Terliakov, PR [18446](https://github.com/python/mypy/pull/18446)) +- Fix unwrapping of assignment expressions in match subject (Marc Mueller, PR [19742](https://github.com/python/mypy/pull/19742)) +- Omit errors for class patterns against object (Marc Mueller, PR [19709](https://github.com/python/mypy/pull/19709)) +- Remove unnecessary error for certain match class patterns (Marc Mueller, PR [19708](https://github.com/python/mypy/pull/19708)) +- Use union type for captured vars in or pattern (Marc Mueller, PR [19710](https://github.com/python/mypy/pull/19710)) +- Prevent final reassignment inside match case (Omer Hadari, PR [19496](https://github.com/python/mypy/pull/19496)) + +### Fixes to Crashes + +- Fix crash with variadic tuple arguments to a generic type (Randolf Scholz, PR [19705](https://github.com/python/mypy/pull/19705)) +- Fix crash when enable_error_code in pyproject.toml has wrong type (wyattscarpenter, PR [19494](https://github.com/python/mypy/pull/19494)) +- Prevent crash for dataclass with PEP 695 TypeVarTuple on Python 3.13+ (Stanislav Terliakov, PR [19565](https://github.com/python/mypy/pull/19565)) +- Fix crash on settable property alias (Ivan Levkivskyi, PR [19615](https://github.com/python/mypy/pull/19615)) + +### Experimental Free-threading Support for Mypyc + +All mypyc tests now pass on free-threading Python 3.14 release candidate builds. The performance +of various micro-benchmarks scale well across multiple threads. + +Free-threading support is still experimental. Note that native attribute access +(get and set), list item access and certain other operations are still +unsafe when there are race conditions. This will likely change in the future. +You can follow the +[area-free-threading label](https://github.com/mypyc/mypyc/issues?q=is%3Aissue%20state%3Aopen%20label%3Aarea-free-threading) +in the mypyc issues tracker to follow progress. + +Related PRs: +- Enable free‑threading when compiling multiple modules (Jukka Lehtosalo, PR [19541](https://github.com/python/mypy/pull/19541)) +- Fix `list.pop` on free‑threaded builds (Jukka Lehtosalo, PR [19522](https://github.com/python/mypy/pull/19522)) +- Make type objects immortal under free‑threading (Jukka Lehtosalo, PR [19538](https://github.com/python/mypy/pull/19538)) + +### Mypyc: Support `__new__` + +Mypyc now has rudimentary support for user-defined `__new__` methods. + +This feature was contributed by Piotr Sawicki (PR [19739](https://github.com/python/mypy/pull/19739)). + +### Mypyc: Faster Generators and Async Functions + +Generators and calls of async functions are now faster, sometimes by 2x or more. + +Related PRs: +- Speed up for loops over native generators (Jukka Lehtosalo, PR [19415](https://github.com/python/mypy/pull/19415)) +- Speed up native‑to‑native calls using await (Jukka Lehtosalo, PR [19398](https://github.com/python/mypy/pull/19398)) +- Call generator helper directly in await expressions (Jukka Lehtosalo, PR [19376](https://github.com/python/mypy/pull/19376)) +- Speed up generator allocation with per‑type freelists (Jukka Lehtosalo, PR [19316](https://github.com/python/mypy/pull/19316)) + +### Miscellaneous Mypyc Improvements + +- Special‑case certain Enum method calls for speed (Ivan Levkivskyi, PR [19634](https://github.com/python/mypy/pull/19634)) +- Fix issues related to subclassing and undefined attribute tracking (Chainfire, PR [19787](https://github.com/python/mypy/pull/19787)) +- Fix invalid C function signature (Jukka Lehtosalo, PR [19773](https://github.com/python/mypy/pull/19773)) +- Speed up implicit `__ne__` (Jukka Lehtosalo, PR [19759](https://github.com/python/mypy/pull/19759)) +- Speed up equality with optional str/bytes types (Jukka Lehtosalo, PR [19758](https://github.com/python/mypy/pull/19758)) +- Speed up access to empty tuples (BobTheBuidler, PR [19654](https://github.com/python/mypy/pull/19654)) +- Speed up calls with `*args` (BobTheBuidler, PRs [19623](https://github.com/python/mypy/pull/19623) and [19631](https://github.com/python/mypy/pull/19631)) +- Speed up calls with `**kwargs` (BobTheBuidler, PR [19630](https://github.com/python/mypy/pull/19630)) +- Optimize `type(x)`, `x.__class__`, and `.__name__` (Jukka Lehtosalo, PR [19691](https://github.com/python/mypy/pull/19691), [19683](https://github.com/python/mypy/pull/19683)) +- Specialize `bytes.decode` for common encodings (Jukka Lehtosalo, PR [19688](https://github.com/python/mypy/pull/19688)) +- Speed up `in` operations using final fixed‑length tuples (Jukka Lehtosalo, PR [19682](https://github.com/python/mypy/pull/19682)) +- Optimize f‑string building from final values (BobTheBuidler, PR [19611](https://github.com/python/mypy/pull/19611)) +- Add dictionary set item for exact dict instances (BobTheBuidler, PR [19657](https://github.com/python/mypy/pull/19657)) +- Cache length when iterating over immutable types (BobTheBuidler, PR [19656](https://github.com/python/mypy/pull/19656)) +- Fix name conflict related to attributes of generator classes (Piotr Sawicki, PR [19535](https://github.com/python/mypy/pull/19535)) +- Fix segfault from heap type objects with a static docstring (Brian Schubert, PR [19636](https://github.com/python/mypy/pull/19636)) +- Unwrap NewType to its base type for additional optimizations (BobTheBuidler, PR [19497](https://github.com/python/mypy/pull/19497)) +- Generate an export table only for separate compilation (Jukka Lehtosalo, PR [19521](https://github.com/python/mypy/pull/19521)) +- Speed up `isinstance` with built‑in types (Piotr Sawicki, PR [19435](https://github.com/python/mypy/pull/19435)) +- Use native integers for some sequence indexing (Jukka Lehtosalo, PR [19426](https://github.com/python/mypy/pull/19426)) +- Speed up `isinstance(obj, list)` (Piotr Sawicki, PR [19416](https://github.com/python/mypy/pull/19416)) +- Report error on reserved method names (Piotr Sawicki, PR [19407](https://github.com/python/mypy/pull/19407)) +- Speed up string equality (Jukka Lehtosalo, PR [19402](https://github.com/python/mypy/pull/19402)) +- Raise `NameError` on undefined names (Piotr Sawicki, PR [19395](https://github.com/python/mypy/pull/19395)) +- Use per‑type freelists for nested functions (Jukka Lehtosalo, PR [19390](https://github.com/python/mypy/pull/19390)) +- Simplify comparison of tuple elements (Piotr Sawicki, PR [19396](https://github.com/python/mypy/pull/19396)) +- Generate introspection signatures for compiled functions (Brian Schubert, PR [19307](https://github.com/python/mypy/pull/19307)) +- Fix undefined attribute checking special case (Jukka Lehtosalo, PR [19378](https://github.com/python/mypy/pull/19378)) +- Fix comparison of tuples with different lengths (Piotr Sawicki, PR [19372](https://github.com/python/mypy/pull/19372)) +- Speed up `list.clear` (Jahongir Qurbonov, PR [19344](https://github.com/python/mypy/pull/19344)) +- Speed up `weakref.proxy` (BobTheBuidler, PR [19217](https://github.com/python/mypy/pull/19217)) +- Speed up `weakref.ref` (BobTheBuidler, PR [19099](https://github.com/python/mypy/pull/19099)) +- Speed up `str.count` (BobTheBuidler, PR [19264](https://github.com/python/mypy/pull/19264)) + +### Stubtest Improvements +- Add temporary `--ignore-disjoint-bases` flag to ease PEP 800 migration (Joren Hammudoglu, PR [19740](https://github.com/python/mypy/pull/19740)) +- Flag redundant uses of `@disjoint_base` (Jelle Zijlstra, PR [19715](https://github.com/python/mypy/pull/19715)) +- Improve signatures for `__init__` of C extension classes (Stephen Morton, PR [18259](https://github.com/python/mypy/pull/18259)) +- Handle overloads with mixed positional‑only parameters (Stephen Morton, PR [18287](https://github.com/python/mypy/pull/18287)) +- Use “parameter” (not “argument”) in error messages (PrinceNaroliya, PR [19707](https://github.com/python/mypy/pull/19707)) +- Don’t require `@disjoint_base` when `__slots__` imply finality (Jelle Zijlstra, PR [19701](https://github.com/python/mypy/pull/19701)) +- Allow runtime‑existing aliases of `@type_check_only` types (Brian Schubert, PR [19568](https://github.com/python/mypy/pull/19568)) +- More detailed checking of type objects in stubtest (Stephen Morton, PR [18251](https://github.com/python/mypy/pull/18251)) +- Support running stubtest in non-UTF8 terminals (Stanislav Terliakov, PR [19085](https://github.com/python/mypy/pull/19085)) + +### Documentation Updates + +- Add idlemypyextension to IDE integrations (CoolCat467, PR [18615](https://github.com/python/mypy/pull/18615)) +- Document that `object` is often preferable to `Any` in APIs (wyattscarpenter, PR [19103](https://github.com/python/mypy/pull/19103)) +- Include a detailed listing of flags enabled by `--strict` (wyattscarpenter, PR [19062](https://github.com/python/mypy/pull/19062)) +- Update “common issues” (reveal_type/reveal_locals; note on orjson) (wyattscarpenter, PR [19059](https://github.com/python/mypy/pull/19059), [19058](https://github.com/python/mypy/pull/19058)) + +### Other Notable Fixes and Improvements + +- Remove deprecated `--new-type-inference` flag (the new algorithm has long been default) (Ivan Levkivskyi, PR [19570](https://github.com/python/mypy/pull/19570)) +- Use empty context as a fallback for return expressions when outer context misleads inference (Ivan Levkivskyi, PR [19767](https://github.com/python/mypy/pull/19767)) +- Fix forward references in type parameters of over‑parameterized PEP 695 aliases (Brian Schubert, PR [19725](https://github.com/python/mypy/pull/19725)) +- Don’t expand PEP 695 aliases when checking node fullnames (Brian Schubert, PR [19699](https://github.com/python/mypy/pull/19699)) +- Don’t use outer context for 'or' expression inference when LHS is Any (Stanislav Terliakov, PR [19748](https://github.com/python/mypy/pull/19748)) +- Recognize buffer protocol special methods (Brian Schubert, PR [19581](https://github.com/python/mypy/pull/19581)) +- Support attribute access on enum members correctly (Stanislav Terliakov, PR [19422](https://github.com/python/mypy/pull/19422)) +- Check `__slots__` assignments on self types (Stanislav Terliakov, PR [19332](https://github.com/python/mypy/pull/19332)) +- Move self‑argument checks after decorator application (Stanislav Terliakov, PR [19490](https://github.com/python/mypy/pull/19490)) +- Infer empty list for `__slots__` and module `__all__` (Stanislav Terliakov, PR [19348](https://github.com/python/mypy/pull/19348)) +- Use normalized tuples for fallback calculation (Stanislav Terliakov, PR [19111](https://github.com/python/mypy/pull/19111)) +- Preserve literals when joining similar types (Stanislav Terliakov, PR [19279](https://github.com/python/mypy/pull/19279)) +- Allow adjacent conditionally‑defined overloads (Stanislav Terliakov, PR [19042](https://github.com/python/mypy/pull/19042)) +- Check property decorators more strictly (Stanislav Terliakov, PR [19313](https://github.com/python/mypy/pull/19313)) +- Support properties with generic setters (Ivan Levkivskyi, PR [19298](https://github.com/python/mypy/pull/19298)) +- Generalize class/static method and property alias support (Ivan Levkivskyi, PR [19297](https://github.com/python/mypy/pull/19297)) +- Re‑widen custom properties after narrowing (Ivan Levkivskyi, PR [19296](https://github.com/python/mypy/pull/19296)) +- Avoid erasing type objects when checking runtime cover (Shantanu, PR [19320](https://github.com/python/mypy/pull/19320)) +- Include tuple fallback in constraints built from tuple types (Stanislav Terliakov, PR [19100](https://github.com/python/mypy/pull/19100)) +- Somewhat better isinstance support on old‑style unions (Shantanu, PR [19714](https://github.com/python/mypy/pull/19714)) +- Improve promotions inside unions (Christoph Tyralla, PR [19245](https://github.com/python/mypy/pull/19245)) +- Treat uninhabited types as having all attributes (Ivan Levkivskyi, PR [19300](https://github.com/python/mypy/pull/19300)) +- Improve metaclass conflict checks (Robsdedude, PR [17682](https://github.com/python/mypy/pull/17682)) +- Fixes to metaclass resolution algorithm (Robsdedude, PR [17713](https://github.com/python/mypy/pull/17713)) +- PEP 702 @deprecated: handle “combined” overloads (Christoph Tyralla, PR [19626](https://github.com/python/mypy/pull/19626)) +- PEP 702 @deprecated: include overloads in snapshot descriptions (Christoph Tyralla, PR [19613](https://github.com/python/mypy/pull/19613)) +- Ignore overload implementation when checking `__OP__` / `__rOP__` compatibility (Stanislav Terliakov, PR [18502](https://github.com/python/mypy/pull/18502)) +- Support `_value_` as a fallback for ellipsis Enum members (Stanislav Terliakov, PR [19352](https://github.com/python/mypy/pull/19352)) +- Sort arguments in TypedDict overlap messages (Marc Mueller, PR [19666](https://github.com/python/mypy/pull/19666)) +- Fix handling of implicit return in lambda (Stanislav Terliakov, PR [19642](https://github.com/python/mypy/pull/19642)) +- Improve behavior of uninhabited types (Stanislav Terliakov, PR [19648](https://github.com/python/mypy/pull/19648)) +- Fix overload diagnostics when `*args` and `**kwargs` both match (Shantanu, PR [19614](https://github.com/python/mypy/pull/19614)) +- Further fix overload diagnostics for `*args`/`**kwargs` (Shantanu, PR [19619](https://github.com/python/mypy/pull/19619)) +- Show type variable name in "Cannot infer type argument" (Brian Schubert, PR [19290](https://github.com/python/mypy/pull/19290)) +- Fail gracefully on unsupported template strings (PEP 750) (Brian Schubert, PR [19700](https://github.com/python/mypy/pull/19700)) +- Revert colored argparse help for Python 3.14 (Marc Mueller, PR [19721](https://github.com/python/mypy/pull/19721)) +- Update stubinfo for latest typeshed (Shantanu, PR [19771](https://github.com/python/mypy/pull/19771)) +- Fix dict assignment when an incompatible same‑shape TypedDict exists (Stanislav Terliakov, PR [19592](https://github.com/python/mypy/pull/19592)) +- Fix constructor type for subclasses of Any (Ivan Levkivskyi, PR [19295](https://github.com/python/mypy/pull/19295)) +- Fix TypeGuard/TypeIs being forgotten in some cases (Brian Schubert, PR [19325](https://github.com/python/mypy/pull/19325)) +- Fix TypeIs negative narrowing for unions of generics (Brian Schubert, PR [18193](https://github.com/python/mypy/pull/18193)) +- dmypy suggest: Fix incorrect signature suggestion when a type matches a module name (Brian Schubert, PR [18937](https://github.com/python/mypy/pull/18937)) +- dmypy suggest: Fix interaction with `__new__` (Stanislav Terliakov, PR [18966](https://github.com/python/mypy/pull/18966)) +- dmypy suggest: Support Callable / callable Protocols in decorator unwrapping (Anthony Sottile, PR [19072](https://github.com/python/mypy/pull/19072)) +- Fix missing error when redeclaring a type variable in a nested generic class (Brian Schubert, PR [18883](https://github.com/python/mypy/pull/18883)) +- Fix for overloaded type object erasure (Shantanu, PR [19338](https://github.com/python/mypy/pull/19338)) +- Fix TypeGuard with call on temporary object (Saul Shanabrook, PR [19577](https://github.com/python/mypy/pull/19577)) + +### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=2480d7e7c74493a024eaf254c5d2c6f452c80ee2+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + +### Mypy 1.18.2 + +- Fix crash on recursive alias (Ivan Levkivskyi, PR [19845](https://github.com/python/mypy/pull/19845)) +- Add additional guidance for stubtest errors when runtime is `object.__init__` (Stephen Morton, PR [19733](https://github.com/python/mypy/pull/19733)) +- Fix handling of None values in f-string expressions in mypyc (BobTheBuidler, PR [19846](https://github.com/python/mypy/pull/19846)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +- Ali Hamdan +- Anthony Sottile +- BobTheBuidler +- Brian Schubert +- Chainfire +- Charlie Denton +- Christoph Tyralla +- CoolCat467 +- Daniel Hnyk +- Emily +- Emma Smith +- Ethan Sarp +- Ivan Levkivskyi +- Jahongir Qurbonov +- Jelle Zijlstra +- Joren Hammudoglu +- Jukka Lehtosalo +- Marc Mueller +- Omer Hadari +- Piotr Sawicki +- PrinceNaroliya +- Randolf Scholz +- Robsdedude +- Saul Shanabrook +- Shantanu +- Stanislav Terliakov +- Stephen Morton +- wyattscarpenter + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +## Mypy 1.17 + +We’ve just uploaded mypy 1.17 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). + +### Optionally Check That Match Is Exhaustive + +Mypy can now optionally generate an error if a match statement does not +match exhaustively, without having to use `assert_never(...)`. Enable +this by using `--enable-error-code exhaustive-match`. + +Example: + +```python +# mypy: enable-error-code=exhaustive-match + +import enum + +class Color(enum.Enum): + RED = 1 + BLUE = 2 + +def show_color(val: Color) -> None: + # error: Unhandled case for values of type "Literal[Color.BLUE]" + match val: + case Color.RED: + print("red") +``` + +This feature was contributed by Donal Burns (PR [19144](https://github.com/python/mypy/pull/19144)). + +### Further Improvements to Attribute Resolution + +This release includes additional improvements to how attribute types +and kinds are resolved. These fix many bugs and overall improve consistency. + +* Handle corner case: protocol/class variable/descriptor (Ivan Levkivskyi, PR [19277](https://github.com/python/mypy/pull/19277)) +* Fix a few inconsistencies in protocol/type object interactions (Ivan Levkivskyi, PR [19267](https://github.com/python/mypy/pull/19267)) +* Refactor/unify access to static attributes (Ivan Levkivskyi, PR [19254](https://github.com/python/mypy/pull/19254)) +* Remove inconsistencies in operator handling (Ivan Levkivskyi, PR [19250](https://github.com/python/mypy/pull/19250)) +* Make protocol subtyping more consistent (Ivan Levkivskyi, PR [18943](https://github.com/python/mypy/pull/18943)) + +### Fixes to Nondeterministic Type Checking + +Previous mypy versions could infer different types for certain expressions +across different runs (typically depending on which order certain types +were processed, and this order was nondeterministic). This release includes +fixes to several such issues. + +* Fix nondeterministic type checking by making join with explicit Protocol and type promotion commute (Shantanu, PR [18402](https://github.com/python/mypy/pull/18402)) +* Fix nondeterministic type checking caused by nonassociative of None joins (Shantanu, PR [19158](https://github.com/python/mypy/pull/19158)) +* Fix nondeterministic type checking caused by nonassociativity of joins (Shantanu, PR [19147](https://github.com/python/mypy/pull/19147)) +* Fix nondeterministic type checking by making join between `type` and TypeVar commute (Shantanu, PR [19149](https://github.com/python/mypy/pull/19149)) + +### Remove Support for Targeting Python 3.8 + +Mypy now requires `--python-version 3.9` or greater. Support for targeting Python 3.8 is +fully removed now. Since 3.8 is an unsupported version, mypy will default to the oldest +supported version (currently 3.9) if you still try to target 3.8. + +This change is necessary because typeshed stopped supporting Python 3.8 after it +reached its End of Life in October 2024. + +Contributed by Marc Mueller +(PR [19157](https://github.com/python/mypy/pull/19157), PR [19162](https://github.com/python/mypy/pull/19162)). + +### Initial Support for Python 3.14 + +Mypy is now tested on 3.14 and mypyc works with 3.14.0b3 and later. +Binary wheels compiled with mypyc for mypy itself will be available for 3.14 +some time after 3.14.0rc1 has been released. + +Note that not all features are supported just yet. + +Contributed by Marc Mueller (PR [19164](https://github.com/python/mypy/pull/19164)) + +### Deprecated Flag: `--force-uppercase-builtins` + +Mypy only supports Python 3.9+. The `--force-uppercase-builtins` flag is now +deprecated as unnecessary, and a no-op. It will be removed in a future version. + +Contributed by Marc Mueller (PR [19176](https://github.com/python/mypy/pull/19176)) + +### Mypyc: Improvements to Generators and Async Functions + +This release includes both performance improvements and bug fixes related +to generators and async functions (these share many implementation details). + +* Fix exception swallowing in async try/finally blocks with await (Chainfire, PR [19353](https://github.com/python/mypy/pull/19353)) +* Fix AttributeError in async try/finally with mixed return paths (Chainfire, PR [19361](https://github.com/python/mypy/pull/19361)) +* Make generated generator helper method internal (Jukka Lehtosalo, PR [19268](https://github.com/python/mypy/pull/19268)) +* Free coroutine after await encounters StopIteration (Jukka Lehtosalo, PR [19231](https://github.com/python/mypy/pull/19231)) +* Use non-tagged integer for generator label (Jukka Lehtosalo, PR [19218](https://github.com/python/mypy/pull/19218)) +* Merge generator and environment classes in simple cases (Jukka Lehtosalo, PR [19207](https://github.com/python/mypy/pull/19207)) + +### Mypyc: Partial, Unsafe Support for Free Threading + +Mypyc has minimal, quite memory-unsafe support for the free threaded +builds of 3.14. It is also only lightly tested. Bug reports and experience +reports are welcome! + +Here are some of the major limitations: +* Free threading only works when compiling a single module at a time. +* If there is concurrent access to an object while another thread is mutating the same + object, it's possible to encounter segfaults and memory corruption. +* There are no efficient native primitives for thread synthronization, though the + regular `threading` module can be used. +* Some workloads don't scale well to multiple threads for no clear reason. + +Related PRs: + +* Enable partial, unsafe support for free-threading (Jukka Lehtosalo, PR [19167](https://github.com/python/mypy/pull/19167)) +* Fix incref/decref on free-threaded builds (Jukka Lehtosalo, PR [19127](https://github.com/python/mypy/pull/19127)) + +### Other Mypyc Fixes and Improvements + +* Derive .c file name from full module name if using multi_file (Jukka Lehtosalo, PR [19278](https://github.com/python/mypy/pull/19278)) +* Support overriding the group name used in output files (Jukka Lehtosalo, PR [19272](https://github.com/python/mypy/pull/19272)) +* Add note about using non-native class to subclass built-in types (Jukka Lehtosalo, PR [19236](https://github.com/python/mypy/pull/19236)) +* Make some generated classes implicitly final (Jukka Lehtosalo, PR [19235](https://github.com/python/mypy/pull/19235)) +* Don't simplify module prefixes if using separate compilation (Jukka Lehtosalo, PR [19206](https://github.com/python/mypy/pull/19206)) + +### Stubgen Improvements + +* Add import for `types` in `__exit__` method signature (Alexey Makridenko, PR [19120](https://github.com/python/mypy/pull/19120)) +* Add support for including class and property docstrings (Chad Dombrova, PR [17964](https://github.com/python/mypy/pull/17964)) +* Don't generate `Incomplete | None = None` argument annotation (Sebastian Rittau, PR [19097](https://github.com/python/mypy/pull/19097)) +* Support several more constructs in stubgen's alias printer (Stanislav Terliakov, PR [18888](https://github.com/python/mypy/pull/18888)) + +### Miscellaneous Fixes and Improvements + +* Combine the revealed types of multiple iteration steps in a more robust manner (Christoph Tyralla, PR [19324](https://github.com/python/mypy/pull/19324)) +* Improve the handling of "iteration dependent" errors and notes in finally clauses (Christoph Tyralla, PR [19270](https://github.com/python/mypy/pull/19270)) +* Lessen dmypy suggest path limitations for Windows machines (CoolCat467, PR [19337](https://github.com/python/mypy/pull/19337)) +* Fix type ignore comments erroneously marked as unused by dmypy (Charlie Denton, PR [15043](https://github.com/python/mypy/pull/15043)) +* Fix misspelled `exhaustive-match` error code (johnthagen, PR [19276](https://github.com/python/mypy/pull/19276)) +* Fix missing error context for unpacking assignment involving star expression (Brian Schubert, PR [19258](https://github.com/python/mypy/pull/19258)) +* Fix and simplify error de-duplication (Ivan Levkivskyi, PR [19247](https://github.com/python/mypy/pull/19247)) +* Disallow `ClassVar` in type aliases (Brian Schubert, PR [19263](https://github.com/python/mypy/pull/19263)) +* Add script that prints list of compiled files when compiling mypy (Jukka Lehtosalo, PR [19260](https://github.com/python/mypy/pull/19260)) +* Fix help message url for "None and Optional handling" section (Guy Wilson, PR [19252](https://github.com/python/mypy/pull/19252)) +* Display fully qualified name of imported base classes in errors about incompatible overrides (Mikhail Golubev, PR [19115](https://github.com/python/mypy/pull/19115)) +* Avoid false `unreachable`, `redundant-expr`, and `redundant-casts` warnings in loops more robustly and efficiently, and avoid multiple `revealed type` notes for the same line (Christoph Tyralla, PR [19118](https://github.com/python/mypy/pull/19118)) +* Fix type extraction from `isinstance` checks (Stanislav Terliakov, PR [19223](https://github.com/python/mypy/pull/19223)) +* Erase stray type variables in `functools.partial` (Stanislav Terliakov, PR [18954](https://github.com/python/mypy/pull/18954)) +* Make inferring condition value recognize the whole truth table (Stanislav Terliakov, PR [18944](https://github.com/python/mypy/pull/18944)) +* Support type aliases, `NamedTuple` and `TypedDict` in constrained TypeVar defaults (Stanislav Terliakov, PR [18884](https://github.com/python/mypy/pull/18884)) +* Move dataclass `kw_only` fields to the end of the signature (Stanislav Terliakov, PR [19018](https://github.com/python/mypy/pull/19018)) +* Provide a better fallback value for the `python_version` option (Marc Mueller, PR [19162](https://github.com/python/mypy/pull/19162)) +* Avoid spurious non-overlapping equality error with metaclass with `__eq__` (Michael J. Sullivan, PR [19220](https://github.com/python/mypy/pull/19220)) +* Narrow type variable bounds (Ivan Levkivskyi, PR [19183](https://github.com/python/mypy/pull/19183)) +* Add classifier for Python 3.14 (Marc Mueller, PR [19199](https://github.com/python/mypy/pull/19199)) +* Capitalize syntax error messages (Charulata, PR [19114](https://github.com/python/mypy/pull/19114)) +* Infer constraints eagerly if actual is Any (Ivan Levkivskyi, PR [19190](https://github.com/python/mypy/pull/19190)) +* Include walrus assignments in conditional inference (Stanislav Terliakov, PR [19038](https://github.com/python/mypy/pull/19038)) +* Use PEP 604 syntax when converting types to strings (Marc Mueller, PR [19179](https://github.com/python/mypy/pull/19179)) +* Use more lower-case builtin types in error messages (Marc Mueller, PR [19177](https://github.com/python/mypy/pull/19177)) +* Fix example to use correct method of Stack (Łukasz Kwieciński, PR [19123](https://github.com/python/mypy/pull/19123)) +* Forbid `.pop` of `Readonly` `NotRequired` TypedDict items (Stanislav Terliakov, PR [19133](https://github.com/python/mypy/pull/19133)) +* Emit a friendlier warning on invalid exclude regex, instead of a stacktrace (wyattscarpenter, PR [19102](https://github.com/python/mypy/pull/19102)) +* Enable ANSI color codes for dmypy client in Windows (wyattscarpenter, PR [19088](https://github.com/python/mypy/pull/19088)) +* Extend special case for context-based type variable inference to unions in return position (Stanislav Terliakov, PR [18976](https://github.com/python/mypy/pull/18976)) + +### Mypy 1.17.1 +* Retain `None` as constraints bottom if no bottoms were provided (Stanislav Terliakov, PR [19485](https://github.com/python/mypy/pull/19485)) +* Fix "ignored exception in `hasattr`" in dmypy (Stanislav Terliakov, PR [19428](https://github.com/python/mypy/pull/19428)) +* Prevent a crash when InitVar is redefined with a method in a subclass (Stanislav Terliakov, PR [19453](https://github.com/python/mypy/pull/19453)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +* Alexey Makridenko +* Brian Schubert +* Chad Dombrova +* Chainfire +* Charlie Denton +* Charulata +* Christoph Tyralla +* CoolCat467 +* Donal Burns +* Guy Wilson +* Ivan Levkivskyi +* johnthagen +* Jukka Lehtosalo +* Łukasz Kwieciński +* Marc Mueller +* Michael J. Sullivan +* Mikhail Golubev +* Sebastian Rittau +* Shantanu +* Stanislav Terliakov +* wyattscarpenter + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +## Mypy 1.16 + +We’ve just uploaded mypy 1.16 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). + +### Different Property Getter and Setter Types + +Mypy now supports using different types for a property getter and setter: + +```python +class A: + _value: int + + @property + def foo(self) -> int: + return self._value + + @foo.setter + def foo(self, x: str | int) -> None: + try: + self._value = int(x) + except ValueError: + raise Exception(f"'{x}' is not a valid value for 'foo'") +``` +This was contributed by Ivan Levkivskyi (PR [18510](https://github.com/python/mypy/pull/18510)). + +### Flexible Variable Redefinitions (Experimental) + +Mypy now allows unannotated variables to be freely redefined with +different types when using the experimental `--allow-redefinition-new` +flag. You will also need to enable `--local-partial-types`. Mypy will +now infer a union type when different types are assigned to a +variable: + +```py +# mypy: allow-redefinition-new, local-partial-types + +def f(n: int, b: bool) -> int | str: + if b: + x = n + else: + x = str(n) + # Type of 'x' is int | str here. + return x +``` + +Without the new flag, mypy only supports inferring optional types (`X +| None`) from multiple assignments, but now mypy can infer arbitrary +union types. + +An unannotated variable can now also have different types in different +code locations: + +```py +# mypy: allow-redefinition-new, local-partial-types +... + +if cond(): + for x in range(n): + # Type of 'x' is 'int' here + ... +else: + for x in ['a', 'b']: + # Type of 'x' is 'str' here + ... +``` + +We are planning to turn this flag on by default in mypy 2.0, along +with `--local-partial-types`. The feature is still experimental and +has known issues, and the semantics may still change in the +future. You may need to update or add type annotations when switching +to the new behavior, but if you encounter anything unexpected, please +create a GitHub issue. + +This was contributed by Jukka Lehtosalo +(PR [18727](https://github.com/python/mypy/pull/18727), PR [19153](https://github.com/python/mypy/pull/19153)). + +### Stricter Type Checking with Imprecise Types + +Mypy can now detect additional errors in code that uses `Any` types or has missing function annotations. + +When calling `dict.get(x, None)` on an object of type `dict[str, Any]`, this +now results in an optional type (in the past it was `Any`): + +```python +def f(d: dict[str, Any]) -> int: + # Error: Return value has type "Any | None" but expected "int" + return d.get("x", None) +``` + +Type narrowing using assignments can result in more precise types in +the presence of `Any` types: + +```python +def foo(): ... + +def bar(n: int) -> None: + x = foo() + # Type of 'x' is 'Any' here + if n > 5: + x = str(n) + # Type of 'x' is 'str' here +``` + +When using `--check-untyped-defs`, unannotated overrides are now +checked more strictly against superclass definitions. + +Related PRs: + + * Use union types instead of join in binder (Ivan Levkivskyi, PR [18538](https://github.com/python/mypy/pull/18538)) + * Check superclass compatibility of untyped methods if `--check-untyped-defs` is set (Stanislav Terliakov, PR [18970](https://github.com/python/mypy/pull/18970)) + +### Improvements to Attribute Resolution + +This release includes several fixes to inconsistent resolution of attribute, method and descriptor types. + + * Consolidate descriptor handling (Ivan Levkivskyi, PR [18831](https://github.com/python/mypy/pull/18831)) + * Make multiple inheritance checking use common semantics (Ivan Levkivskyi, PR [18876](https://github.com/python/mypy/pull/18876)) + * Make method override checking use common semantics (Ivan Levkivskyi, PR [18870](https://github.com/python/mypy/pull/18870)) + * Fix descriptor overload selection (Ivan Levkivskyi, PR [18868](https://github.com/python/mypy/pull/18868)) + * Handle union types when binding `self` (Ivan Levkivskyi, PR [18867](https://github.com/python/mypy/pull/18867)) + * Make variable override checking use common semantics (Ivan Levkivskyi, PR [18847](https://github.com/python/mypy/pull/18847)) + * Make descriptor handling behave consistently (Ivan Levkivskyi, PR [18831](https://github.com/python/mypy/pull/18831)) + +### Make Implementation for Abstract Overloads Optional + +The implementation can now be omitted for abstract overloaded methods, +even outside stubs: + +```py +from abc import abstractmethod +from typing import overload + +class C: + @abstractmethod + @overload + def foo(self, x: int) -> int: ... + + @abstractmethod + @overload + def foo(self, x: str) -> str: ... + + # No implementation required for "foo" +``` + +This was contributed by Ivan Levkivskyi (PR [18882](https://github.com/python/mypy/pull/18882)). + +### Option to Exclude Everything in .gitignore + +You can now use `--exclude-gitignore` to exclude everything in a +`.gitignore` file from the mypy build. This behaves similar to +excluding the paths using `--exclude`. We might enable this by default +in a future mypy release. + +This was contributed by Ivan Levkivskyi (PR [18696](https://github.com/python/mypy/pull/18696)). + +### Selectively Disable Deprecated Warnings + +It's now possible to selectively disable warnings generated from +[`warnings.deprecated`](https://docs.python.org/3/library/warnings.html#warnings.deprecated) +using the [`--deprecated-calls-exclude`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-deprecated-calls-exclude) +option: + +```python +# mypy --enable-error-code deprecated +# --deprecated-calls-exclude=foo.A +import foo + +foo.A().func() # OK, the deprecated warning is ignored +``` + +```python +# file foo.py + +from typing_extensions import deprecated + +class A: + @deprecated("Use A.func2 instead") + def func(self): pass + + ... +``` + +Contributed by Marc Mueller (PR [18641](https://github.com/python/mypy/pull/18641)) + +### Annotating Native/Non-Native Classes in Mypyc + +You can now declare a class as a non-native class when compiling with +mypyc. Unlike native classes, which are extension classes and have an +immutable structure, non-native classes are normal Python classes at +runtime and are fully dynamic. Example: + +```python +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class NonNativeClass: + ... + +o = NonNativeClass() + +# Ok, even if attribute "foo" not declared in class body +setattr(o, "foo", 1) +``` + +Classes are native by default in compiled modules, but classes that +use certain features (such as most metaclasses) are implicitly +non-native. + +You can also explicitly declare a class as native. In this case mypyc +will generate an error if it can't compile the class as a native +class, instead of falling back to a non-native class: + +```python +from mypy_extensions import mypyc_attr +from foo import MyMeta + +# Error: Unsupported metaclass for a native class +@mypyc_attr(native_class=True) +class C(metaclass=MyMeta): + ... +``` + +Since native classes are significantly more efficient that non-native +classes, you may want to ensure that certain classes always compiled +as native classes. + +This feature was contributed by Valentin Stanciu (PR [18802](https://github.com/python/mypy/pull/18802)). + +### Mypyc Fixes and Improvements + + * Improve documentation of native and non-native classes (Jukka Lehtosalo, PR [19154](https://github.com/python/mypy/pull/19154)) + * Fix compilation when using Python 3.13 debug build (Valentin Stanciu, PR [19045](https://github.com/python/mypy/pull/19045)) + * Show the reason why a class can't be a native class (Valentin Stanciu, PR [19016](https://github.com/python/mypy/pull/19016)) + * Support await/yield while temporary values are live (Michael J. Sullivan, PR [16305](https://github.com/python/mypy/pull/16305)) + * Fix spilling values with overlapping error values (Jukka Lehtosalo, PR [18961](https://github.com/python/mypy/pull/18961)) + * Fix reference count of spilled register in async def (Jukka Lehtosalo, PR [18957](https://github.com/python/mypy/pull/18957)) + * Add basic optimization for `sorted` (Marc Mueller, PR [18902](https://github.com/python/mypy/pull/18902)) + * Fix access of class object in a type annotation (Advait Dixit, PR [18874](https://github.com/python/mypy/pull/18874)) + * Optimize `list.__imul__` and `tuple.__mul__ `(Marc Mueller, PR [18887](https://github.com/python/mypy/pull/18887)) + * Optimize `list.__add__`, `list.__iadd__` and `tuple.__add__` (Marc Mueller, PR [18845](https://github.com/python/mypy/pull/18845)) + * Add and implement primitive `list.copy()` (exertustfm, PR [18771](https://github.com/python/mypy/pull/18771)) + * Optimize `builtins.repr` (Marc Mueller, PR [18844](https://github.com/python/mypy/pull/18844)) + * Support iterating over keys/values/items of dict-bound TypeVar and ParamSpec.kwargs (Stanislav Terliakov, PR [18789](https://github.com/python/mypy/pull/18789)) + * Add efficient primitives for `str.strip()` etc. (Advait Dixit, PR [18742](https://github.com/python/mypy/pull/18742)) + * Document that `strip()` etc. are optimized (Jukka Lehtosalo, PR [18793](https://github.com/python/mypy/pull/18793)) + * Fix mypyc crash with enum type aliases (Valentin Stanciu, PR [18725](https://github.com/python/mypy/pull/18725)) + * Optimize `str.find` and `str.rfind` (Marc Mueller, PR [18709](https://github.com/python/mypy/pull/18709)) + * Optimize `str.__contains__` (Marc Mueller, PR [18705](https://github.com/python/mypy/pull/18705)) + * Fix order of steal/unborrow in tuple unpacking (Ivan Levkivskyi, PR [18732](https://github.com/python/mypy/pull/18732)) + * Optimize `str.partition` and `str.rpartition` (Marc Mueller, PR [18702](https://github.com/python/mypy/pull/18702)) + * Optimize `str.startswith` and `str.endswith` with tuple argument (Marc Mueller, PR [18678](https://github.com/python/mypy/pull/18678)) + * Improve `str.startswith` and `str.endswith` with tuple argument (Marc Mueller, PR [18703](https://github.com/python/mypy/pull/18703)) + * `pythoncapi_compat`: don't define Py_NULL if it is already defined (Michael R. Crusoe, PR [18699](https://github.com/python/mypy/pull/18699)) + * Optimize `str.splitlines` (Marc Mueller, PR [18677](https://github.com/python/mypy/pull/18677)) + * Mark `dict.setdefault` as optimized (Marc Mueller, PR [18685](https://github.com/python/mypy/pull/18685)) + * Support `__del__` methods (Advait Dixit, PR [18519](https://github.com/python/mypy/pull/18519)) + * Optimize `str.rsplit` (Marc Mueller, PR [18673](https://github.com/python/mypy/pull/18673)) + * Optimize `str.removeprefix` and `str.removesuffix` (Marc Mueller, PR [18672](https://github.com/python/mypy/pull/18672)) + * Recognize literal types in `__match_args__` (Stanislav Terliakov, PR [18636](https://github.com/python/mypy/pull/18636)) + * Fix non extension classes with attribute annotations using forward references (Valentin Stanciu, PR [18577](https://github.com/python/mypy/pull/18577)) + * Use lower-case generic types such as `list[t]` in documentation (Jukka Lehtosalo, PR [18576](https://github.com/python/mypy/pull/18576)) + * Improve support for `frozenset` (Marc Mueller, PR [18571](https://github.com/python/mypy/pull/18571)) + * Fix wheel build for cp313-win (Marc Mueller, PR [18560](https://github.com/python/mypy/pull/18560)) + * Reduce impact of immortality (introduced in Python 3.12) on reference counting performance (Jukka Lehtosalo, PR [18459](https://github.com/python/mypy/pull/18459)) + * Update math error messages for 3.14 (Marc Mueller, PR [18534](https://github.com/python/mypy/pull/18534)) + * Update math error messages for 3.14 (2) (Marc Mueller, PR [18949](https://github.com/python/mypy/pull/18949)) + * Replace deprecated `_PyLong_new` with `PyLongWriter` API (Marc Mueller, PR [18532](https://github.com/python/mypy/pull/18532)) + +### Fixes to Crashes + + * Traverse module ancestors when traversing reachable graph nodes during dmypy update (Stanislav Terliakov, PR [18906](https://github.com/python/mypy/pull/18906)) + * Fix crash on multiple unpacks in a bare type application (Stanislav Terliakov, PR [18857](https://github.com/python/mypy/pull/18857)) + * Prevent crash when enum/TypedDict call is stored as a class attribute (Stanislav Terliakov, PR [18861](https://github.com/python/mypy/pull/18861)) + * Fix crash on multiple unpacks in a bare type application (Stanislav Terliakov, PR [18857](https://github.com/python/mypy/pull/18857)) + * Fix crash on type inference against non-normal callables (Ivan Levkivskyi, PR [18858](https://github.com/python/mypy/pull/18858)) + * Fix crash on decorated getter in settable property (Ivan Levkivskyi, PR [18787](https://github.com/python/mypy/pull/18787)) + * Fix crash on callable with `*args` and suffix against Any (Ivan Levkivskyi, PR [18781](https://github.com/python/mypy/pull/18781)) + * Fix crash on deferred supertype and setter override (Ivan Levkivskyi, PR [18649](https://github.com/python/mypy/pull/18649)) + * Fix crashes on incorrectly detected recursive aliases (Ivan Levkivskyi, PR [18625](https://github.com/python/mypy/pull/18625)) + * Report that `NamedTuple` and `dataclass` are incompatile instead of crashing (Christoph Tyralla, PR [18633](https://github.com/python/mypy/pull/18633)) + * Fix mypy daemon crash (Valentin Stanciu, PR [19087](https://github.com/python/mypy/pull/19087)) + +### Performance Improvements + +These are specific to mypy. Mypyc-related performance improvements are discussed elsewhere. + + * Speed up binding `self` in trivial cases (Ivan Levkivskyi, PR [19024](https://github.com/python/mypy/pull/19024)) + * Small constraint solver optimization (Aaron Gokaslan, PR [18688](https://github.com/python/mypy/pull/18688)) + +### Documentation Updates + + * Improve documentation of `--strict` (lenayoung8, PR [18903](https://github.com/python/mypy/pull/18903)) + * Remove a note about `from __future__ import annotations` (Ageev Maxim, PR [18915](https://github.com/python/mypy/pull/18915)) + * Improve documentation on type narrowing (Tim Hoffmann, PR [18767](https://github.com/python/mypy/pull/18767)) + * Fix metaclass usage example (Georg, PR [18686](https://github.com/python/mypy/pull/18686)) + * Update documentation on `extra_checks` flag (Ivan Levkivskyi, PR [18537](https://github.com/python/mypy/pull/18537)) + +### Stubgen Improvements + + * Fix `TypeAlias` handling (Alexey Makridenko, PR [18960](https://github.com/python/mypy/pull/18960)) + * Handle `arg=None` in C extension modules (Anthony Sottile, PR [18768](https://github.com/python/mypy/pull/18768)) + * Fix valid type detection to allow pipe unions (Chad Dombrova, PR [18726](https://github.com/python/mypy/pull/18726)) + * Include simple decorators in stub files (Marc Mueller, PR [18489](https://github.com/python/mypy/pull/18489)) + * Support positional and keyword-only arguments in stubdoc (Paul Ganssle, PR [18762](https://github.com/python/mypy/pull/18762)) + * Fall back to `Incomplete` if we are unable to determine the module name (Stanislav Terliakov, PR [19084](https://github.com/python/mypy/pull/19084)) + +### Stubtest Improvements + + * Make stubtest ignore `__slotnames__` (Nick Pope, PR [19077](https://github.com/python/mypy/pull/19077)) + * Fix stubtest tests on 3.14 (Jelle Zijlstra, PR [19074](https://github.com/python/mypy/pull/19074)) + * Support for `strict_bytes` in stubtest (Joren Hammudoglu, PR [19002](https://github.com/python/mypy/pull/19002)) + * Understand override (Shantanu, PR [18815](https://github.com/python/mypy/pull/18815)) + * Better checking of runtime arguments with dunder names (Shantanu, PR [18756](https://github.com/python/mypy/pull/18756)) + * Ignore setattr and delattr inherited from object (Stephen Morton, PR [18325](https://github.com/python/mypy/pull/18325)) + +### Miscellaneous Fixes and Improvements + + * Add `--strict-bytes` to `--strict` (wyattscarpenter, PR [19049](https://github.com/python/mypy/pull/19049)) + * Admit that Final variables are never redefined (Stanislav Terliakov, PR [19083](https://github.com/python/mypy/pull/19083)) + * Add special support for `@django.cached_property` needed in `django-stubs` (sobolevn, PR [18959](https://github.com/python/mypy/pull/18959)) + * Do not narrow types to `Never` with binder (Ivan Levkivskyi, PR [18972](https://github.com/python/mypy/pull/18972)) + * Local forward references should precede global forward references (Ivan Levkivskyi, PR [19000](https://github.com/python/mypy/pull/19000)) + * Do not cache module lookup results in incremental mode that may become invalid (Stanislav Terliakov, PR [19044](https://github.com/python/mypy/pull/19044)) + * Only consider meta variables in ambiguous "any of" constraints (Stanislav Terliakov, PR [18986](https://github.com/python/mypy/pull/18986)) + * Allow accessing `__init__` on final classes and when `__init__` is final (Stanislav Terliakov, PR [19035](https://github.com/python/mypy/pull/19035)) + * Treat varargs as positional-only (A5rocks, PR [19022](https://github.com/python/mypy/pull/19022)) + * Enable colored output for argparse help in Python 3.14 (Marc Mueller, PR [19021](https://github.com/python/mypy/pull/19021)) + * Fix argparse for Python 3.14 (Marc Mueller, PR [19020](https://github.com/python/mypy/pull/19020)) + * `dmypy suggest` can now suggest through contextmanager-based decorators (Anthony Sottile, PR [18948](https://github.com/python/mypy/pull/18948)) + * Fix `__r__` being used under the same `____` hook (Arnav Jain, PR [18995](https://github.com/python/mypy/pull/18995)) + * Prioritize `.pyi` from `-stubs` packages over bundled `.pyi` (Joren Hammudoglu, PR [19001](https://github.com/python/mypy/pull/19001)) + * Fix missing subtype check case for `type[T]` (Stanislav Terliakov, PR [18975](https://github.com/python/mypy/pull/18975)) + * Fixes to the detection of redundant casts (Anthony Sottile, PR [18588](https://github.com/python/mypy/pull/18588)) + * Make some parse errors non-blocking (Shantanu, PR [18941](https://github.com/python/mypy/pull/18941)) + * Fix PEP 695 type alias with a mix of type arguments (PEP 696) (Marc Mueller, PR [18919](https://github.com/python/mypy/pull/18919)) + * Allow deeper recursion in mypy daemon, better error reporting (Carter Dodd, PR [17707](https://github.com/python/mypy/pull/17707)) + * Fix swapped errors for frozen/non-frozen dataclass inheritance (Nazrawi Demeke, PR [18918](https://github.com/python/mypy/pull/18918)) + * Fix incremental issue with namespace packages (Shantanu, PR [18907](https://github.com/python/mypy/pull/18907)) + * Exclude irrelevant members when narrowing union overlapping with enum (Stanislav Terliakov, PR [18897](https://github.com/python/mypy/pull/18897)) + * Flatten union before contracting literals when checking subtyping (Stanislav Terliakov, PR [18898](https://github.com/python/mypy/pull/18898)) + * Do not add `kw_only` dataclass fields to `__match_args__` (sobolevn, PR [18892](https://github.com/python/mypy/pull/18892)) + * Fix error message when returning long tuple with type mismatch (Thomas Mattone, PR [18881](https://github.com/python/mypy/pull/18881)) + * Treat `TypedDict` (old-style) aliases as regular `TypedDict`s (Stanislav Terliakov, PR [18852](https://github.com/python/mypy/pull/18852)) + * Warn about unused `type: ignore` comments when error code is disabled (Brian Schubert, PR [18849](https://github.com/python/mypy/pull/18849)) + * Reject duplicate `ParamSpec.{args,kwargs}` at call site (Stanislav Terliakov, PR [18854](https://github.com/python/mypy/pull/18854)) + * Make detection of enum members more consistent (sobolevn, PR [18675](https://github.com/python/mypy/pull/18675)) + * Admit that `**kwargs` mapping subtypes may have no direct type parameters (Stanislav Terliakov, PR [18850](https://github.com/python/mypy/pull/18850)) + * Don't suggest `types-setuptools` for `pkg_resources` (Shantanu, PR [18840](https://github.com/python/mypy/pull/18840)) + * Suggest `scipy-stubs` for `scipy` as non-typeshed stub package (Joren Hammudoglu, PR [18832](https://github.com/python/mypy/pull/18832)) + * Narrow tagged unions in match statements (Gene Parmesan Thomas, PR [18791](https://github.com/python/mypy/pull/18791)) + * Consistently store settable property type (Ivan Levkivskyi, PR [18774](https://github.com/python/mypy/pull/18774)) + * Do not blindly undefer on leaving function (Ivan Levkivskyi, PR [18674](https://github.com/python/mypy/pull/18674)) + * Process superclass methods before subclass methods in semanal (Ivan Levkivskyi, PR [18723](https://github.com/python/mypy/pull/18723)) + * Only defer top-level functions (Ivan Levkivskyi, PR [18718](https://github.com/python/mypy/pull/18718)) + * Add one more type-checking pass (Ivan Levkivskyi, PR [18717](https://github.com/python/mypy/pull/18717)) + * Properly account for `member` and `nonmember` in enums (sobolevn, PR [18559](https://github.com/python/mypy/pull/18559)) + * Fix instance vs tuple subtyping edge case (Ivan Levkivskyi, PR [18664](https://github.com/python/mypy/pull/18664)) + * Improve handling of Any/object in variadic generics (Ivan Levkivskyi, PR [18643](https://github.com/python/mypy/pull/18643)) + * Fix handling of named tuples in class match pattern (Ivan Levkivskyi, PR [18663](https://github.com/python/mypy/pull/18663)) + * Fix regression for user config files (Shantanu, PR [18656](https://github.com/python/mypy/pull/18656)) + * Fix dmypy socket issue on GNU/Hurd (Mattias Ellert, PR [18630](https://github.com/python/mypy/pull/18630)) + * Don't assume that for loop body index variable is always set (Jukka Lehtosalo, PR [18631](https://github.com/python/mypy/pull/18631)) + * Fix overlap check for variadic generics (Ivan Levkivskyi, PR [18638](https://github.com/python/mypy/pull/18638)) + * Improve support for `functools.partial` of overloaded callable protocol (Shantanu, PR [18639](https://github.com/python/mypy/pull/18639)) + * Allow lambdas in `except*` clauses (Stanislav Terliakov, PR [18620](https://github.com/python/mypy/pull/18620)) + * Fix trailing commas in many multiline string options in `pyproject.toml` (sobolevn, PR [18624](https://github.com/python/mypy/pull/18624)) + * Allow trailing commas for `files` setting in `mypy.ini` and `setup.ini` (sobolevn, PR [18621](https://github.com/python/mypy/pull/18621)) + * Fix "not callable" issue for `@dataclass(frozen=True)` with `Final` attr (sobolevn, PR [18572](https://github.com/python/mypy/pull/18572)) + * Add missing TypedDict special case when checking member access (Stanislav Terliakov, PR [18604](https://github.com/python/mypy/pull/18604)) + * Use lower case `list` and `dict` in invariance notes (Jukka Lehtosalo, PR [18594](https://github.com/python/mypy/pull/18594)) + * Fix inference when class and instance match protocol (Ivan Levkivskyi, PR [18587](https://github.com/python/mypy/pull/18587)) + * Remove support for `builtins.Any` (Marc Mueller, PR [18578](https://github.com/python/mypy/pull/18578)) + * Update the overlapping check for tuples to account for NamedTuples (A5rocks, PR [18564](https://github.com/python/mypy/pull/18564)) + * Fix `@deprecated` (PEP 702) with normal overloaded methods (Christoph Tyralla, PR [18477](https://github.com/python/mypy/pull/18477)) + * Start propagating end columns/lines for `type-arg` errors (A5rocks, PR [18533](https://github.com/python/mypy/pull/18533)) + * Improve handling of `type(x) is Foo` checks (Stanislav Terliakov, PR [18486](https://github.com/python/mypy/pull/18486)) + * Suggest `typing.Literal` for exit-return error messages (Marc Mueller, PR [18541](https://github.com/python/mypy/pull/18541)) + * Allow redefinitions in except/else/finally (Stanislav Terliakov, PR [18515](https://github.com/python/mypy/pull/18515)) + * Disallow setting Python version using inline config (Shantanu, PR [18497](https://github.com/python/mypy/pull/18497)) + * Improve type inference in tuple multiplication plugin (Shantanu, PR [18521](https://github.com/python/mypy/pull/18521)) + * Add missing line number to `yield from` with wrong type (Stanislav Terliakov, PR [18518](https://github.com/python/mypy/pull/18518)) + * Hint at argument names when formatting callables with compatible return types in error messages (Stanislav Terliakov, PR [18495](https://github.com/python/mypy/pull/18495)) + * Add better naming and improve compatibility for ad hoc intersections of instances (Christoph Tyralla, PR [18506](https://github.com/python/mypy/pull/18506)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +- A5rocks +- Aaron Gokaslan +- Advait Dixit +- Ageev Maxim +- Alexey Makridenko +- Ali Hamdan +- Anthony Sottile +- Arnav Jain +- Brian Schubert +- bzoracler +- Carter Dodd +- Chad Dombrova +- Christoph Tyralla +- Dimitri Papadopoulos Orfanos +- Emma Smith +- exertustfm +- Gene Parmesan Thomas +- Georg +- Ivan Levkivskyi +- Jared Hance +- Jelle Zijlstra +- Joren Hammudoglu +- lenayoung8 +- Marc Mueller +- Mattias Ellert +- Michael J. Sullivan +- Michael R. Crusoe +- Nazrawi Demeke +- Nick Pope +- Paul Ganssle +- Shantanu +- sobolevn +- Stanislav Terliakov +- Stephen Morton +- Thomas Mattone +- Tim Hoffmann +- Tim Ruffing +- Valentin Stanciu +- Wesley Collin Wright +- wyattscarpenter + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +## Mypy 1.15 + +We’ve just uploaded mypy 1.15 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). + +### Performance Improvements + +Mypy is up to 40% faster in some use cases. This improvement comes largely from tuning the performance +of the garbage collector. Additionally, the release includes several micro-optimizations that may +be impactful for large projects. + +Contributed by Jukka Lehtosalo +- PR [18306](https://github.com/python/mypy/pull/18306) +- PR [18302](https://github.com/python/mypy/pull/18302) +- PR [18298](https://github.com/python/mypy/pull/18298) +- PR [18299](https://github.com/python/mypy/pull/18299) + +### Mypyc Accelerated Mypy Wheels for ARM Linux + +For best performance, mypy can be compiled to C extension modules using mypyc. This makes +mypy 3-5x faster than when interpreted with pure Python. We now build and upload mypyc +accelerated mypy wheels for `manylinux_aarch64` to PyPI, making it easy for Linux users on +ARM platforms to realise this speedup -- just `pip install` the latest mypy. + +Contributed by Christian Bundy and Marc Mueller +(PR [mypy_mypyc-wheels#76](https://github.com/mypyc/mypy_mypyc-wheels/pull/76), +PR [mypy_mypyc-wheels#89](https://github.com/mypyc/mypy_mypyc-wheels/pull/89)). + +### `--strict-bytes` + +By default, mypy treats `bytearray` and `memoryview` values as assignable to the `bytes` +type, for historical reasons. Use the `--strict-bytes` flag to disable this +behavior. [PEP 688](https://peps.python.org/pep-0688) specified the removal of this +special case. The flag will be enabled by default in **mypy 2.0**. + +Contributed by Ali Hamdan (PR [18263](https://github.com/python/mypy/pull/18263)) and +Shantanu Jain (PR [13952](https://github.com/python/mypy/pull/13952)). + +### Improvements to Reachability Analysis and Partial Type Handling in Loops + +This change results in mypy better modelling control flow within loops and hence detecting +several previously ignored issues. In some cases, this change may require additional +explicit variable annotations. + +Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull/18180), +PR [18433](https://github.com/python/mypy/pull/18433)). + +(Speaking of partial types, remember that we plan to enable `--local-partial-types` +by default in **mypy 2.0**.) + +### Better Discovery of Configuration Files + +Mypy will now walk up the filesystem (up until a repository or file system root) to discover +configuration files. See the +[mypy configuration file documentation](https://mypy.readthedocs.io/en/stable/config_file.html) +for more details. + +Contributed by Mikhail Shiryaev and Shantanu Jain +(PR [16965](https://github.com/python/mypy/pull/16965), PR [18482](https://github.com/python/mypy/pull/18482)) + +### Better Line Numbers for Decorators and Slice Expressions + +Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, +you may have to change the location of a `# type: ignore` comment. + +Contributed by Shantanu Jain (PR [18392](https://github.com/python/mypy/pull/18392), +PR [18397](https://github.com/python/mypy/pull/18397)). + +### Drop Support for Python 3.8 + +Mypy no longer supports running with Python 3.8, which has reached end-of-life. +When running mypy with Python 3.9+, it is still possible to type check code +that needs to support Python 3.8 with the `--python-version 3.8` argument. +Support for this will be dropped in the first half of 2025! + +Contributed by Marc Mueller (PR [17492](https://github.com/python/mypy/pull/17492)). + +### Mypyc Improvements + + * Fix `__init__` for classes with `@attr.s(slots=True)` (Advait Dixit, PR [18447](https://github.com/python/mypy/pull/18447)) + * Report error for nested class instead of crashing (Valentin Stanciu, PR [18460](https://github.com/python/mypy/pull/18460)) + * Fix `InitVar` for dataclasses (Advait Dixit, PR [18319](https://github.com/python/mypy/pull/18319)) + * Remove unnecessary mypyc files from wheels (Marc Mueller, PR [18416](https://github.com/python/mypy/pull/18416)) + * Fix issues with relative imports (Advait Dixit, PR [18286](https://github.com/python/mypy/pull/18286)) + * Add faster primitive for some list get item operations (Jukka Lehtosalo, PR [18136](https://github.com/python/mypy/pull/18136)) + * Fix iteration over `NamedTuple` objects (Advait Dixit, PR [18254](https://github.com/python/mypy/pull/18254)) + * Mark mypyc package with `py.typed` (bzoracler, PR [18253](https://github.com/python/mypy/pull/18253)) + * Fix list index while checking for `Enum` class (Advait Dixit, PR [18426](https://github.com/python/mypy/pull/18426)) + +### Stubgen Improvements + + * Improve dataclass init signatures (Marc Mueller, PR [18430](https://github.com/python/mypy/pull/18430)) + * Preserve `dataclass_transform` decorator (Marc Mueller, PR [18418](https://github.com/python/mypy/pull/18418)) + * Fix `UnpackType` for 3.11+ (Marc Mueller, PR [18421](https://github.com/python/mypy/pull/18421)) + * Improve `self` annotations (Marc Mueller, PR [18420](https://github.com/python/mypy/pull/18420)) + * Print `InspectError` traceback in stubgen `walk_packages` when verbose is specified (Gareth, PR [18224](https://github.com/python/mypy/pull/18224)) + +### Stubtest Improvements + + * Fix crash with numpy array default values (Ali Hamdan, PR [18353](https://github.com/python/mypy/pull/18353)) + * Distinguish metaclass attributes from class attributes (Stephen Morton, PR [18314](https://github.com/python/mypy/pull/18314)) + +### Fixes to Crashes + + * Prevent crash with `Unpack` of a fixed tuple in PEP695 type alias (Stanislav Terliakov, PR [18451](https://github.com/python/mypy/pull/18451)) + * Fix crash with `--cache-fine-grained --cache-dir=/dev/null` (Shantanu, PR [18457](https://github.com/python/mypy/pull/18457)) + * Prevent crashing when `match` arms use name of existing callable (Stanislav Terliakov, PR [18449](https://github.com/python/mypy/pull/18449)) + * Gracefully handle encoding errors when writing to stdout (Brian Schubert, PR [18292](https://github.com/python/mypy/pull/18292)) + * Prevent crash on generic NamedTuple with unresolved typevar bound (Stanislav Terliakov, PR [18585](https://github.com/python/mypy/pull/18585)) + +### Documentation Updates + + * Add inline tabs to documentation (Marc Mueller, PR [18262](https://github.com/python/mypy/pull/18262)) + * Document any `TYPE_CHECKING` name works (Shantanu, PR [18443](https://github.com/python/mypy/pull/18443)) + * Update documentation to not mention 3.8 where possible (sobolevn, PR [18455](https://github.com/python/mypy/pull/18455)) + * Mention `ignore_errors` in exclude documentation (Shantanu, PR [18412](https://github.com/python/mypy/pull/18412)) + * Add `Self` misuse to common issues (Shantanu, PR [18261](https://github.com/python/mypy/pull/18261)) + +### Other Notable Fixes and Improvements + + * Fix literal context for ternary expressions (Ivan Levkivskyi, PR [18545](https://github.com/python/mypy/pull/18545)) + * Ignore `dataclass.__replace__` LSP violations (Marc Mueller, PR [18464](https://github.com/python/mypy/pull/18464)) + * Bind `self` to the class being defined when checking multiple inheritance (Stanislav Terliakov, PR [18465](https://github.com/python/mypy/pull/18465)) + * Fix attribute type resolution with multiple inheritance (Stanislav Terliakov, PR [18415](https://github.com/python/mypy/pull/18415)) + * Improve security of our GitHub Actions (sobolevn, PR [18413](https://github.com/python/mypy/pull/18413)) + * Unwrap `type[Union[...]]` when solving type variable constraints (Stanislav Terliakov, PR [18266](https://github.com/python/mypy/pull/18266)) + * Allow `Any` to match sequence patterns in match/case (Stanislav Terliakov, PR [18448](https://github.com/python/mypy/pull/18448)) + * Fix parent generics mapping when overriding generic attribute with property (Stanislav Terliakov, PR [18441](https://github.com/python/mypy/pull/18441)) + * Add dedicated error code for explicit `Any` (Shantanu, PR [18398](https://github.com/python/mypy/pull/18398)) + * Reject invalid `ParamSpec` locations (Stanislav Terliakov, PR [18278](https://github.com/python/mypy/pull/18278)) + * Stop suggesting stubs that have been removed from typeshed (Shantanu, PR [18373](https://github.com/python/mypy/pull/18373)) + * Allow inverting `--local-partial-types` (Shantanu, PR [18377](https://github.com/python/mypy/pull/18377)) + * Allow to use `Final` and `ClassVar` after Python 3.13 (정승원, PR [18358](https://github.com/python/mypy/pull/18358)) + * Update suggestions to include latest stubs in typeshed (Shantanu, PR [18366](https://github.com/python/mypy/pull/18366)) + * Fix `--install-types` masking failure details (wyattscarpenter, PR [17485](https://github.com/python/mypy/pull/17485)) + * Reject promotions when checking against protocols (Christoph Tyralla, PR [18360](https://github.com/python/mypy/pull/18360)) + * Don't erase type object arguments in diagnostics (Shantanu, PR [18352](https://github.com/python/mypy/pull/18352)) + * Clarify status in `dmypy status` output (Kcornw, PR [18331](https://github.com/python/mypy/pull/18331)) + * Disallow no-argument generic aliases when using PEP 613 explicit aliases (Brian Schubert, PR [18173](https://github.com/python/mypy/pull/18173)) + * Suppress errors for unreachable branches in conditional expressions (Brian Schubert, PR [18295](https://github.com/python/mypy/pull/18295)) + * Do not allow `ClassVar` and `Final` in `TypedDict` and `NamedTuple` (sobolevn, PR [18281](https://github.com/python/mypy/pull/18281)) + * Report error if not enough or too many types provided to `TypeAliasType` (bzoracler, PR [18308](https://github.com/python/mypy/pull/18308)) + * Use more precise context for `TypedDict` plugin errors (Brian Schubert, PR [18293](https://github.com/python/mypy/pull/18293)) + * Use more precise context for invalid type argument errors (Brian Schubert, PR [18290](https://github.com/python/mypy/pull/18290)) + * Do not allow `type[]` to contain `Literal` types (sobolevn, PR [18276](https://github.com/python/mypy/pull/18276)) + * Allow bytearray/bytes comparisons with `--strict-bytes` (Jukka Lehtosalo, PR [18255](https://github.com/python/mypy/pull/18255)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +- Advait Dixit +- Ali Hamdan +- Brian Schubert +- bzoracler +- Cameron Matsui +- Christoph Tyralla +- Gareth +- Ivan Levkivskyi +- Jukka Lehtosalo +- Kcornw +- Marc Mueller +- Mikhail f. Shiryaev +- Shantanu +- sobolevn +- Stanislav Terliakov +- Stephen Morton +- Valentin Stanciu +- Viktor Szépe +- wyattscarpenter +- 정승원 + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + +## 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 button link in documentation (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) + +### Other Notables Fixes and Improvements + + * Allow enum members to have type objects as values (Jukka Lehtosalo, PR [19160](https://github.com/python/mypy/pull/19160)) + * 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 @@ -119,7 +2135,7 @@ This feature was contributed by Jukka Lehtosalo (PR [17404](https://github.com/p ### 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. +Mypyc now supports the new syntax for generics introduced in Python 3.12 (see above). Another notable improvement is significantly 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)) @@ -210,6 +2226,15 @@ Mypyc now supports the new syntax for generics introduced in Python 3.12 (see ab 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: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5d339330a75a..8d7dd2d1e886e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ articulated in the [Python Community Code of Conduct](https://www.python.org/psf #### (1) Fork the mypy repository -Within Github, navigate to and fork the repository. +Within GitHub, navigate to and fork the repository. #### (2) Clone the mypy repository and enter into it @@ -51,7 +51,7 @@ hash -r # This resets shell PATH cache, not necessary on Windows ``` > **Note** -> You'll need Python 3.8 or higher to install all requirements listed in +> You'll need Python 3.9 or higher to install all requirements listed in > test-requirements.txt ### Running tests @@ -65,21 +65,25 @@ 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' +# Run a single test from the test suite (uses pytest substring expression matching) +python runtests.py test_name +# or equivalently: +pytest -n0 -k test_name # Run all test cases in the "test-data/unit/check-dataclasses.test" file +python runtests.py check-dataclasses.test +# or equivalently: pytest mypy/test/testcheck.py::TypeCheckSuite::check-dataclasses.test # Run the formatters and linters @@ -117,7 +121,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/CREDITS b/CREDITS index fb2fe155a9b89..cbe5954c81b29 100644 --- a/CREDITS +++ b/CREDITS @@ -15,7 +15,7 @@ Dropbox core team: Non-Dropbox core team members: - Ethan Smith + Emma Harper Smith Guido van Rossum Jelle Zijlstra Michael J. Sullivan diff --git a/LICENSE b/LICENSE index 55d01ee19ad84..080c5a402dbb0 100644 --- a/LICENSE +++ b/LICENSE @@ -227,3 +227,38 @@ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + += = = = = + +Files under lib-rt/base64 are licensed under the following license. + += = = = = + +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015-2018, Wojciech Muła +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) 2013-2022, Alfred Klomp +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in index c18b83cc0088a..f36c98f4dd3b9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ recursive-include mypy/typeshed *.pyi # mypy and mypyc include mypy/py.typed +include mypyc/py.typed recursive-include mypy *.py recursive-include mypyc *.py @@ -25,8 +26,10 @@ prune docs/source/_build # assorted mypyc requirements graft mypyc/external graft mypyc/lib-rt +graft mypyc/test graft mypyc/test-data graft mypyc/doc +prune mypyc/doc/build # files necessary for testing sdist include mypy-requirements.txt @@ -36,13 +39,13 @@ include test-requirements.txt include mypy_self_check.ini prune misc graft test-data +graft mypy/test include conftest.py 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/README.md b/README.md index 07c170d46cb38..8040566b18eff 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Got a question? We are always happy to answer questions! Here are some good places to ask them: -- for anything you're curious about, try [gitter chat](https://gitter.im/python/typing) - for general questions about Python typing, try [typing discussions](https://github.com/python/typing/discussions) +- for anything you're curious about, try [gitter chat](https://gitter.im/python/typing) If you're just getting started, [the documentation](https://mypy.readthedocs.io/en/stable/index.html) @@ -30,7 +30,6 @@ If you think you've found a bug: - check our [common issues page](https://mypy.readthedocs.io/en/stable/common_issues.html) - search our [issue tracker](https://github.com/python/mypy/issues) to see if it's already been reported -- consider asking on [gitter chat](https://gitter.im/python/typing) To report a bug or request an enhancement: @@ -101,8 +100,6 @@ repo directly: ```bash python3 -m pip install -U git+https://github.com/python/mypy.git -# or if you don't have 'git' installed -python3 -m pip install -U https://github.com/python/mypy/zipball/master ``` Now you can type-check the [statically typed parts] of a program like this: @@ -118,14 +115,16 @@ programs, even if mypy reports type errors: python3 PROGRAM ``` -You can also try mypy in an [online playground](https://mypy-play.net/) (developed by -Yusuke Miyazaki). If you are working with large code bases, you can run mypy in +If you are working with large code bases, you can run mypy in [daemon mode], that will give much faster (often sub-second) incremental updates: ```bash dmypy run -- PROGRAM ``` +You can also try mypy in an [online playground](https://mypy-play.net/) (developed by +Yusuke Miyazaki). + [statically typed parts]: https://mypy.readthedocs.io/en/latest/getting_started.html#function-signatures-and-dynamic-vs-static-typing [daemon mode]: https://mypy.readthedocs.io/en/stable/mypy_daemon.html @@ -134,6 +133,7 @@ Integrations Mypy can be integrated into popular IDEs: +- VS Code: provides [basic integration](https://code.visualstudio.com/docs/python/linting#_mypy) with mypy. - Vim: - Using [Syntastic](https://github.com/vim-syntastic/syntastic): in `~/.vimrc` add `let g:syntastic_python_checkers=['mypy']` @@ -141,11 +141,10 @@ Mypy can be integrated into popular IDEs: or can be explicitly enabled by adding `let b:ale_linters = ['mypy']` in `~/vim/ftplugin/python.vim` - Emacs: using [Flycheck](https://github.com/flycheck/) - Sublime Text: [SublimeLinter-contrib-mypy](https://github.com/fredcallaway/SublimeLinter-contrib-mypy) -- Atom: [linter-mypy](https://atom.io/packages/linter-mypy) -- PyCharm: [mypy plugin](https://github.com/dropbox/mypy-PyCharm-plugin) (PyCharm integrates - [its own implementation](https://www.jetbrains.com/help/pycharm/type-hinting-in-product.html) of [PEP 484](https://peps.python.org/pep-0484/)) -- VS Code: provides [basic integration](https://code.visualstudio.com/docs/python/linting#_mypy) with mypy. -- pre-commit: use [pre-commit mirrors-mypy](https://github.com/pre-commit/mirrors-mypy). +- PyCharm: [mypy plugin](https://github.com/dropbox/mypy-PyCharm-plugin) +- IDLE: [idlemypyextension](https://github.com/CoolCat467/idlemypyextension) +- pre-commit: use [pre-commit mirrors-mypy](https://github.com/pre-commit/mirrors-mypy), although + note by default this will limit mypy's ability to analyse your third party dependencies. Web site and documentation -------------------------- @@ -171,8 +170,6 @@ contributors of all experience levels. To get started with developing mypy, see [CONTRIBUTING.md](CONTRIBUTING.md). -If you need help getting started, don't hesitate to ask on [gitter](https://gitter.im/python/typing). - Mypyc and compiled version of mypy ---------------------------------- @@ -190,4 +187,4 @@ To use a compiled version of a development version of mypy, directly install a binary from . -To contribute to the mypyc project, check out +To contribute to the mypyc project, check out the issue tracker at diff --git a/action.yml b/action.yml index df8715327830f..732929412651b 100644 --- a/action.yml +++ b/action.yml @@ -32,7 +32,7 @@ branding: runs: using: composite steps: - - name: mypy setup + - name: mypy setup # zizmor: ignore[template-injection] shell: bash run: | echo ::group::Installing mypy... diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index dc502d121ffc4..09062a635e63f 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,3 +1,5 @@ -sphinx>=5.1.0 +-r ../mypy-requirements.txt +sphinx>=8.1.0 furo>=2022.3.4 myst-parser>=4.0.0 +sphinx_inline_tabs>=2023.04.21 diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 20fb3821438a5..5efec68555931 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -68,10 +68,11 @@ for full details, see :ref:`running-mypy`. checked. For instance, ``mypy --exclude '/setup.py$' but_still_check/setup.py``. - In particular, ``--exclude`` does not affect mypy's :ref:`import following - `. You can use a per-module :confval:`follow_imports` config - option to additionally avoid mypy from following imports and checking code - you do not wish to be checked. + In particular, ``--exclude`` does not affect mypy's discovery of files + via :ref:`import following `. You can use a per-module + :confval:`ignore_errors` config option to silence errors from a given module, + or a per-module :confval:`follow_imports` config option to additionally avoid + mypy from following imports and checking code you do not wish to be checked. Note that mypy will never recursively discover files and directories named "site-packages", "node_modules" or "__pycache__", or those whose name starts @@ -80,6 +81,10 @@ for full details, see :ref:`running-mypy`. never recursively discover files with extensions other than ``.py`` or ``.pyi``. +.. option:: --exclude-gitignore + + This flag will add everything that matches ``.gitignore`` file(s) to :option:`--exclude`. + Optional arguments ****************** @@ -166,6 +171,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 @@ -356,7 +372,7 @@ definitions or calls. .. option:: --untyped-calls-exclude - This flag allows to selectively disable :option:`--disallow-untyped-calls` + This flag allows one to selectively disable :option:`--disallow-untyped-calls` for functions and methods defined in specific packages, modules, or classes. Note that each exclude entry acts as a prefix. For example (assuming there are no type annotations for ``third_party_lib`` available): @@ -537,6 +553,32 @@ 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``. + +.. option:: --deprecated-calls-exclude + + This flag allows one to selectively disable :ref:`deprecated` warnings + for functions and methods defined in specific packages, modules, or classes. + Note that each exclude entry acts as a prefix. For example (assuming ``foo.A.func`` is deprecated): + + .. code-block:: python + + # mypy --enable-error-code deprecated + # --deprecated-calls-exclude=foo.A + import foo + + foo.A().func() # OK, the deprecated warning is ignored + + # file foo.py + from typing_extensions import deprecated + class A: + @deprecated("Use A.func2 instead") + def func(self): pass .. _miscellaneous-strictness-flags: @@ -551,12 +593,58 @@ of the above sections. This flag causes mypy to suppress errors caused by not being able to fully infer the types of global and class variables. -.. option:: --allow-redefinition +.. option:: --allow-redefinition-new By default, mypy won't allow a variable to be redefined with an - unrelated type. This flag enables redefinition of a variable with an + unrelated type. This *experimental* flag enables the redefinition of + unannotated variables with an arbitrary type. You will also need to enable + :option:`--local-partial-types `. + Example: + + .. code-block:: python + + def maybe_convert(n: int, b: bool) -> int | str: + if b: + x = str(n) # Assign "str" + else: + x = n # Assign "int" + # Type of "x" is "int | str" here. + return x + + Without the new flag, mypy only supports inferring optional types + (``X | None``) from multiple assignments. With this option enabled, + mypy can infer arbitrary union types. + + This also enables an unannotated variable to have different types in different + code locations: + + .. code-block:: python + + if check(): + for x in range(n): + # Type of "x" is "int" here. + ... + else: + for x in ['a', 'b']: + # Type of "x" is "str" here. + ... + + Note: We are planning to turn this flag on by default in a future mypy + release, along with :option:`--local-partial-types `. + The feature is still experimental, and the semantics may still change. + +.. option:: --allow-redefinition + + This is an older variant of + :option:`--allow-redefinition-new `. + This flag enables redefinition of a variable with an arbitrary type *in some contexts*: only redefinitions within the same block and nesting depth as the original definition are allowed. + + We have no plans to remove this flag, but we expect that + :option:`--allow-redefinition-new ` + will replace this flag for new use cases eventually. + Example where this can be useful: .. code-block:: python @@ -640,14 +728,55 @@ of the above sections. if text != b'other bytes': # Error: non-overlapping equality check! ... - assert text is not None # OK, check against None is allowed as a special case. + assert text is not None # OK, check against None is allowed + + +.. option:: --strict-equality-for-none + + This flag extends :option:`--strict-equality ` for checks + against ``None``: + + .. code-block:: python + + text: str + assert text is not None # Error: non-overlapping identity check! + + Note that :option:`--strict-equality-for-none ` + only works in combination with :option:`--strict-equality `. + +.. option:: --strict-bytes + + By default, mypy treats ``bytearray`` and ``memoryview`` as subtypes of ``bytes`` which + is not true at runtime. Use this flag to disable this behavior. ``--strict-bytes`` will + be enabled by default in *mypy 2.0*. + + .. code-block:: python + + def f(buf: bytes) -> None: + assert isinstance(buf, bytes) # Raises runtime AssertionError with bytearray/memoryview + with open("binary_file", "wb") as fp: + fp.write(buf) + + f(bytearray(b"")) # error: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" + f(memoryview(b"")) # error: Argument 1 to "f" has incompatible type "memoryview"; expected "bytes" + + # If `f` accepts any object that implements the buffer protocol, consider using: + from collections.abc import Buffer # "from typing_extensions" in Python 3.11 and earlier + + def f(buf: Buffer) -> None: + with open("binary_file", "wb") as fp: + fp.write(buf) + + f(b"") # Ok + f(bytearray(b"")) # Ok + f(memoryview(b"")) # Ok + .. option:: --extra-checks This flag enables additional checks that are technically correct but may be - impractical in real code. In particular, it prohibits partial overlap in - ``TypedDict`` updates, and makes arguments prepended via ``Concatenate`` - positional-only. For example: + impractical. In particular, it prohibits partial overlap in ``TypedDict`` updates, + and makes arguments prepended via ``Concatenate`` positional-only. For example: .. code-block:: python @@ -670,14 +799,40 @@ of the above sections. bad: Bad = {"a": 0, "b": "no"} test(bad, bar) + In future more checks may be added to this flag if: + + * The corresponding use cases are rare, thus not justifying a dedicated + strictness flag. + + * The new check cannot be supported as an opt-in error code. + .. option:: --strict - This flag mode enables all optional error checking flags. You can see the - list of flags enabled by strict mode in the full :option:`mypy --help` output. + This flag mode enables a defined subset of optional error-checking flags. + This subset primarily includes checks for inadvertent type unsoundness (i.e + strict will catch type errors as long as intentional methods like type ignore + or casting were not used.) + + Note: the :option:`--warn-unreachable` flag + is not automatically enabled by the strict flag. + + The strict flag does not take precedence over other strict-related flags. + Directly specifying a flag of alternate behavior will override the + behavior of strict, regardless of the order in which they are passed. + You can see the list of flags enabled by strict mode in the full + :option:`mypy --help` output. Note: the exact list of flags enabled by running :option:`--strict` may change over time. + .. include:: strict_list.rst + .. + The above file is autogenerated and included during html generation. + (That's an include directive, and this is a comment.) + It would be fine to generate it at some other time instead, + theoretically, but we already had a convenient hook during html gen. + + .. option:: --disable-error-code This flag allows disabling one or multiple error codes globally. @@ -711,6 +866,7 @@ of the above sections. x = 'a string' x.trim() # error: "str" has no attribute "trim" [attr-defined] + .. _configuring-error-messages: Configuring error messages @@ -802,11 +958,6 @@ in error messages. useful or they may be overly noisy. If ``N`` is negative, there is no limit. The default limit is -1. -.. option:: --force-uppercase-builtins - - Always use ``List`` instead of ``list`` in error messages, - even on Python 3.9+. - .. option:: --force-union-syntax Always use ``Union[]`` and ``Optional[]`` for union types @@ -1008,7 +1159,7 @@ format into the specified directory. Enabling incomplete/experimental features ***************************************** -.. option:: --enable-incomplete-feature {PreciseTupleTypes, InlineTypedDict} +.. option:: --enable-incomplete-feature {PreciseTupleTypes,InlineTypedDict,TypeForm} Some features may require several mypy releases to implement, for example due to their complexity, potential for backwards incompatibility, or @@ -1060,8 +1211,11 @@ List of currently incomplete/experimental features: .. code-block:: python - def test_values() -> {"int": int, "str": str}: - return {"int": 42, "str": "test"} + def test_values() -> {"width": int, "description": str}: + return {"width": 42, "description": "test"} + +* ``TypeForm``: this feature enables ``TypeForm``, as described in + `PEP 747 – Annotating Type Forms _`. Miscellaneous @@ -1104,12 +1258,18 @@ Miscellaneous stub packages were found, they are installed and then another run is performed. -.. option:: --junit-xml JUNIT_XML +.. option:: --junit-xml JUNIT_XML_OUTPUT_FILE Causes mypy to generate a JUnit XML test result document with type checking results. This can make it easier to integrate mypy with continuous integration (CI) tools. +.. option:: --junit-format {global,per_file} + + If --junit-xml is set, specifies format. + global (default): single test with all errors; + per_file: one test entry per file with failures. + .. option:: --find-occurrences CLASS.MEMBER This flag will make mypy print out all usages of a class member diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 39954b8e332a0..e4239bd7a8eed 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -218,6 +218,14 @@ daemon `, which can speed up incremental mypy runtimes by a factor of 10 or more. :ref:`Remote caching ` can make cold mypy runs several times faster. +Furthermore: as of `mypy 1.13 `_, +mypy 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. + Types of empty collections -------------------------- @@ -427,8 +435,8 @@ More specifically, mypy will understand the use of :py:data:`sys.version_info` a import sys # Distinguishing between different versions of Python: - if sys.version_info >= (3, 8): - # Python 3.8+ specific definitions and imports + if sys.version_info >= (3, 13): + # Python 3.13+ specific definitions and imports else: # Other definitions and imports @@ -455,7 +463,7 @@ Example: # The rest of this file doesn't apply to Windows. Some other expressions exhibit similar behavior; in particular, -:py:data:`~typing.TYPE_CHECKING`, variables named ``MYPY``, and any variable +:py:data:`~typing.TYPE_CHECKING`, variables named ``MYPY`` or ``TYPE_CHECKING``, and any variable whose name is passed to :option:`--always-true ` or :option:`--always-false `. (However, ``True`` and ``False`` are not treated specially!) @@ -505,11 +513,15 @@ to see the types of all local variables at once. Example: # b: builtins.str .. note:: - ``reveal_type`` and ``reveal_locals`` are only understood by mypy and - don't exist in Python. If you try to run your program, you'll have to - remove any ``reveal_type`` and ``reveal_locals`` calls before you can - run your code. Both are always available and you don't need to import - them. + ``reveal_type`` and ``reveal_locals`` are handled specially by mypy during + type checking, and don't have to be defined or imported. + + However, if you want to run your code, + you'll have to remove any ``reveal_type`` and ``reveal_locals`` + calls from your program or else Python will give you an error at runtime. + + Alternatively, you can import ``reveal_type`` from ``typing_extensions`` + or ``typing`` (on Python 3.11 and newer) .. _silencing-linters: @@ -719,7 +731,7 @@ This example demonstrates both safe and unsafe overrides: class NarrowerReturn(A): # A more specific return type is fine - def test(self, t: Sequence[int]) -> List[str]: # OK + def test(self, t: Sequence[int]) -> list[str]: # OK ... class GeneralizedReturn(A): @@ -734,7 +746,7 @@ not necessary: .. code-block:: python class NarrowerArgument(A): - def test(self, t: List[int]) -> Sequence[str]: # type: ignore[override] + def test(self, t: list[int]) -> Sequence[str]: # type: ignore[override] ... .. _unreachable: @@ -757,7 +769,7 @@ type check such code. Consider this example: x: int = 'abc' # Unreachable -- no error It's easy to see that any statement after ``return`` is unreachable, -and hence mypy will not complain about the mis-typed code below +and hence mypy will not complain about the mistyped code below it. For a more subtle example, consider this code: .. code-block:: python @@ -819,3 +831,30 @@ This is best understood via an example: To get this code to type check, you could assign ``y = x`` after ``x`` has been narrowed, and use ``y`` in the inner function, or add an assert in the inner function. + +.. _incorrect-self: + +Incorrect use of ``Self`` +------------------------- + +``Self`` is not the type of the current class; it's a type variable with upper +bound of the current class. That is, it represents the type of the current class +or of potential subclasses. + +.. code-block:: python + + from typing import Self + + class Foo: + @classmethod + def constructor(cls) -> Self: + # Instead, either call cls() or change the annotation to -> Foo + return Foo() # error: Incompatible return value type (got "Foo", expected "Self") + + class Bar(Foo): + ... + + reveal_type(Foo.constructor()) # note: Revealed type is "Foo" + # In the context of the subclass Bar, the Self return type promises + # that the return value will be Bar + reveal_type(Bar.constructor()) # note: Revealed type is "Bar" diff --git a/docs/source/conf.py b/docs/source/conf.py index f8faa03a09b27..02caa44dce117 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,7 +35,12 @@ # 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", "myst_parser"] +extensions = [ + "sphinx.ext.intersphinx", + "sphinx_inline_tabs", + "docs.source.html_builder", + "myst_parser", +] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -51,7 +56,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 @@ -107,6 +112,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. @@ -267,9 +278,9 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "attrs": ("https://www.attrs.org/en/stable/", None), - "cython": ("https://docs.cython.org/en/latest", None), + "cython": ("https://cython.readthedocs.io/en/stable", None), "monkeytype": ("https://monkeytype.readthedocs.io/en/latest", None), - "setuptools": ("https://setuptools.readthedocs.io/en/latest", None), + "setuptools": ("https://setuptools.pypa.io/en/latest", None), } diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index ded8476b60e35..7abd1f02db68b 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -7,22 +7,30 @@ Mypy is very configurable. This is most useful when introducing typing to an existing codebase. See :ref:`existing-code` for concrete advice for that situation. -Mypy supports reading configuration settings from a file with the following precedence order: +Mypy supports reading configuration settings from a file. By default, mypy will +discover configuration files by walking up the file system (up until the root of +a repository or the root of the filesystem). In each directory, it will look for +the following configuration files (in this order): - 1. ``./mypy.ini`` - 2. ``./.mypy.ini`` - 3. ``./pyproject.toml`` - 4. ``./setup.cfg`` - 5. ``$XDG_CONFIG_HOME/mypy/config`` - 6. ``~/.config/mypy/config`` - 7. ``~/.mypy.ini`` + 1. ``mypy.ini`` + 2. ``.mypy.ini`` + 3. ``pyproject.toml`` (containing a ``[tool.mypy]`` section) + 4. ``setup.cfg`` (containing a ``[mypy]`` section) + +If no configuration file is found by this method, mypy will then look for +configuration files in the following locations (in this order): + + 1. ``$XDG_CONFIG_HOME/mypy/config`` + 2. ``~/.config/mypy/config`` + 3. ``~/.mypy.ini`` + +The :option:`--config-file ` command-line flag has the +highest precedence and must point towards a valid configuration file; +otherwise mypy will report an error and exit. Without the command line option, +mypy will look for configuration files in the precedence order above. It is important to understand that there is no merging of configuration -files, as it would lead to ambiguity. The :option:`--config-file ` -command-line flag has the highest precedence and -must be correct; otherwise mypy will report an error and exit. Without the -command line option, mypy will look for configuration files in the -precedence order above. +files, as it would lead to ambiguity. Most flags correspond closely to :ref:`command-line flags ` but there are some differences in flag names and some @@ -280,6 +288,14 @@ section of the command line docs. See :ref:`using-a-pyproject-toml`. +.. confval:: exclude_gitignore + + :type: boolean + :default: False + + This flag will add everything that matches ``.gitignore`` file(s) to :confval:`exclude`. + This option may only be set in the global section (``[mypy]``). + .. confval:: namespace_packages :type: boolean @@ -315,6 +331,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 @@ -398,7 +432,7 @@ Platform configuration Specifies the Python version used to parse and check the target program. The string should be in the format ``MAJOR.MINOR`` -- - for example ``2.7``. The default is the version of the Python + for example ``3.9``. The default is the version of the Python interpreter used to run mypy. This option may only be set in the global section (``[mypy]``). @@ -640,6 +674,16 @@ section of the command line docs. Shows a warning when encountering any code inferred to be unreachable or redundant after performing type analysis. +.. confval:: deprecated_calls_exclude + + :type: comma-separated list of strings + + Selectively excludes functions and methods defined in specific packages, + modules, and classes from the :ref:`deprecated` error code. + This also applies to all submodules of packages (i.e. everything inside + a given prefix). Note, this option does not support per-file configuration, + the exclusions list is defined globally for all your code. + Suppressing errors ****************** @@ -669,6 +713,44 @@ section of the command line docs. Causes mypy to suppress errors caused by not being able to fully infer the types of global and class variables. +.. confval:: allow_redefinition_new + + :type: boolean + :default: False + + By default, mypy won't allow a variable to be redefined with an + unrelated type. This *experimental* flag enables the redefinition of + unannotated variables with an arbitrary type. You will also need to enable + :confval:`local_partial_types`. + Example: + + .. code-block:: python + + def maybe_convert(n: int, b: bool) -> int | str: + if b: + x = str(n) # Assign "str" + else: + x = n # Assign "int" + # Type of "x" is "int | str" here. + return x + + This also enables an unannotated variable to have different types in different + code locations: + + .. code-block:: python + + if check(): + for x in range(n): + # Type of "x" is "int" here. + ... + else: + for x in ['a', 'b']: + # Type of "x" is "str" here. + ... + + Note: We are planning to turn this flag on by default in a future mypy + release, along with :confval:`local_partial_types`. + .. confval:: allow_redefinition :type: boolean @@ -702,6 +784,7 @@ section of the command line docs. Disallows inferring variable type for ``None`` from two assignments in different scopes. This is always implicitly enabled when using the :ref:`mypy daemon `. + This will be enabled by default in a future mypy release. .. confval:: disable_error_code @@ -717,6 +800,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. + See :option:`mypy --extra-checks` for more info. + .. confval:: implicit_reexport :type: boolean @@ -737,25 +828,34 @@ section of the command line docs. from foo import bar __all__ = ['bar'] -.. confval:: strict_concatenate +.. confval:: strict_equality - :type: boolean - :default: False + :type: boolean + :default: False - Make arguments prepended via ``Concatenate`` be truly positional-only. + Prohibit equality checks, identity checks, and container checks between + non-overlapping types (except ``None``). -.. confval:: strict_equality +.. confval:: strict_equality_for_none - :type: boolean - :default: False + :type: boolean + :default: False - Prohibit equality checks, identity checks, and container checks between - non-overlapping types. + Include ``None`` in strict equality checks (requires :confval:`strict_equality` + to be activated). + +.. confval:: strict_bytes + + :type: boolean + :default: False + + Disable treating ``bytearray`` and ``memoryview`` as subtypes of ``bytes``. + This will be enabled by default in *mypy 2.0*. .. 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` @@ -830,14 +930,6 @@ These options may only be set in the global section (``[mypy]``). Show absolute paths to files. -.. confval:: force_uppercase_builtins - - :type: boolean - :default: False - - Always use ``List`` instead of ``list`` in error messages, - even on Python 3.9+. - .. confval:: force_union_syntax :type: boolean @@ -1061,6 +1153,15 @@ These options may only be set in the global section (``[mypy]``). type checking results. This can make it easier to integrate mypy with continuous integration (CI) tools. +.. confval:: junit_format + + :type: string + :default: ``global`` + + If junit_xml is set, specifies format. + global (default): single test with all errors; + per_file: one test entry per file with failures. + .. confval:: scripts_are_modules :type: boolean @@ -1143,7 +1244,7 @@ of your repo (or append it to the end of an existing ``pyproject.toml`` file) an # mypy global options: [tool.mypy] - python_version = "2.7" + python_version = "3.9" warn_return_any = true warn_unused_configs = true exclude = [ diff --git a/docs/source/duck_type_compatibility.rst b/docs/source/duck_type_compatibility.rst index e801f9251db5e..7f4b67503ebef 100644 --- a/docs/source/duck_type_compatibility.rst +++ b/docs/source/duck_type_compatibility.rst @@ -9,6 +9,8 @@ supported for a small set of built-in types: * ``int`` is duck type compatible with ``float`` and ``complex``. * ``float`` is duck type compatible with ``complex``. * ``bytearray`` and ``memoryview`` are duck type compatible with ``bytes``. + (this will be disabled by default in **mypy 2.0**, and currently can be + disabled with :option:`--strict-bytes `.) For example, mypy considers an ``int`` object to be valid whenever a ``float`` object is expected. Thus code like this is nice and clean diff --git a/docs/source/dynamic_typing.rst b/docs/source/dynamic_typing.rst index 1c31a535bdc1d..da40142d377d4 100644 --- a/docs/source/dynamic_typing.rst +++ b/docs/source/dynamic_typing.rst @@ -1,6 +1,5 @@ .. _dynamic-typing: - Dynamically typed code ====================== @@ -86,7 +85,7 @@ treated as ``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 @@ -94,6 +93,8 @@ third party libraries that mypy does not know about. This is particularly the ca when using the :option:`--ignore-missing-imports ` flag. See :ref:`fix-missing-imports` for more information about this. +.. _any-vs-object: + Any vs. object -------------- diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 73171131bc8d9..d4e2c83323ac6 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -215,6 +215,35 @@ You can use :py:class:`~collections.abc.Callable` as the type for callable objec for x in objs: f(x) +.. _code-metaclass: + +Check the validity of a class's metaclass [metaclass] +----------------------------------------------------- + +Mypy checks whether the metaclass of a class is valid. The metaclass +must be a subclass of ``type``. Further, the class hierarchy must yield +a consistent metaclass. For more details, see the +`Python documentation `_ + +Note that mypy's metaclass checking is limited and may produce false-positives. +See also :ref:`limitations`. + +Example with an error: + +.. code-block:: python + + class GoodMeta(type): + pass + + class BadMeta: + pass + + class A1(metaclass=GoodMeta): # OK + pass + + class A2(metaclass=BadMeta): # Error: Metaclasses not inheriting from "type" are not supported [metaclass] + pass + .. _code-var-annotated: Require annotation if variable type is unclear [var-annotated] @@ -1003,8 +1032,8 @@ Warn about top level await expressions [top-level-await] This error code is separate from the general ``[syntax]`` errors, because in some environments (e.g. IPython) a top level ``await`` is allowed. In such environments a user may want to use ``--disable-error-code=top-level-await``, -that allows to still have errors for other improper uses of ``await``, for -example: +which allows one to still have errors for other improper uses of ``await``, +for example: .. code-block:: python @@ -1241,6 +1270,81 @@ Consider this example: `PEP 705 `_ specifies how ``ReadOnly`` special form works for ``TypedDict`` objects. +.. _code-narrowed-type-not-subtype: + +Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] +--------------------------------------------------------------- + +:pep:`742` requires that when ``TypeIs`` is used, the narrowed +type must be a subtype of the original type:: + + from typing_extensions import TypeIs + + def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int + ... + + def g(x: object) -> TypeIs[str]: # OK + ... + +.. _code-maybe-unrecognized-str-typeform: + +String appears in a context which expects a TypeForm [maybe-unrecognized-str-typeform] +-------------------------------------------------------------------------------------- + +TypeForm literals may contain string annotations: + +.. code-block:: python + + typx1: TypeForm = str | None + typx2: TypeForm = 'str | None' # OK + typx3: TypeForm = 'str' | None # OK + +However TypeForm literals containing a string annotation can only be recognized +by mypy in the following locations: + +.. code-block:: python + + typx_var: TypeForm = 'str | None' # assignment r-value + + def func(typx_param: TypeForm) -> TypeForm: + return 'str | None' # returned expression + + func('str | None') # callable's argument + +If you try to use a string annotation in some other location +which expects a TypeForm, the string value will always be treated as a ``str`` +even if a ``TypeForm`` would be more appropriate and this error code +will be generated: + +.. code-block:: python + + # Error: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. [maybe-unrecognized-str-typeform] + # Error: List item 0 has incompatible type "str"; expected "TypeForm[Any]" [list-item] + list_of_typx: list[TypeForm] = ['str | None', float] + +Fix the error by surrounding the entire type with ``TypeForm(...)``: + +.. code-block:: python + + list_of_typx: list[TypeForm] = [TypeForm('str | None'), float] # OK + +Similarly, if you try to use a string literal in a location which expects a +TypeForm, this error code will be generated: + +.. code-block:: python + + dict_of_typx = {'str_or_none': TypeForm(str | None)} + # Error: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. [maybe-unrecognized-str-typeform] + list_of_typx: list[TypeForm] = [dict_of_typx['str_or_none']] + +Fix the error by adding ``# type: ignore[maybe-unrecognized-str-typeform]`` +to the line with the string literal: + +.. code-block:: python + + dict_of_typx = {'str_or_none': TypeForm(str | None)} + list_of_typx: list[TypeForm] = [dict_of_typx['str_or_none']] # type: ignore[maybe-unrecognized-str-typeform] + .. _code-misc: Miscellaneous checks [misc] diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 6d50e217a77de..bd24360619748 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -145,6 +145,29 @@ literal: def is_magic(x: bytes) -> bool: return x == b'magic' # OK +:option:`--strict-equality ` does not include comparisons with +``None``: + +.. code-block:: python + + # mypy: strict-equality + + def is_none(x: str) -> bool: + return x is None # OK + +If you want such checks, you must also activate +:option:`--strict-equality-for-none ` (we might merge +these two options later). + +.. code-block:: python + + # mypy: strict-equality strict-equality-for-none + + def is_none(x: str) -> bool: + # Error: Non-overlapping identity check + # (left operand type: "str", right operand type: "None") + return x is None + .. _code-no-untyped-call: Check that no untyped functions are called [no-untyped-call] @@ -231,6 +254,46 @@ 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. +Use :option:`--deprecated-calls-exclude ` to hide warnings +for specific functions, classes and packages. + +.. 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] @@ -556,18 +619,83 @@ Correct usage: When this code is enabled, using ``reveal_locals`` is always an error, because there's no way one can import it. -.. _code-narrowed-type-not-subtype: -Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] ---------------------------------------------------------------- +.. _code-explicit-any: -:pep:`742` requires that when ``TypeIs`` is used, the narrowed -type must be a subtype of the original type:: +Check that explicit Any type annotations are not allowed [explicit-any] +----------------------------------------------------------------------- - from typing_extensions import TypeIs +If you use :option:`--disallow-any-explicit `, mypy generates an error +if you use an explicit ``Any`` type annotation. - def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int - ... +Example: - def g(x: object) -> TypeIs[str]: # OK - ... +.. code-block:: python + + # mypy: disallow-any-explicit + from typing import Any + x: Any = 1 # Error: Explicit "Any" type annotation [explicit-any] + + +.. _code-exhaustive-match: + +Check that match statements match exhaustively [exhaustive-match] +----------------------------------------------------------------------- + +If enabled with :option:`--enable-error-code exhaustive-match `, +mypy generates an error if a match statement does not match all possible cases/types. + + +Example: + +.. code-block:: python + + import enum + + + class Color(enum.Enum): + RED = 1 + BLUE = 2 + + val: Color = Color.RED + + # OK without --enable-error-code exhaustive-match + match val: + case Color.RED: + print("red") + + # With --enable-error-code exhaustive-match + # Error: Match statement has unhandled case for values of type "Literal[Color.BLUE]" + match val: + case Color.RED: + print("red") + + # OK with or without --enable-error-code exhaustive-match, since all cases are handled + match val: + case Color.RED: + print("red") + case _: + print("other") + +.. _code-untyped-decorator: + +Error if an untyped decorator makes a typed function effectively untyped [untyped-decorator] +-------------------------------------------------------------------------------------------- + +If enabled with :option:`--disallow-untyped-decorators ` +mypy generates an error if a typed function is wrapped by an untyped decorator +(as this would effectively remove the benefits of typing the function). + +Example: + +.. code-block:: python + + def printing_decorator(func): + def wrapper(*args, **kwds): + print("Calling", func) + return func(*args, **kwds) + return wrapper + # A decorated function. + @printing_decorator # E: Untyped decorator makes function "add_forty_two" untyped [untyped-decorator] + def add_forty_two(value: int) -> int: + return value + 42 diff --git a/docs/source/existing_code.rst b/docs/source/existing_code.rst index 0a5ac2bfa8f64..dfdc7ef19e163 100644 --- a/docs/source/existing_code.rst +++ b/docs/source/existing_code.rst @@ -199,9 +199,8 @@ The following config is equivalent to ``--strict`` (as of mypy 1.0): warn_redundant_casts = True warn_unused_ignores = True - # Getting these passing should be easy + # Getting this passing should be easy strict_equality = True - strict_concatenate = True # Strongly recommend enabling this one as soon as you can check_untyped_defs = True @@ -223,6 +222,10 @@ The following config is equivalent to ``--strict`` (as of mypy 1.0): # This one can be tricky to get passing if you use a lot of untyped libraries warn_return_any = True + # This one is a catch-all flag for the rest of strict checks that are technically + # correct but may not be practical + extra_checks = True + Note that you can also start with ``--strict`` and subtract, for instance: .. code-block:: text 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/generics.rst b/docs/source/generics.rst index 9c0a308ee39ab..bdd6e333f8959 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -93,7 +93,7 @@ Using ``Stack`` is similar to built-in container types: stack.push('x') stack2: Stack[str] = Stack() - stack2.append('x') + stack2.push('x') Construction of instances of generic types is type checked (Python 3.12 syntax): @@ -146,7 +146,7 @@ example (Python 3.12 syntax): from typing import Mapping, Iterator # This is a generic subclass of Mapping - class MyMapp[KT, VT](Mapping[KT, VT]): + class MyMap[KT, VT](Mapping[KT, VT]): def __getitem__(self, k: KT) -> VT: ... def __iter__(self) -> Iterator[KT]: ... def __len__(self) -> int: ... @@ -284,7 +284,7 @@ and the return type is derived from the sequence item type. Example: 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: +functions to declare type parameters: .. code-block:: python @@ -630,7 +630,7 @@ Let us illustrate this by few simple examples: my_circles: list[Circle] = [] 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 + my_circles[0].rotate() # ...this will fail, since my_circles[0] is now a Shape, not a Circle Another example of invariant type is ``dict``. Most mutable containers are invariant. @@ -641,7 +641,7 @@ infer the most flexible variance for each class type variable. Here .. code-block:: python - class Box[T]: # this type is implilicitly covariant + class Box[T]: # this type is implicitly covariant def __init__(self, content: T) -> None: self._content = content @@ -663,12 +663,12 @@ the attribute as ``Final``, the class could still be made covariant: from typing import Final - class Box[T]: # this type is implilicitly covariant + 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 + return self.content 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 @@ -999,7 +999,7 @@ similarly supported via generics (Python 3.12 syntax): .. code-block:: python - from colletions.abc import Callable + from collections.abc import Callable from typing import Any def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: @@ -1165,7 +1165,7 @@ This example correctly uses a covariant type variable: See :ref:`variance-of-generics` for more about variance. -Generic protocols can also be recursive. Example (Python 3.12 synta): +Generic protocols can also be recursive. Example (Python 3.12 syntax): .. code-block:: python diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 28a4481e502eb..9b510314fd8fe 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -16,7 +16,7 @@ may not make much sense otherwise. Installing and running mypy *************************** -Mypy requires Python 3.8 or later to run. You can install mypy using pip: +Mypy requires Python 3.9 or later to run. You can install mypy using pip: .. code-block:: shell diff --git a/docs/source/html_builder.py b/docs/source/html_builder.py index ea3594e0617b6..387f7f13b4c29 100644 --- a/docs/source/html_builder.py +++ b/docs/source/html_builder.py @@ -11,16 +11,37 @@ from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment import BuildEnvironment +from mypy.main import define_options + class MypyHTMLBuilder(StandaloneHTMLBuilder): + strict_file: Path + def __init__(self, app: Sphinx, env: BuildEnvironment) -> None: super().__init__(app, env) self._ref_to_doc = {} + self.strict_file = Path(self.srcdir) / "strict_list.rst" + self._add_strict_list() def write_doc(self, docname: str, doctree: document) -> None: super().write_doc(docname, doctree) self._ref_to_doc.update({_id: docname for _id in doctree.ids}) + def _add_strict_list(self) -> None: + strict_flags: list[str] + _, strict_flags, _ = define_options() + strict_part = ", ".join(f":option:`{s} `" for s in strict_flags) + if ( + not strict_part + or strict_part.isspace() + or len(strict_part) < 20 + or len(strict_part) > 2000 + ): + raise ValueError(f"{strict_part=}, which doesn't look right (by a simple heuristic).") + self.strict_file.write_text( + "For this version of mypy, the list of flags enabled by strict is: " + strict_part + ) + def _verify_error_codes(self) -> None: from mypy.errorcodes import error_codes @@ -55,6 +76,7 @@ def _write_ref_redirector(self) -> None: def finish(self) -> None: super().finish() self._write_ref_redirector() + self.strict_file.unlink() def setup(app: Sphinx) -> dict[str, Any]: diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index 54693cddf953b..23ebc14e8670e 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -41,6 +41,11 @@ operations are permitted on the value, and the operations are only checked at runtime. You can use ``Any`` as an "escape hatch" when you can't use a more precise type for some reason. +This should not be confused with the +:py:class:`object` type, which represents the set of all values. +Unlike ``object``, ``Any`` introduces type unsafety — see +:ref:`any-vs-object` for more. + ``Any`` is compatible with every other type, and vice versa. You can freely assign a value of type ``Any`` to a variable with a more precise type: @@ -289,6 +294,7 @@ isn't supported by the runtime with some limitations, if you use def f(x: int | str) -> None: # OK on Python 3.7 and later ... +.. _no-strict-optional: .. _strict_optional: Optional types and the None type diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst index 877ab5de90877..e449589ddb4d2 100644 --- a/docs/source/literal_types.rst +++ b/docs/source/literal_types.rst @@ -468,6 +468,10 @@ If we forget to handle one of the cases, mypy will generate an error: assert_never(direction) # E: Argument 1 to "assert_never" has incompatible type "Direction"; expected "NoReturn" Exhaustiveness checking is also supported for match statements (Python 3.10 and later). +For match statements specifically, inexhaustive matches can be caught +without needing to use ``assert_never`` by using +:option:`--enable-error-code exhaustive-match `. + Extra Enum checks ***************** diff --git a/docs/source/metaclasses.rst b/docs/source/metaclasses.rst index a3ee25f160546..e30dfe80f9f95 100644 --- a/docs/source/metaclasses.rst +++ b/docs/source/metaclasses.rst @@ -34,12 +34,14 @@ Mypy supports the lookup of attributes in the metaclass: .. code-block:: python - from typing import ClassVar, Self + from typing import ClassVar, TypeVar + + S = TypeVar("S") class M(type): count: ClassVar[int] = 0 - def make(cls) -> Self: + def make(cls: type[S]) -> S: M.count += 1 return cls() @@ -55,9 +57,6 @@ 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 @@ -88,3 +87,31 @@ so it's better not to combine metaclasses and class hierarchies: such as ``class A(metaclass=f()): ...`` * Mypy does not and cannot understand arbitrary metaclass code. * Mypy only recognizes subclasses of :py:class:`type` as potential metaclasses. +* ``Self`` is not allowed as annotation in metaclasses as per `PEP 673`_. + +.. _PEP 673: https://peps.python.org/pep-0673/#valid-locations-for-self + +For some builtin types, mypy may think their metaclass is :py:class:`abc.ABCMeta` +even if it is :py:class:`type` at runtime. In those cases, you can either: + +* use :py:class:`abc.ABCMeta` instead of :py:class:`type` as the + superclass of your metaclass if that works in your use-case +* mute the error with ``# type: ignore[metaclass]`` + +.. code-block:: python + + import abc + + assert type(tuple) is type # metaclass of tuple is type at runtime + + # The problem: + class M0(type): pass + class A0(tuple, metaclass=M0): pass # Mypy Error: metaclass conflict + + # Option 1: use ABCMeta instead of type + class M1(abc.ABCMeta): pass + class A1(tuple, metaclass=M1): pass + + # Option 2: mute the error + class M2(type): pass + class A2(tuple, metaclass=M2): pass # type: ignore[metaclass] diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index cbf40d5dcaa51..0383c3448d068 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -390,7 +390,7 @@ program: The ``summarize([])`` call matches both variants: an empty list could be either a ``list[int]`` or a ``list[str]``. In this case, mypy will break the tie by picking the first matching variant: ``output`` -will have an inferred type of ``float``. The implementor is responsible +will have an inferred type of ``float``. The implementer is responsible for making sure ``summarize`` breaks ties in the same way at runtime. However, there are two exceptions to the "pick the first match" rule. diff --git a/docs/source/mypy_daemon.rst b/docs/source/mypy_daemon.rst index 6c511e14eb953..e0fc8129a0b82 100644 --- a/docs/source/mypy_daemon.rst +++ b/docs/source/mypy_daemon.rst @@ -252,16 +252,16 @@ command. Statically inspect expressions ****************************** -The daemon allows to get declared or inferred type of an expression (or other +The daemon allows one to get the declared or inferred type of an expression (or other information about an expression, such as known attributes or definition location) -using ``dmypy inspect LOCATION`` command. The location of the expression should be +using the ``dmypy inspect LOCATION`` command. The location of the expression should be specified in the format ``path/to/file.py:line:column[:end_line:end_column]``. Both line and column are 1-based. Both start and end position are inclusive. These rules match how mypy prints the error location in error messages. If a span is given (i.e. all 4 numbers), then only an exactly matching expression is inspected. If only a position is given (i.e. 2 numbers, line and column), mypy -will inspect all *expressions*, that include this position, starting from the +will inspect all expressions that include this position, starting from the innermost one. Consider this Python code snippet: diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index ed8d94f62ef15..258cd4b0de564 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -352,6 +352,53 @@ the parameters are positional-only. Example (using the legacy syntax for generic copy_a = copy_b # OK copy_b = copy_a # Also OK +Binding of types in protocol attributes +*************************************** + +All protocol attributes annotations are treated as externally visible types +of those attributes. This means that for example callables are not bound, +and descriptors are not invoked: + +.. code-block:: python + + from typing import Callable, Protocol, overload + + class Integer: + @overload + def __get__(self, instance: None, owner: object) -> Integer: ... + @overload + def __get__(self, instance: object, owner: object) -> int: ... + # + + class Example(Protocol): + foo: Callable[[object], int] + bar: Integer + + ex: Example + reveal_type(ex.foo) # Revealed type is Callable[[object], int] + reveal_type(ex.bar) # Revealed type is Integer + +In other words, protocol attribute types are handled as they would appear in a +``self`` attribute annotation in a regular class. If you want some protocol +attributes to be handled as though they were defined at class level, you should +declare them explicitly using ``ClassVar[...]``. Continuing previous example: + +.. code-block:: python + + from typing import ClassVar + + class OtherExample(Protocol): + # This style is *not recommended*, but may be needed to reuse + # some complex callable types. Otherwise use regular methods. + foo: ClassVar[Callable[[object], int]] + # This may be needed to mimic descriptor access on Type[...] types, + # otherwise use a plain "bar: int" style. + bar: ClassVar[Integer] + + ex2: OtherExample + reveal_type(ex2.foo) # Revealed type is Callable[[], int] + reveal_type(ex2.bar) # Revealed type is int + .. _predefined_protocols_reference: Predefined protocol reference diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 42474ae94c48c..9f7461d24f72f 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -277,6 +277,31 @@ 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: + + .. tab:: mypy.ini + + .. code-block:: ini + + [mypy-untyped_package.*] + follow_untyped_imports = True + + .. tab:: pyproject.toml + + .. code-block:: 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. @@ -293,10 +318,22 @@ not catch errors in its use. suppose your codebase makes heavy use of an (untyped) library named ``foobar``. You can silence all import errors associated with that library and that library alone by - adding the following section to your config file:: + adding the following section to your config file: + + .. tab:: mypy.ini + + .. code-block:: ini + + [mypy-foobar.*] + ignore_missing_imports = True + + .. tab:: pyproject.toml - [mypy-foobar.*] - ignore_missing_imports = True + .. code-block:: 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 @@ -309,11 +346,21 @@ not catch errors in its use. in your codebase, use :option:`--disable-error-code=import-untyped `. See :ref:`code-import-untyped` for more details on this error code. - You can also set :confval:`disable_error_code`, like so:: + You can also set :confval:`disable_error_code`, like so: + + .. tab:: mypy.ini + + .. code-block:: ini - [mypy] - disable_error_code = import-untyped + [mypy] + disable_error_code = import-untyped + .. tab:: pyproject.toml + + .. code-block:: ini + + [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 +550,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 +561,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 d039db30f3fa6..edc375e26485b 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -8,10 +8,9 @@ version of Python considers legal code. This section describes these scenarios and explains how to get your code running again. Generally speaking, we have three tools at our disposal: -* Use of ``from __future__ import annotations`` (:pep:`563`) - (this behaviour may eventually be made the default in a future Python version) * Use of string literal types or type comments * Use of ``typing.TYPE_CHECKING`` +* Use of ``from __future__ import annotations`` (:pep:`563`) We provide a description of these before moving onto discussion of specific problems you may encounter. @@ -274,7 +273,7 @@ libraries if types are generic only in stubs. Using types defined in stubs but not at runtime ----------------------------------------------- -Sometimes stubs that you're using may define types you wish to re-use that do +Sometimes stubs that you're using may define types you wish to reuse that do not exist at runtime. Importing these types naively will cause your code to fail at runtime with ``ImportError`` or ``ModuleNotFoundError``. Similar to previous sections, these can be dealt with by using :ref:`typing.TYPE_CHECKING @@ -335,16 +334,14 @@ Using new additions to the typing module ---------------------------------------- You may find yourself wanting to use features added to the :py:mod:`typing` -module in earlier versions of Python than the addition, for example, using any -of ``Literal``, ``Protocol``, ``TypedDict`` with Python 3.6. +module in earlier versions of Python than the addition. The easiest way to do this is to install and use the ``typing_extensions`` package from PyPI for the relevant imports, for example: .. code-block:: python - from typing_extensions import Literal - x: Literal["open", "close"] + from typing_extensions import TypeIs If you don't want to rely on ``typing_extensions`` being installed on newer Pythons, you could alternatively use: @@ -352,12 +349,10 @@ Pythons, you could alternatively use: .. code-block:: python import sys - if sys.version_info >= (3, 8): - from typing import Literal + if sys.version_info >= (3, 13): + from typing import TypeIs else: - from typing_extensions import Literal - - x: Literal["open", "close"] + from typing_extensions import TypeIs This plays nicely well with following :pep:`508` dependency specification: -``typing_extensions; python_version<"3.8"`` +``typing_extensions; python_version<"3.13"`` diff --git a/docs/source/stubtest.rst b/docs/source/stubtest.rst index 59889252f0569..e7ea69290b78a 100644 --- a/docs/source/stubtest.rst +++ b/docs/source/stubtest.rst @@ -99,8 +99,75 @@ to mypy build errors". In this case, you will need to mitigate those errors before stubtest will run. Despite potential overlap in errors here, stubtest is not intended as a substitute for running mypy directly. +Allowlist +********* + If you wish to ignore some of stubtest's complaints, stubtest supports a -pretty handy allowlist system. +pretty handy :option:`--allowlist` system. + +Let's say that you have this python module called ``ex``: + +.. code-block:: python + + try: + import optional_expensive_dep + except ImportError: + optional_expensive_dep = None + + first = 1 + if optional_expensive_dep: + second = 2 + +Let's say that you can't install ``optional_expensive_dep`` in CI for some reason, +but you still want to include ``second: int`` in the stub file: + +.. code-block:: python + + first: int + second: int + +In this case stubtest will correctly complain: + +.. code-block:: shell + + error: ex.second is not present at runtime + Stub: in file /.../ex.pyi:2 + builtins.int + Runtime: + MISSING + + Found 1 error (checked 1 module) + +To fix this, you can add an ``allowlist`` entry: + +.. code-block:: ini + + # Allowlist entries in `allowlist.txt` file: + + # Does not exist if `optional_expensive_dep` is not installed: + ex.second + +And now when running stubtest with ``--allowlist=allowlist.txt``, +no errors will be generated anymore. + +Allowlists also support regular expressions, +which can be useful to ignore many similar errors at once. +They can also be useful for suppressing stubtest errors that occur sometimes, +but not on every CI run. For example, if some CI workers have +``optional_expensive_dep`` installed, stubtest might complain with this message +on those workers if you had the ``ex.second`` allowlist entry: + +.. code-block:: ini + + note: unused allowlist entry ex.second + Found 1 error (checked 1 module) + +Changing ``ex.second`` to be ``(ex\.second)?`` will make this error optional, +meaning that stubtest will pass whether or not a CI runner +has``optional_expensive_dep`` installed. + +CLI +*** The rest of this section documents the command line interface of stubtest. @@ -119,15 +186,15 @@ The rest of this section documents the command line interface of stubtest. .. option:: --allowlist FILE Use file as an allowlist. Can be passed multiple times to combine multiple - allowlists. Allowlists can be created with --generate-allowlist. Allowlists - support regular expressions. + allowlists. Allowlists can be created with :option:`--generate-allowlist`. + Allowlists support regular expressions. The presence of an entry in the allowlist means stubtest will not generate any errors for the corresponding definition. .. option:: --generate-allowlist - Print an allowlist (to stdout) to be used with --allowlist + Print an allowlist (to stdout) to be used with :option:`--allowlist`. When introducing stubtest to an existing project, this is an easy way to silence all existing errors. @@ -141,17 +208,17 @@ The rest of this section documents the command line interface of stubtest. Note if an allowlist entry is a regex that matches the empty string, stubtest will never consider it unused. For example, to get - `--ignore-unused-allowlist` behaviour for a single allowlist entry like + ``--ignore-unused-allowlist`` behaviour for a single allowlist entry like ``foo.bar`` you could add an allowlist entry ``(foo\.bar)?``. This can be useful when an error only occurs on a specific platform. .. option:: --mypy-config-file FILE - Use specified mypy config file to determine mypy plugins and mypy path + Use specified mypy config *file* to determine mypy plugins and mypy path .. option:: --custom-typeshed-dir DIR - Use the custom typeshed in DIR + Use the custom typeshed in *DIR* .. option:: --check-typeshed diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 231a7edccfe76..ccd16ffbc0a35 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -8,6 +8,15 @@ techniques which are supported by mypy. Type narrowing is when you convince a type checker that a broader type is actually more specific, for instance, that an object of type ``Shape`` is actually of the narrower type ``Square``. +The following type narrowing techniques are available: + +- :ref:`type-narrowing-expressions` +- :ref:`casts` +- :ref:`type-guards` +- :ref:`typeis` + + +.. _type-narrowing-expressions: Type narrowing expressions -------------------------- @@ -356,6 +365,173 @@ What happens here? The same will work with ``isinstance(x := a, float)`` as well. + +.. _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) + + Limitations ----------- diff --git a/docs/source/typed_dict.rst b/docs/source/typed_dict.rst index e69b3895c668b..d42b434b05b2d 100644 --- a/docs/source/typed_dict.rst +++ b/docs/source/typed_dict.rst @@ -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 -------------------- @@ -263,8 +303,8 @@ to use inline TypedDict syntax. For example: .. code-block:: python - def test_values() -> {"int": int, "str": str}: - return {"int": 42, "str": "test"} + def test_values() -> {"width": int, "description": str}: + return {"width": 42, "description": "test"} class Response(TypedDict): status: int diff --git a/misc/analyze_cache.py b/misc/analyze_cache.py index 33205f5132fc3..f911522f5c648 100644 --- a/misc/analyze_cache.py +++ b/misc/analyze_cache.py @@ -6,12 +6,13 @@ import os import os.path from collections import Counter -from typing import Any, Dict, Final, Iterable +from collections.abc import Iterable +from typing import Any, Final from typing_extensions import TypeAlias as _TypeAlias ROOT: Final = ".mypy_cache/3.5" -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] class CacheData: @@ -40,7 +41,7 @@ def extract(chunks: Iterable[JsonDict]) -> Iterable[JsonDict]: if isinstance(chunk, dict): yield chunk yield from extract(chunk.values()) - elif isinstance(chunk, list): + elif isinstance(chunk, list): # type: ignore[unreachable] #TODO: is this actually unreachable, or are our types wrong? yield from extract(chunk) yield from extract([chunk.data for chunk in chunks]) @@ -92,7 +93,7 @@ def compress(chunk: JsonDict) -> JsonDict: def helper(chunk: JsonDict) -> JsonDict: nonlocal counter if not isinstance(chunk, dict): - return chunk + return chunk # type: ignore[unreachable] #TODO: is this actually unreachable, or are our types wrong? if len(chunk) <= 2: return chunk @@ -123,7 +124,7 @@ def decompress(chunk: JsonDict) -> JsonDict: def helper(chunk: JsonDict) -> JsonDict: if not isinstance(chunk, dict): - return chunk + return chunk # type: ignore[unreachable] #TODO: is this actually unreachable, or are our types wrong? if ".id" in chunk: return cache[chunk[".id"]] diff --git a/misc/analyze_typeform_stats.py b/misc/analyze_typeform_stats.py new file mode 100644 index 0000000000000..0a540610bc620 --- /dev/null +++ b/misc/analyze_typeform_stats.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +""" +Analyze TypeForm parsing efficiency from mypy build stats. + +Usage: + python3 analyze_typeform_stats.py '' + python3 -m mypy --dump-build-stats file.py 2>&1 | python3 analyze_typeform_stats.py + +Example output: + TypeForm Expression Parsing Statistics: + ================================================== + Total calls to SA.try_parse_as_type_expression: 14,555 + Quick rejections (no full parse): 14,255 + Full parses attempted: 300 + - Successful: 248 + - Failed: 52 + + Efficiency Metrics: + - Quick rejection rate: 97.9% + - Full parse rate: 2.1% + - Full parse success rate: 82.7% + - Overall success rate: 1.7% + + Performance Implications: + - Expensive failed full parses: 52 (0.4% of all calls) + +See also: + - mypy/semanal.py: SemanticAnalyzer.try_parse_as_type_expression() + - mypy/semanal.py: DEBUG_TYPE_EXPRESSION_FULL_PARSE_FAILURES +""" + +import re +import sys + + +def analyze_stats(output: str) -> None: + """Parse mypy stats output and calculate TypeForm parsing efficiency.""" + + # Extract the three counters + total_match = re.search(r"type_expression_parse_count:\s*(\d+)", output) + success_match = re.search(r"type_expression_full_parse_success_count:\s*(\d+)", output) + failure_match = re.search(r"type_expression_full_parse_failure_count:\s*(\d+)", output) + + if not (total_match and success_match and failure_match): + print("Error: Could not find all required counters in output") + return + + total = int(total_match.group(1)) + successes = int(success_match.group(1)) + failures = int(failure_match.group(1)) + + full_parses = successes + failures + + print("TypeForm Expression Parsing Statistics:") + print("=" * 50) + print(f"Total calls to SA.try_parse_as_type_expression: {total:,}") + print(f"Quick rejections (no full parse): {total - full_parses:,}") + print(f"Full parses attempted: {full_parses:,}") + print(f" - Successful: {successes:,}") + print(f" - Failed: {failures:,}") + if total > 0: + print() + print("Efficiency Metrics:") + print(f" - Quick rejection rate: {((total - full_parses) / total * 100):.1f}%") + print(f" - Full parse rate: {(full_parses / total * 100):.1f}%") + print(f" - Full parse success rate: {(successes / full_parses * 100):.1f}%") + print(f" - Overall success rate: {(successes / total * 100):.1f}%") + print() + print("Performance Implications:") + print( + f" - Expensive failed full parses: {failures:,} ({(failures / total * 100):.1f}% of all calls)" + ) + + +if __name__ == "__main__": + if len(sys.argv) == 1: + # Read from stdin + output = sys.stdin.read() + elif len(sys.argv) == 2: + # Read from command line argument + output = sys.argv[1] + else: + print("Usage: python3 analyze_typeform_stats.py [mypy_output_with_stats]") + print("Examples:") + print( + " python3 -m mypy --dump-build-stats file.py 2>&1 | python3 analyze_typeform_stats.py" + ) + print(" python3 analyze_typeform_stats.py 'output_string'") + sys.exit(1) + + analyze_stats(output) 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/docker/README.md b/misc/docker/README.md index 839f9761cb03a..0e9a3a80ff0ed 100644 --- a/misc/docker/README.md +++ b/misc/docker/README.md @@ -12,7 +12,7 @@ Why use Docker? Mypyc tests can be significantly faster in a Docker container than running natively on macOS. -Also, if it's inconvient to install the necessary dependencies on the +Also, if it's inconvenient to install the necessary dependencies on the host operating system, or there are issues getting some tests to pass on the host operating system, using a container can be an easy workaround. diff --git a/misc/gen_blog_post_html.py b/misc/gen_blog_post_html.py index 7170696d5d092..1c2d876486049 100644 --- a/misc/gen_blog_post_html.py +++ b/misc/gen_blog_post_html.py @@ -44,25 +44,34 @@ def format_code(h: str) -> str: while i < len(a): if a[i].startswith(" ") or a[i].startswith("```"): indent = a[i].startswith(" ") + language: str = "" if not indent: + language = a[i][3:] i += 1 - r.append("
")
+            if language:
+                r.append(f'
')
+            else:
+                r.append("
")
             while i < len(a) and (
                 (indent and a[i].startswith("    ")) or (not indent and not a[i].startswith("```"))
             ):
                 # Undo > and <
                 line = a[i].replace(">", ">").replace("<", "<")
-                if not indent:
-                    line = "    " + line
+                if indent:
+                    # Undo this extra level of indentation so it looks nice with
+                    # syntax highlighting CSS.
+                    line = line[4:]
                 r.append(html.escape(line))
                 i += 1
-            r.append("
") + r.append("
") if not indent and a[i].startswith("```"): i += 1 else: r.append(a[i]) i += 1 - return "\n".join(r) + formatted = "\n".join(r) + # remove empty first line for code blocks + return re.sub(r"]*)>\n", r"", formatted) def convert(src: str) -> str: @@ -76,7 +85,7 @@ def convert(src: str) -> str: h = re.sub(r"^## (Mypy [0-9.]+)", r"

\1 Released

", h, flags=re.MULTILINE) # Subheadings - h = re.sub(r"\n#### ([A-Z`].*)\n", r"\n

\1

\n", h) + h = re.sub(r"\n### ([A-Z`].*)\n", r"\n

\1

\n", h) # Sub-subheadings h = re.sub(r"\n\*\*([A-Z_`].*)\*\*\n", r"\n

\1

\n", h) @@ -86,7 +95,7 @@ def convert(src: str) -> str: h = re.sub(r"`\*\*`", "**", h) # Paragraphs - h = re.sub(r"\n([A-Z])", r"\n

\1", h) + h = re.sub(r"\n\n([A-Z])", r"\n\n

\1", h) # Bullet lists h = format_lists(h) @@ -95,6 +104,7 @@ def convert(src: str) -> str: h = format_code(h) # Code fragments + h = re.sub(r"``([^`]+)``", r"\1", h) h = re.sub(r"`([^`]+)`", r"\1", h) # Remove **** noise @@ -116,7 +126,9 @@ def convert(src: str) -> str: r'fixes issue \1', h, ) - h = re.sub(r"#([0-9]+)", r'PR \1', h) + # Note the leading space to avoid stomping on strings that contain #\d in the middle (such as + # links to PRs in other repos) + h = re.sub(r" #([0-9]+)", r' PR \1', h) h = re.sub(r"\) \(PR", ", PR", h) # Markdown links @@ -129,8 +141,18 @@ def convert(src: str) -> str: h, ) - # Add missing top-level HTML tags - h = '\n\n\n' + h + "\n" + # Add top-level HTML tags and headers for syntax highlighting css/js. + # We're configuring hljs to highlight python and bash code. We can remove + # this configure call to make it try all the languages it supports. + h = f""" + + + + + +{h} + +""" return h diff --git a/misc/generate_changelog.py b/misc/generate_changelog.py index ebab6c5691525..c53a06e391330 100644 --- a/misc/generate_changelog.py +++ b/misc/generate_changelog.py @@ -145,7 +145,8 @@ def format_changelog_entry(c: CommitInfo) -> str: s += f" (#{c.pr_number})" s += f" ({c.author})" """ - s = f" * {c.title} ({c.author}" + title = c.title.removesuffix(".") + s = f" * {title} ({c.author}" if c.pr_number: s += f", PR [{c.pr_number}](https://github.com/python/mypy/pull/{c.pr_number})" s += ")" diff --git a/misc/incremental_checker.py b/misc/incremental_checker.py index 4e42aef333bb4..a9ed61d134145 100755 --- a/misc/incremental_checker.py +++ b/misc/incremental_checker.py @@ -44,7 +44,7 @@ import textwrap import time from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter -from typing import Any, Dict, Final +from typing import Any, Final from typing_extensions import TypeAlias as _TypeAlias CACHE_PATH: Final = ".incremental_checker_cache.json" @@ -52,7 +52,7 @@ MYPY_TARGET_FILE: Final = "mypy" DAEMON_CMD: Final = ["python3", "-m", "mypy.dmypy"] -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] def print_offset(text: str, indent_length: int = 4) -> None: diff --git a/misc/log_trace_check.py b/misc/log_trace_check.py new file mode 100644 index 0000000000000..677c164fe9925 --- /dev/null +++ b/misc/log_trace_check.py @@ -0,0 +1,85 @@ +"""Compile mypy using mypyc with trace logging enabled, and collect a trace. + +The trace log can be used to analyze low-level performance bottlenecks. + +By default does a self check as the workload. + +This works on all supported platforms, unlike some of our other performance tools. +""" + +from __future__ import annotations + +import argparse +import glob +import os +import shutil +import subprocess +import sys +import time + +from perf_compare import build_mypy, clone + +# Generated files, including binaries, go under this directory to avoid overwriting user state. +TARGET_DIR = "mypy.log_trace.tmpdir" + + +def perform_type_check(target_dir: str, code: str | None) -> None: + cache_dir = os.path.join(target_dir, ".mypy_cache") + if os.path.exists(cache_dir): + shutil.rmtree(cache_dir) + args = [] + if code is None: + args.extend(["--config-file", "mypy_self_check.ini"]) + for pat in "mypy/*.py", "mypy/*/*.py", "mypyc/*.py", "mypyc/test/*.py": + args.extend(glob.glob(pat)) + else: + args.extend(["-c", code]) + check_cmd = ["python", "-m", "mypy"] + args + t0 = time.time() + subprocess.run(check_cmd, cwd=target_dir, check=True) + elapsed = time.time() - t0 + print(f"{elapsed:.2f}s elapsed") + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Compile mypy and collect a trace log while type checking (by default, self check)." + ) + parser.add_argument( + "--multi-file", + action="store_true", + help="compile mypy into one C file per module (to reduce RAM use during compilation)", + ) + parser.add_argument( + "--skip-compile", action="store_true", help="use compiled mypy from previous run" + ) + parser.add_argument( + "-c", + metavar="CODE", + default=None, + type=str, + help="type check Python code fragment instead of mypy self-check", + ) + args = parser.parse_args() + multi_file: bool = args.multi_file + skip_compile: bool = args.skip_compile + code: str | None = args.c + + target_dir = TARGET_DIR + + if not skip_compile: + clone(target_dir, "HEAD") + + print(f"Building mypy in {target_dir} with trace logging enabled...") + build_mypy(target_dir, multi_file, log_trace=True, opt_level="0") + elif not os.path.isdir(target_dir): + sys.exit("error: Can't find compile mypy from previous run -- can't use --skip-compile") + + perform_type_check(target_dir, code) + + trace_fnam = os.path.join(target_dir, "mypyc_trace.txt") + print(f"Generated event trace log in {trace_fnam}") + + +if __name__ == "__main__": + main() diff --git a/misc/perf_compare.py b/misc/perf_compare.py old mode 100644 new mode 100755 index be05bb6ddc32a..aa05270a8c00f --- a/misc/perf_compare.py +++ b/misc/perf_compare.py @@ -1,15 +1,17 @@ +#! /usr/bin/env python + """Compare performance of mypyc-compiled mypy between one or more commits/branches. Simple usage: - python misc/perf_compare.py my-branch master ... + python misc/perf_compare.py master my-branch ... What this does: * Create a temp clone of the mypy repo for each target commit to measure * Checkout a target commit in each of the clones * Compile mypyc in each of the clones *in parallel* - * Create another temp clone of the mypy repo as the code to check + * Create another temp clone of the first provided revision (or, with -r, a foreign repo) as the code to check * Self check with each of the compiled mypys N times * Report the average runtimes and relative performance * Remove the temp clones @@ -25,8 +27,8 @@ import statistics import subprocess import sys -import threading import time +from concurrent.futures import ThreadPoolExecutor, as_completed def heading(s: str) -> None: @@ -35,77 +37,183 @@ def heading(s: str) -> None: print() -def build_mypy(target_dir: str) -> None: +def build_mypy( + target_dir: str, + multi_file: bool, + *, + cflags: str | None = None, + log_trace: bool = False, + opt_level: str = "2", +) -> None: env = os.environ.copy() env["CC"] = "clang" - env["MYPYC_OPT_LEVEL"] = "2" + env["MYPYC_OPT_LEVEL"] = opt_level + env["PYTHONHASHSEED"] = "1" + if multi_file: + env["MYPYC_MULTI_FILE"] = "1" + if log_trace: + env["MYPYC_LOG_TRACE"] = "1" + if cflags is not None: + env["CFLAGS"] = cflags cmd = [sys.executable, "setup.py", "--use-mypyc", "build_ext", "--inplace"] subprocess.run(cmd, env=env, check=True, cwd=target_dir) -def clone(target_dir: str, commit: str | None) -> None: - heading(f"Cloning mypy to {target_dir}") - repo_dir = os.getcwd() +def clone(target_dir: str, commit: str | None, repo_source: str | None = None) -> None: + source_name = repo_source or "mypy" + heading(f"Cloning {source_name} to {target_dir}") + if repo_source is None: + repo_source = os.getcwd() if os.path.isdir(target_dir): print(f"{target_dir} exists: deleting") shutil.rmtree(target_dir) - subprocess.run(["git", "clone", repo_dir, target_dir], check=True) + subprocess.run(["git", "clone", repo_source, target_dir], check=True) if commit: subprocess.run(["git", "checkout", commit], check=True, cwd=target_dir) -def run_benchmark(compiled_dir: str, check_dir: str) -> float: +def edit_python_file(fnam: str) -> None: + with open(fnam) as f: + data = f.read() + data += "\n#" + with open(fnam, "w") as f: + f.write(data) + + +def run_benchmark( + compiled_dir: str, check_dir: str, *, incremental: bool, code: str | None, foreign: bool | None +) -> float: cache_dir = os.path.join(compiled_dir, ".mypy_cache") - if os.path.isdir(cache_dir): + if os.path.isdir(cache_dir) and not incremental: shutil.rmtree(cache_dir) env = os.environ.copy() env["PYTHONPATH"] = os.path.abspath(compiled_dir) + env["PYTHONHASHSEED"] = "1" abschk = os.path.abspath(check_dir) - cmd = [ - sys.executable, - "-m", - "mypy", - "--config-file", - os.path.join(abschk, "mypy_self_check.ini"), - ] - cmd += glob.glob(os.path.join(abschk, "mypy/*.py")) - cmd += glob.glob(os.path.join(abschk, "mypy/*/*.py")) + cmd = [sys.executable, "-m", "mypy"] + if code: + cmd += ["-c", code] + elif foreign: + pass + else: + cmd += ["--config-file", os.path.join(abschk, "mypy_self_check.ini")] + cmd += glob.glob(os.path.join(abschk, "mypy/*.py")) + cmd += glob.glob(os.path.join(abschk, "mypy/*/*.py")) + if incremental: + # Update a few files to force non-trivial incremental run + edit_python_file(os.path.join(abschk, "mypy/__main__.py")) + edit_python_file(os.path.join(abschk, "mypy/test/testcheck.py")) t0 = time.time() # Ignore errors, since some commits being measured may generate additional errors. - subprocess.run(cmd, cwd=compiled_dir, env=env) + if foreign: + subprocess.run(cmd, cwd=check_dir, env=env) + else: + subprocess.run(cmd, cwd=compiled_dir, env=env) return time.time() - t0 def main() -> None: - parser = argparse.ArgumentParser() - parser.add_argument("commit", nargs="+") + whole_program_time_0 = time.time() + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__, + epilog="Remember: you usually want the first argument to this command to be 'master'.", + ) + parser.add_argument( + "--incremental", + default=False, + action="store_true", + help="measure incremental run (fully cached)", + ) + parser.add_argument( + "--multi-file", + default=False, + action="store_true", + help="compile each mypy module to a separate C file (reduces RAM use)", + ) + parser.add_argument( + "--dont-setup", + default=False, + action="store_true", + help="don't make the clones or compile mypy, just run the performance measurement benchmark " + + "(this will fail unless the clones already exist, such as from a previous run that was canceled before it deleted them)", + ) + parser.add_argument( + "--num-runs", + metavar="N", + default=15, + type=int, + help="set number of measurements to perform (default=15)", + ) + parser.add_argument( + "-j", + metavar="N", + default=4, + type=int, + help="set maximum number of parallel builds (default=4) -- high numbers require a lot of RAM!", + ) + parser.add_argument( + "-r", + metavar="FOREIGN_REPOSITORY", + default=None, + type=str, + help="measure time to typecheck the project at FOREIGN_REPOSITORY instead of mypy self-check; " + + "the provided value must be the URL or path of a git repo " + + "(note that this script will take no special steps to *install* the foreign repo, so you will probably get a lot of missing import errors)", + ) + parser.add_argument( + "-c", + metavar="CODE", + default=None, + type=str, + help="measure time to type check Python code fragment instead of mypy self-check", + ) + parser.add_argument( + "commit", + nargs="+", + help="git revision(s), e.g. branch name or commit id, to measure the performance of", + ) args = parser.parse_args() + incremental: bool = args.incremental + dont_setup: bool = args.dont_setup + multi_file: bool = args.multi_file commits = args.commit - num_runs = 16 + num_runs: int = args.num_runs + 1 + max_workers: int = args.j + code: str | None = args.c + foreign_repo: str | None = args.r if not (os.path.isdir(".git") and os.path.isdir("mypyc")): - sys.exit("error: Run this the mypy repo root") + sys.exit("error: You must run this script from the mypy repo root") - build_threads = [] target_dirs = [] for i, commit in enumerate(commits): target_dir = f"mypy.{i}.tmpdir" target_dirs.append(target_dir) - clone(target_dir, commit) - t = threading.Thread(target=lambda: build_mypy(target_dir)) - t.start() - build_threads.append(t) + if not dont_setup: + clone(target_dir, commit) - self_check_dir = "mypy.self.tmpdir" - clone(self_check_dir, commits[0]) + if foreign_repo: + check_dir = "mypy.foreign.tmpdir" + if not dont_setup: + clone(check_dir, None, foreign_repo) + else: + check_dir = "mypy.self.tmpdir" + if not dont_setup: + clone(check_dir, commits[0]) - heading("Compiling mypy") - print("(This will take a while...)") + if not dont_setup: + heading("Compiling mypy") + print("(This will take a while...)") - for t in build_threads: - t.join() + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = [ + executor.submit(build_mypy, target_dir, multi_file) for target_dir in target_dirs + ] + for future in as_completed(futures): + future.result() - print(f"Finished compiling mypy ({len(commits)} builds)") + print(f"Finished compiling mypy ({len(commits)} builds)") heading("Performing measurements") @@ -118,7 +226,13 @@ def main() -> None: items = list(enumerate(commits)) random.shuffle(items) for i, commit in items: - tt = run_benchmark(target_dirs[i], self_check_dir) + tt = run_benchmark( + target_dirs[i], + check_dir, + incremental=incremental, + code=code, + foreign=bool(foreign_repo), + ) # Don't record the first warm-up run if n > 0: print(f"{commit}: t={tt:.3f}s") @@ -129,15 +243,28 @@ def main() -> None: first = -1.0 for commit in commits: tt = statistics.mean(results[commit]) + # pstdev (instead of stdev) is used here primarily to accommodate the case where num_runs=1 + s = statistics.pstdev(results[commit]) if len(results[commit]) > 1 else 0 if first < 0: delta = "0.0%" first = tt else: d = (tt / first) - 1 delta = f"{d:+.1%}" - print(f"{commit:<25} {tt:.3f}s ({delta})") + print(f"{commit:<25} {tt:.3f}s ({delta}) | stdev {s:.3f}s ") + + t = int(time.time() - whole_program_time_0) + total_time_taken_formatted = ", ".join( + f"{v} {n if v==1 else n+'s'}" + for v, n in ((t // 3600, "hour"), (t // 60 % 60, "minute"), (t % 60, "second")) + if v + ) + print( + "Total time taken by the whole benchmarking program (including any setup):", + total_time_taken_formatted, + ) - shutil.rmtree(self_check_dir) + shutil.rmtree(check_dir) for target_dir in target_dirs: shutil.rmtree(target_dir) diff --git a/misc/profile_check.py b/misc/profile_check.py new file mode 100644 index 0000000000000..6bd23b09b2d5b --- /dev/null +++ b/misc/profile_check.py @@ -0,0 +1,145 @@ +"""Compile mypy using mypyc and profile type checking using perf. + +By default does a self check. + +Notes: + - Only Linux is supported for now (TODO: add support for other profilers) + - The profile is collected at C level + - It includes C functions compiled by mypyc and CPython runtime functions + - The names of mypy functions are mangled to C names, but usually it's clear what they mean + - Generally CPyDef_ prefix for native functions and CPyPy_ prefix for wrapper functions + - It's important to compile CPython using special flags (see below) to get good results + - Generally use the latest Python feature release (or the most recent beta if supported by mypyc) + - The tool prints a command that can be used to analyze the profile afterwards + +You may need to adjust kernel parameters temporarily, e.g. this (note that this has security +implications): + + sudo sysctl kernel.perf_event_paranoid=-1 + +This is the recommended way to configure CPython for profiling: + + ./configure \ + --enable-optimizations \ + --with-lto \ + CFLAGS="-O2 -g -fno-omit-frame-pointer" +""" + +from __future__ import annotations + +import argparse +import glob +import os +import shutil +import subprocess +import sys +import time + +from perf_compare import build_mypy, clone + +# Use these C compiler flags when compiling mypy (important). Note that it's strongly recommended +# to also compile CPython using similar flags, but we don't enforce it in this script. +CFLAGS = "-O2 -fno-omit-frame-pointer -g" + +# Generated files, including binaries, go under this directory to avoid overwriting user state. +TARGET_DIR = "mypy.profile.tmpdir" + + +def _profile_type_check(target_dir: str, code: str | None) -> None: + cache_dir = os.path.join(target_dir, ".mypy_cache") + if os.path.exists(cache_dir): + shutil.rmtree(cache_dir) + args = [] + if code is None: + args.extend(["--config-file", "mypy_self_check.ini"]) + for pat in "mypy/*.py", "mypy/*/*.py", "mypyc/*.py", "mypyc/test/*.py": + args.extend(glob.glob(pat)) + else: + args.extend(["-c", code]) + check_cmd = ["python", "-m", "mypy"] + args + cmdline = ["perf", "record", "-g"] + check_cmd + t0 = time.time() + subprocess.run(cmdline, cwd=target_dir, check=True) + elapsed = time.time() - t0 + print(f"{elapsed:.2f}s elapsed") + + +def profile_type_check(target_dir: str, code: str | None) -> None: + try: + _profile_type_check(target_dir, code) + except subprocess.CalledProcessError: + print("\nProfiling failed! You may missing some permissions.") + print("\nThis may help (note that it has security implications):") + print(" sudo sysctl kernel.perf_event_paranoid=-1") + sys.exit(1) + + +def check_requirements() -> None: + if sys.platform != "linux": + # TODO: How to make this work on other platforms? + sys.exit("error: Only Linux is supported") + else: # fun fact/todo: we have to use else here, because of https://github.com/python/mypy/issues/10773 + try: + subprocess.run(["perf", "-h"], capture_output=True) + except (subprocess.CalledProcessError, FileNotFoundError): + print("error: The 'perf' profiler is not installed") + sys.exit(1) + + try: + subprocess.run(["clang", "--version"], capture_output=True) + except (subprocess.CalledProcessError, FileNotFoundError): + print("error: The clang compiler is not installed") + sys.exit(1) + + if not os.path.isfile("mypy_self_check.ini"): + print("error: Run this in the mypy repository root") + sys.exit(1) + + +def main() -> None: + check_requirements() + + parser = argparse.ArgumentParser( + description="Compile mypy and profile type checking using 'perf' (by default, self check)." + ) + parser.add_argument( + "--multi-file", + action="store_true", + help="compile mypy into one C file per module (to reduce RAM use during compilation)", + ) + parser.add_argument( + "--skip-compile", action="store_true", help="use compiled mypy from previous run" + ) + parser.add_argument( + "-c", + metavar="CODE", + default=None, + type=str, + help="profile type checking Python code fragment instead of mypy self-check", + ) + args = parser.parse_args() + multi_file: bool = args.multi_file + skip_compile: bool = args.skip_compile + code: str | None = args.c + + target_dir = TARGET_DIR + + if not skip_compile: + clone(target_dir, "HEAD") + + print(f"Building mypy in {target_dir}...") + build_mypy(target_dir, multi_file, cflags=CFLAGS) + elif not os.path.isdir(target_dir): + sys.exit("error: Can't find compile mypy from previous run -- can't use --skip-compile") + + profile_type_check(target_dir, code) + + print() + print('NOTE: Compile CPython using CFLAGS="-O2 -g -fno-omit-frame-pointer" for good results') + print() + print("CPU profile collected. You can now analyze the profile:") + print(f" perf report -i {target_dir}/perf.data ") + + +if __name__ == "__main__": + main() diff --git a/misc/self_compile_info.py b/misc/self_compile_info.py new file mode 100644 index 0000000000000..f413eb489165e --- /dev/null +++ b/misc/self_compile_info.py @@ -0,0 +1,45 @@ +"""Print list of files compiled when compiling self (mypy and mypyc).""" + +import argparse +import sys +from typing import Any + +import setuptools + +import mypyc.build + + +class FakeExtension: + def __init__(self, *args: Any, **kwargs: Any) -> None: + pass + + +def fake_mypycify(args: list[str], **kwargs: Any) -> list[FakeExtension]: + for target in sorted(args): + if not target.startswith("-"): + print(target) + return [FakeExtension()] + + +def fake_setup(*args: Any, **kwargs: Any) -> Any: + pass + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Print list of files compiled when compiling self. Run in repository root." + ) + parser.parse_args() + + # Prepare fake state for running setup.py. + mypyc.build.mypycify = fake_mypycify # type: ignore[assignment] + setuptools.Extension = FakeExtension # type: ignore[misc, assignment] + setuptools.setup = fake_setup + sys.argv = [sys.argv[0], "--use-mypyc"] + + # Run setup.py at the root of the repository. + import setup # noqa: F401 + + +if __name__ == "__main__": + main() diff --git a/misc/trigger_wheel_build.sh b/misc/trigger_wheel_build.sh index c914a6e7cf867..a2608d93f3490 100755 --- a/misc/trigger_wheel_build.sh +++ b/misc/trigger_wheel_build.sh @@ -3,7 +3,7 @@ # Trigger a build of mypyc compiled mypy wheels by updating the mypy # submodule in the git repo that drives those builds. -# $WHEELS_PUSH_TOKEN is stored in Github Settings and is an API token +# $WHEELS_PUSH_TOKEN is stored in GitHub Settings and is an API token # for the mypy-build-bot account. git config --global user.email "nobody" diff --git a/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch new file mode 100644 index 0000000000000..5c31569711e5a --- /dev/null +++ b/misc/typeshed_patches/0001-Partially-revert-Clean-up-argparse-hacks.patch @@ -0,0 +1,45 @@ +From 84a9d586544a0408d4654f57f83a93cb048070fb Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Sat, 15 Feb 2025 20:11:06 +0100 +Subject: [PATCH] Partially revert Clean up argparse hacks + +--- + mypy/typeshed/stdlib/argparse.pyi | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi +index b9fa31139..3c3ba116a 100644 +--- a/mypy/typeshed/stdlib/argparse.pyi ++++ b/mypy/typeshed/stdlib/argparse.pyi +@@ -2,7 +2,7 @@ import sys + from _typeshed import SupportsWrite, sentinel + from collections.abc import Callable, Generator, Iterable, Sequence + from re import Pattern +-from typing import IO, Any, ClassVar, Final, Generic, NoReturn, Protocol, TypeVar, overload, type_check_only ++from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload, type_check_only + from typing_extensions import Self, TypeAlias, deprecated + + __all__ = [ +@@ -36,7 +36,9 @@ ONE_OR_MORE: Final = "+" + OPTIONAL: Final = "?" + PARSER: Final = "A..." + REMAINDER: Final = "..." +-SUPPRESS: Final = "==SUPPRESS==" ++_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: Final = "*" + _UNRECOGNIZED_ARGS_ATTR: Final = "_unrecognized_args" # undocumented + +@@ -79,7 +81,7 @@ class _ActionsContainer: + # more precisely, Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="], + # but using this would make it hard to annotate callers that don't use a + # literal argument and for subclasses to override this method. +- nargs: int | str | None = None, ++ nargs: int | str | _SUPPRESS_T | None = None, + const: Any = ..., + default: Any = ..., + type: _ActionType = ..., +-- +2.50.1 + 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..f9334251c2bdb 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 3229a6066cff3d80d6cb923322c2d42a300d0be3 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 969d16876..044e264d2 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,UP035 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, @@ -19,10 +19,10 @@ index 53e00ec6a..bad3250ef 100644 ParamSpec, Self, TypeAlias, -@@ -435,31 +434,16 @@ class str(Sequence[str]): - def __new__(cls, object: object = ...) -> Self: ... +@@ -480,31 +479,16 @@ class str(Sequence[str]): + def __new__(cls, object: object = "") -> Self: ... @overload - def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + def __new__(cls, object: ReadableBuffer, encoding: str = "utf-8", errors: str = "strict") -> Self: ... - @overload - def capitalize(self: LiteralString) -> LiteralString: ... - @overload @@ -35,23 +35,23 @@ index 53e00ec6a..bad3250ef 100644 - def center(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... - @overload def center(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] - def count(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def count(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... def endswith( - self, suffix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, suffix: str | tuple[str, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> bool: ... - @overload - def expandtabs(self: LiteralString, tabsize: SupportsIndex = 8) -> LiteralString: ... - @overload def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] - def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def find(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... - @overload - def format(self: LiteralString, *args: LiteralString, **kwargs: LiteralString) -> LiteralString: ... - @overload 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]): + def index(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... +@@ -520,98 +504,34 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... @@ -89,18 +89,17 @@ index 53e00ec6a..bad3250ef 100644 - ) -> LiteralString: ... - @overload def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] - if sys.version_info >= (3, 9): -- @overload -- def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ... -- @overload - def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] -- @overload -- def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ... -- @overload - def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] - def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... - def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... +- @overload +- def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ... +- @overload + def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] +- @overload +- def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ... +- @overload + def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] + def rfind(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... + def rindex(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... - @overload - def rjust(self: LiteralString, width: SupportsIndex, fillchar: LiteralString = " ", /) -> LiteralString: ... - @overload @@ -126,7 +125,7 @@ index 53e00ec6a..bad3250ef 100644 - @overload def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( - self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, prefix: str | tuple[str, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> bool: ... - @overload - def strip(self: LiteralString, chars: LiteralString | None = None, /) -> LiteralString: ... @@ -151,7 +150,7 @@ index 53e00ec6a..bad3250ef 100644 def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] @staticmethod @overload -@@ -578,9 +498,6 @@ class str(Sequence[str]): +@@ -622,39 +542,21 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... @@ -161,8 +160,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 @@ -186,7 +190,7 @@ index 53e00ec6a..bad3250ef 100644 - @overload def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... - + def __format__(self, format_spec: str, /) -> str: ... -- -2.45.2 +2.51.1 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..7110eff5f148c --- /dev/null +++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch @@ -0,0 +1,330 @@ +From 7678bc3f80e4d3f04a0ff0ee3a7d51f49ae4c465 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 d663f5d93..f43178e4d 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, GenericAlias + from typing import Any, Literal, TextIO, TypeVar +@@ -11,7 +11,7 @@ _T_co = TypeVar("_T_co", covariant=True) + _TaskYieldType: TypeAlias = Future[object] | None + + @disjoint_base +-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 044e264d2..6d813f172 100644 +--- a/mypy/typeshed/stdlib/builtins.pyi ++++ b/mypy/typeshed/stdlib/builtins.pyi +@@ -1210,7 +1210,7 @@ class frozenset(AbstractSet[_T_co]): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + + @disjoint_base +-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]: ... +@@ -1404,7 +1404,7 @@ else: + exit: _sitebuiltins.Quitter + + @disjoint_base +-class filter(Generic[_T]): ++class filter(Iterator[_T]): + @overload + def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... + @overload +@@ -1468,7 +1468,7 @@ license: _sitebuiltins._Printer + + def locals() -> dict[str, Any]: ... + @disjoint_base +-class map(Generic[_S]): ++class map(Iterator[_S]): + # 3.14 adds `strict` argument. + if sys.version_info >= (3, 14): + @overload +@@ -1775,7 +1775,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex + quit: _sitebuiltins.Quitter + + @disjoint_base +-class reversed(Generic[_T]): ++class reversed(Iterator[_T]): + @overload + def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] + @overload +@@ -1839,7 +1839,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... + @overload + def vars(object: Any = ..., /) -> dict[str, Any]: ... + @disjoint_base +-class zip(Generic[_T_co]): ++class zip(Iterator[_T_co]): + if sys.version_info >= (3, 10): + @overload + def __new__(cls, *, strict: bool = False) -> zip[Any]: ... +diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi +index 2c8e7109c..4ed0ab1d8 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 types import GenericAlias + from typing import Any, Generic, Literal, TypeVar, overload + from typing_extensions import Self +@@ -73,7 +73,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 910d63814..eb942bc55 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 GenericAlias, TracebackType +-from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload, type_check_only ++from typing import IO, Any, AnyStr, Literal, Protocol, overload, type_check_only + from typing_extensions import Self, TypeAlias + + __all__ = [ +@@ -105,7 +105,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 fe4ccbdf8..73745fe92 100644 +--- a/mypy/typeshed/stdlib/itertools.pyi ++++ b/mypy/typeshed/stdlib/itertools.pyi +@@ -28,7 +28,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 + @disjoint_base +-class count(Generic[_N]): ++class count(Iterator[_N]): + @overload + def __new__(cls) -> count[int]: ... + @overload +@@ -39,13 +39,13 @@ class count(Generic[_N]): + def __iter__(self) -> Self: ... + + @disjoint_base +-class cycle(Generic[_T]): ++class cycle(Iterator[_T]): + def __new__(cls, iterable: Iterable[_T], /) -> Self: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... + + @disjoint_base +-class repeat(Generic[_T]): ++class repeat(Iterator[_T]): + @overload + def __new__(cls, object: _T) -> Self: ... + @overload +@@ -55,7 +55,7 @@ class repeat(Generic[_T]): + def __length_hint__(self) -> int: ... + + @disjoint_base +-class accumulate(Generic[_T]): ++class accumulate(Iterator[_T]): + @overload + def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ... + @overload +@@ -64,7 +64,7 @@ class accumulate(Generic[_T]): + def __next__(self) -> _T: ... + + @disjoint_base +-class chain(Generic[_T]): ++class chain(Iterator[_T]): + def __new__(cls, *iterables: Iterable[_T]) -> Self: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... +@@ -74,25 +74,25 @@ class chain(Generic[_T]): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + + @disjoint_base +-class compress(Generic[_T]): ++class compress(Iterator[_T]): + def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + @disjoint_base +-class dropwhile(Generic[_T]): ++class dropwhile(Iterator[_T]): + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + @disjoint_base +-class filterfalse(Generic[_T]): ++class filterfalse(Iterator[_T]): + def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + @disjoint_base +-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 +@@ -101,7 +101,7 @@ class groupby(Generic[_T_co, _S_co]): + def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... + + @disjoint_base +-class islice(Generic[_T]): ++class islice(Iterator[_T]): + @overload + def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ... + @overload +@@ -110,20 +110,20 @@ class islice(Generic[_T]): + def __next__(self) -> _T: ... + + @disjoint_base +-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: ... + + @disjoint_base +-class takewhile(Generic[_T]): ++class takewhile(Iterator[_T]): + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... + @disjoint_base +-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]]: ... +@@ -202,7 +202,7 @@ class zip_longest(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + @disjoint_base +-class product(Generic[_T_co]): ++class product(Iterator[_T_co]): + @overload + def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... + @overload +@@ -288,7 +288,7 @@ class product(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + @disjoint_base +-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 +@@ -303,7 +303,7 @@ class permutations(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + @disjoint_base +-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 +@@ -318,7 +318,7 @@ class combinations(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + @disjoint_base +-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 +@@ -334,14 +334,14 @@ class combinations_with_replacement(Generic[_T_co]): + + if sys.version_info >= (3, 10): + @disjoint_base +- 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): + @disjoint_base +- 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 b79f9e773..f276372d0 100644 +--- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi ++++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +@@ -1,4 +1,4 @@ +-from collections.abc import Callable, Iterable, Mapping ++from collections.abc import Callable, Iterable, Iterator, Mapping + from multiprocessing.context import DefaultContext, Process + from types import GenericAlias, TracebackType + from typing import Any, Final, Generic, TypeVar +@@ -32,7 +32,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 6b0f1ba94..882cd143c 100644 +--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi ++++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +@@ -407,7 +407,7 @@ class Connection: + ) -> Literal[False]: ... + + @disjoint_base +-class Cursor: ++class Cursor(Iterator[Any]): + arraysize: int + @property + def connection(self) -> Connection: ... +-- +2.51.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 331628af14243..559e32569f2ba 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 58c6a6ab863c1c38e95ccafaf13792ed9c00e499 Mon Sep 17 00:00:00 2001 +From 16b0b50ec77e470f24145071acde5274a1de53a0 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,10 +19,10 @@ 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 ea9f8c894..a6065cc67 100644 +index 900c4c93f..d874edd8f 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi -@@ -1653,7 +1653,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit +@@ -1782,7 +1782,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 @@ -32,5 +32,5 @@ index ea9f8c894..a6065cc67 100644 def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload -- -2.46.0 +2.49.0 diff --git a/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch b/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch index 27066bf3c25bb..c16f5ebaa92e2 100644 --- a/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch +++ b/misc/typeshed_patches/0001-Revert-typeshed-ctypes-change.patch @@ -1,4 +1,4 @@ -From 61a490091d7c941780919660dc4fdfa88ae6474a Mon Sep 17 00:00:00 2001 +From 85c0cfb55c6211c2a47c3f45d2ff28fa76f8204b Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Mon, 1 May 2023 20:34:55 +0100 Subject: [PATCH] Revert typeshed ctypes change Since the plugin provides @@ -11,10 +11,10 @@ Subject: [PATCH] Revert typeshed ctypes change Since the plugin provides 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi -index 60bbc51d9..cf9cb81a4 100644 +index 944685646..dc8c7b2ca 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi -@@ -169,11 +169,7 @@ class Array(_CData, Generic[_CT]): +@@ -289,11 +289,7 @@ class Array(_CData, Generic[_CT], metaclass=_PyCArrayType): def _type_(self) -> type[_CT]: ... @_type_.setter def _type_(self, value: type[_CT]) -> None: ... @@ -25,8 +25,8 @@ index 60bbc51d9..cf9cb81a4 100644 - def raw(self, value: ReadableBuffer) -> None: ... + raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise - # TODO These methods cannot be annotated correctly at the moment. + # TODO: These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT -- -2.39.3 (Apple Git-146) +2.49.0 diff --git a/misc/update-stubinfo.py b/misc/update-stubinfo.py new file mode 100644 index 0000000000000..4a5b9a40c408c --- /dev/null +++ b/misc/update-stubinfo.py @@ -0,0 +1,67 @@ +import argparse +from pathlib import Path + +import tomli as tomllib + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--typeshed", type=Path, required=True) + args = parser.parse_args() + + typeshed_p_to_d = {} + for stub in (args.typeshed / "stubs").iterdir(): + if not stub.is_dir(): + continue + try: + metadata = tomllib.loads((stub / "METADATA.toml").read_text()) + except FileNotFoundError: + continue + d = metadata.get("stub_distribution", f"types-{stub.name}") + for p in stub.iterdir(): + if not p.stem.isidentifier(): + continue + if p.is_dir() and not any(f.suffix == ".pyi" for f in p.iterdir()): + # ignore namespace packages + continue + if p.is_file() and p.suffix != ".pyi": + continue + typeshed_p_to_d[p.stem] = d + + import mypy.stubinfo + + mypy_p = set(mypy.stubinfo.non_bundled_packages_flat) | set( + mypy.stubinfo.legacy_bundled_packages + ) + + for p in typeshed_p_to_d.keys() & mypy_p: + mypy_d = mypy.stubinfo.non_bundled_packages_flat.get(p) + mypy_d = mypy_d or mypy.stubinfo.legacy_bundled_packages.get(p) + if mypy_d != typeshed_p_to_d[p]: + raise ValueError( + f"stub_distribution mismatch for {p}: {mypy_d} != {typeshed_p_to_d[p]}" + ) + + print("=" * 40) + print("Add the following to non_bundled_packages_flat:") + print("=" * 40) + for p in sorted(typeshed_p_to_d.keys() - mypy_p): + if p in { + "pika", # see comment in stubinfo.py + "distutils", # don't recommend types-setuptools here + }: + continue + print(f'"{p}": "{typeshed_p_to_d[p]}",') + print() + + print("=" * 40) + print("Consider removing the following packages no longer in typeshed:") + print("=" * 40) + for p in sorted(mypy_p - typeshed_p_to_d.keys()): + if p in {"lxml", "pandas", "scipy"}: # never in typeshed + continue + print(p) + + +if __name__ == "__main__": + main() diff --git a/misc/upload-pypi.py b/misc/upload-pypi.py index 9d8827c5e46c6..8ea86bbea584b 100644 --- a/misc/upload-pypi.py +++ b/misc/upload-pypi.py @@ -16,9 +16,10 @@ import tarfile import tempfile import venv +from collections.abc import Iterator from concurrent.futures import ThreadPoolExecutor from pathlib import Path -from typing import Any, Iterator +from typing import Any from urllib.request import urlopen BASE = "https://api.github.com/repos" @@ -26,17 +27,15 @@ def is_whl_or_tar(name: str) -> bool: - return name.endswith(".tar.gz") or name.endswith(".whl") + return name.endswith((".tar.gz", ".whl")) def item_ok_for_pypi(name: str) -> bool: if not is_whl_or_tar(name): return False - if name.endswith(".tar.gz"): - name = name[:-7] - if name.endswith(".whl"): - name = name[:-4] + name = name.removesuffix(".tar.gz") + name = name.removesuffix(".whl") if name.endswith("wasm32"): return False @@ -109,7 +108,7 @@ def tmp_twine() -> Iterator[Path]: def upload_dist(dist: Path, dry_run: bool = True) -> None: with tmp_twine() as twine: files = [item for item in dist.iterdir() if item_ok_for_pypi(item.name)] - cmd: list[Any] = [twine, "upload"] + cmd: list[Any] = [twine, "upload", "--skip-existing"] cmd += files if dry_run: print("[dry run] " + " ".join(map(str, cmd))) @@ -122,8 +121,7 @@ def upload_to_pypi(version: str, dry_run: bool = True) -> None: assert re.match(r"v?[1-9]\.[0-9]+\.[0-9](\+\S+)?$", version) if "dev" in version: assert dry_run, "Must use --dry-run with dev versions of mypy" - if version.startswith("v"): - version = version[1:] + version = version.removeprefix("v") target_dir = tempfile.mkdtemp() dist = Path(target_dir) / "dist" diff --git a/mypy-requirements.txt b/mypy-requirements.txt index 8d41a3fc70033..6984d9a5d070c 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -2,4 +2,6 @@ # and the pins in setup.py typing_extensions>=4.6.0 mypy_extensions>=1.0.0 +pathspec>=0.9.0 tomli>=1.1.0; python_version<'3.11' +librt>=0.6.2; platform_python_implementation != 'PyPy' diff --git a/mypy/applytype.py b/mypy/applytype.py index e88947cc64304..dfeaf7752d211 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Callable, Iterable, Sequence +from collections.abc import Iterable, Sequence +from typing import Callable import mypy.subtypes from mypy.erasetype import erase_typevars @@ -242,7 +243,7 @@ def visit_callable_type(self, t: CallableType) -> Type: self.bound_tvars -= set(found_vars) assert isinstance(result, ProperType) and isinstance(result, CallableType) - result.variables = list(result.variables) + found_vars + result.variables = result.variables + tuple(found_vars) return result def visit_type_var(self, t: TypeVarType) -> Type: diff --git a/mypy/argmap.py b/mypy/argmap.py index e6700c9f10928..a3e8f7fc8c2e1 100644 --- a/mypy/argmap.py +++ b/mypy/argmap.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Callable from mypy import nodes from mypy.maptype import map_instance_to_supertype @@ -77,7 +78,7 @@ def map_actuals_to_formals( elif actual_kind.is_named(): assert actual_names is not None, "Internal error: named kinds without names given" name = actual_names[ai] - if name in formal_names: + if name in formal_names and formal_kinds[formal_names.index(name)] != nodes.ARG_STAR: formal_to_actual[formal_names.index(name)].append(ai) elif nodes.ARG_STAR2 in formal_kinds: formal_to_actual[formal_kinds.index(nodes.ARG_STAR2)].append(ai) @@ -166,7 +167,7 @@ def __init__(self, context: ArgumentInferContext) -> None: # Next tuple *args index to use. self.tuple_index = 0 # Keyword arguments in TypedDict **kwargs used. - self.kwargs_used: set[str] = set() + self.kwargs_used: set[str] | None = None # Type context for `*` and `**` arg kinds. self.context = context @@ -219,7 +220,7 @@ def expand_actual_type( self.tuple_index += 1 item = actual_type.items[self.tuple_index - 1] if isinstance(item, UnpackType) and not allow_unpack: - # An upack item that doesn't have special handling, use upper bound as above. + # An unpack item that doesn't have special handling, use upper bound as above. unpacked = get_proper_type(item.type) if isinstance(unpacked, TypeVarTupleType): fallback = get_proper_type(unpacked.upper_bound) @@ -240,6 +241,8 @@ def expand_actual_type( from mypy.subtypes import is_subtype if isinstance(actual_type, TypedDictType): + if self.kwargs_used is None: + self.kwargs_used = set() if formal_kind != nodes.ARG_STAR2 and formal_name in actual_type.items: # Lookup type based on keyword argument name. assert formal_name is not None @@ -248,10 +251,8 @@ def expand_actual_type( formal_name = (set(actual_type.items.keys()) - self.kwargs_used).pop() self.kwargs_used.add(formal_name) return actual_type.items[formal_name] - elif ( - isinstance(actual_type, Instance) - and len(actual_type.args) > 1 - and is_subtype(actual_type, self.context.mapping_type) + elif isinstance(actual_type, Instance) and is_subtype( + actual_type, self.context.mapping_type ): # Only `Mapping` type can be unpacked with `**`. # Other types will produce an error somewhere else. diff --git a/mypy/binder.py b/mypy/binder.py index 9d0a33b54bc29..a83e65276ff43 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -1,15 +1,26 @@ from __future__ import annotations from collections import defaultdict +from collections.abc import Iterator from contextlib import contextmanager -from typing import DefaultDict, Iterator, List, Optional, Tuple, Union, cast +from typing import NamedTuple, Optional, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values -from mypy.join import join_simple -from mypy.literals import Key, literal, literal_hash, subkeys -from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var +from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash, subkeys +from mypy.nodes import ( + LITERAL_NO, + Expression, + IndexExpr, + MemberExpr, + NameExpr, + RefExpr, + TypeInfo, + Var, +) +from mypy.options import Options from mypy.subtypes import is_same_type, is_subtype +from mypy.typeops import make_simplified_union from mypy.types import ( AnyType, Instance, @@ -20,6 +31,7 @@ Type, TypeOfAny, TypeType, + TypeVarType, UnionType, UnpackType, find_unpack_in_list, @@ -30,21 +42,37 @@ 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 that point, arising either from assignments to those expressions - or the result of isinstance checks. It also records whether it is - possible to reach that point at all. + or the result of isinstance checks and other type narrowing + operations. It also records whether it is possible to reach that + point at all. + + We add a new frame wherenever there is a new scope or control flow + branching. This information is not copied into a new Frame when it is pushed onto the stack, so a given Frame only has information about types that were assigned in that frame. + + Expressions are stored in dicts using 'literal hashes' as keys (type + "Key"). These are hashable values derived from expression AST nodes + (only those that can be narrowed). literal_hash(expr) is used to + calculate the hashes. Note that this isn't directly related to literal + types -- the concept predates literal types. """ 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 @@ -53,35 +81,35 @@ def __repr__(self) -> str: return f"Frame({self.id}, {self.types}, {self.unreachable}, {self.conditional_frame})" -Assigns = DefaultDict[Expression, List[Tuple[Type, Optional[Type]]]] +Assigns = defaultdict[Expression, list[tuple[Type, Optional[Type]]]] class ConditionalTypeBinder: """Keep track of conditional types of variables. - NB: Variables are tracked by literal expression, so it is possible - to confuse the binder; for example, - - ``` - class A: - a: Union[int, str] = None - x = A() - lst = [x] - reveal_type(x.a) # Union[int, str] - x.a = 1 - reveal_type(x.a) # int - reveal_type(lst[0].a) # Union[int, str] - lst[0].a = 'a' - reveal_type(x.a) # int - reveal_type(lst[0].a) # str - ``` + NB: Variables are tracked by literal hashes of expressions, so it is + possible to confuse the binder when there is aliasing. Example: + + class A: + a: int | str + + x = A() + lst = [x] + reveal_type(x.a) # int | str + x.a = 1 + reveal_type(x.a) # int + reveal_type(lst[0].a) # int | str + lst[0].a = 'a' + reveal_type(x.a) # int + reveal_type(lst[0].a) # str """ # Stored assignments for situations with tuple/list lvalue and rvalue of union type. # This maps an expression to a list of bound types for every item in the union type. type_assignments: Assigns | None = None - def __init__(self) -> None: + def __init__(self, options: Options) -> None: + # Each frame gets an increasing, distinct id. self.next_id = 1 # The stack of frames currently used. These map @@ -109,10 +137,20 @@ def __init__(self) -> None: # Whether the last pop changed the newly top frame on exit self.last_pop_changed = False + # These are used to track control flow in try statements and loops. self.try_frames: set[int] = set() self.break_frames: list[int] = [] self.continue_frames: list[int] = [] + # If True, initial assignment to a simple variable (e.g. "x", but not "x.y") + # is added to the binder. This allows more precise narrowing and more + # flexible inference of variable types (--allow-redefinition-new). + self.bind_all = options.allow_redefinition_new + + # This tracks any externally visible changes in binder to invalidate + # expression caches when needed. + self.version = 0 + def _get_id(self) -> int: self.next_id += 1 return self.next_id @@ -132,10 +170,11 @@ 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.version += 1 + 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 +182,20 @@ 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: + @classmethod + def can_put_directly(cls, expr: Expression) -> bool: + """Will `.put()` on this expression be successful? + + This is inlined in `.put()` because the logic is rather hot and must be kept + in sync. + """ + return isinstance(expr, (IndexExpr, MemberExpr, NameExpr)) and literal(expr) > LITERAL_NO + + def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> None: + """Directly set the narrowed type of expression (if it supports it). + + This is used for isinstance() etc. Assignments should go through assign_type(). + """ if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): return if not literal(expr): @@ -153,9 +205,10 @@ 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.version += 1 self.frames[-1].unreachable = True def suppress_unreachable_warnings(self) -> None: @@ -164,7 +217,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,31 +249,66 @@ 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. """ - - frames = [f for f in frames if not f.unreachable] + all_reachable = all(not f.unreachable for f in frames) + if not all_reachable: + frames = [f for f in frames if not f.unreachable] changed = False - keys = {key for f in frames for key in f.types} - + keys = [key for f in frames for key in f.types] + if len(keys) > 1: + keys = list(set(keys)) for key in keys: current_value = self._get(key) resulting_values = [f.types.get(key, current_value) for f in frames] - if any(x is None for x in resulting_values): + # Keys can be narrowed using two different semantics. The new semantics + # is enabled for plain variables when bind_all is true, and it allows + # variable types to be widened using subsequent assignments. This is + # tricky to support for instance attributes (primarily due to deferrals), + # so we don't use it for them. + old_semantics = not self.bind_all or extract_var_from_literal_hash(key) is None + if old_semantics and any(x is None for x in resulting_values): # We didn't know anything about key before # (current_value must be None), and we still don't # know anything about key in at least one possible frame. continue - type = resulting_values[0] - assert type is not None + resulting_values = [x for x in resulting_values if x 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) + possible_types = [] + for t in resulting_values: + assert t is not None + possible_types.append(t.type) + if len(possible_types) == 1: + # This is to avoid calling get_proper_type() unless needed, as this may + # interfere with our (hacky) TypeGuard support. + type = possible_types[0] + else: + type = make_simplified_union(possible_types) + # Legacy guard for corner case when the original type is TypeVarType. + if isinstance(declaration_type, TypeVarType) and not is_subtype( + type, declaration_type + ): + type = declaration_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: @@ -228,7 +319,11 @@ def update_from_options(self, frames: list[Frame]) -> bool: # still equivalent to such type). if isinstance(type, UnionType): type = collapse_variadic_union(type) - if isinstance(type, ProperType) and isinstance(type, UnionType): + if ( + old_semantics + and isinstance(type, ProperType) + and isinstance(type, UnionType) + ): # Simplify away any extra Any's that were added to the declared # type when popping a frame. simplified = UnionType.make_union( @@ -236,8 +331,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.type): + self._put(key, type, from_assignment=True) changed = True self.frames[-1].unreachable = not frames @@ -278,9 +373,14 @@ def accumulate_type_assignments(self) -> Iterator[Assigns]: yield self.type_assignments self.type_assignments = old_assignments - def assign_type( - self, expr: Expression, type: Type, declared_type: Type | None, restrict_any: bool = False - ) -> None: + def assign_type(self, expr: Expression, type: Type, declared_type: Type | None) -> None: + """Narrow type of expression through an assignment. + + Do nothing if the expression doesn't support narrowing. + + When not narrowing though an assignment (isinstance() etc.), use put() + directly. This omits some special-casing logic for assignments. + """ # We should erase last known value in binder, because if we are using it, # it means that the target is not final, and therefore can't hold a literal. type = remove_instance_last_known_values(type) @@ -311,41 +411,39 @@ def assign_type( p_declared = get_proper_type(declared_type) p_type = get_proper_type(type) - enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type)) - if isinstance(enclosing_type, AnyType) and not restrict_any: - # If x is Any and y is int, after x = y we do not infer that x is int. - # This could be changed. - # Instead, since we narrowed type from Any in a recent frame (probably an - # isinstance check), but now it is reassigned, we broaden back - # to Any (which is the most recent enclosing type) - self.put(expr, enclosing_type) - # As a special case, when assigning Any to a variable with a - # declared Optional type that has been narrowed to None, - # replace all the Nones in the declared Union type with Any. - # This overrides the normal behavior of ignoring Any assignments to variables - # in order to prevent false positives. - # (See discussion in #3526) - elif ( - isinstance(p_type, AnyType) - and isinstance(p_declared, UnionType) - and any(isinstance(get_proper_type(item), NoneType) for item in p_declared.items) - and isinstance( - get_proper_type(self.most_recent_enclosing_type(expr, NoneType())), NoneType - ) - ): - # Replace any Nones in the union type with Any - new_items = [ - type if isinstance(get_proper_type(item), NoneType) else item - for item in p_declared.items - ] - self.put(expr, UnionType(new_items)) - elif isinstance(p_type, AnyType) and not ( - isinstance(p_declared, UnionType) - and any(isinstance(get_proper_type(item), AnyType) for item in p_declared.items) - ): - # Assigning an Any value doesn't affect the type to avoid false negatives, unless - # there is an Any item in a declared union type. - self.put(expr, declared_type) + if isinstance(p_type, AnyType): + # Any type requires some special casing, for both historical reasons, + # and to optimise user experience without sacrificing correctness too much. + if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred: + # First case: a local/global variable without explicit annotation, + # in this case we just assign Any (essentially following the SSA logic). + self.put(expr, type) + elif isinstance(p_declared, UnionType) and any( + isinstance(get_proper_type(item), NoneType) for item in p_declared.items + ): + # Second case: explicit optional type, in this case we optimize for a common + # pattern when an untyped value used as a fallback replacing None. + new_items = [ + type if isinstance(get_proper_type(item), NoneType) else item + for item in p_declared.items + ] + self.put(expr, UnionType(new_items)) + elif isinstance(p_declared, UnionType) and any( + isinstance(get_proper_type(item), AnyType) for item in p_declared.items + ): + # Third case: a union already containing Any (most likely from an un-imported + # name), in this case we allow assigning Any as well. + self.put(expr, type) + else: + # In all other cases we don't narrow to Any to minimize false negatives. + self.put(expr, declared_type) + elif isinstance(p_declared, AnyType): + # Mirroring the first case above, we don't narrow to a precise type if the variable + # has an explicit `Any` type annotation. + if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred: + self.put(expr, type) + else: + self.put(expr, declared_type) else: self.put(expr, type) @@ -367,17 +465,6 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None: for dep in self.dependencies.get(key, set()): self._cleanse_key(dep) - def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type | None: - type = get_proper_type(type) - if isinstance(type, AnyType): - return get_declaration(expr) - 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]) - ] - return enclosers[-1] - def allow_jump(self, index: int) -> None: # self.frames and self.options_on_return have different lengths # so make sure the index is positive @@ -468,6 +555,11 @@ def top_frame_context(self) -> Iterator[Frame]: def get_declaration(expr: BindableExpression) -> Type | None: + """Get the declared or inferred type of a RefExpr expression. + + Return None if there is no type or the expression is not a RefExpr. + This can return None if the type hasn't been inferred yet. + """ if isinstance(expr, RefExpr): if isinstance(expr.node, Var): type = expr.node.type diff --git a/mypy/build.py b/mypy/build.py index 733f0685792ed..aee099fed316a 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -15,7 +15,6 @@ import collections import contextlib -import errno import gc import json import os @@ -25,31 +24,28 @@ import sys import time import types -from typing import ( - TYPE_CHECKING, - AbstractSet, - Any, - Callable, - ClassVar, - Dict, - Final, - Iterator, - Mapping, - NamedTuple, - NoReturn, - Sequence, - TextIO, -) -from typing_extensions import TypeAlias as _TypeAlias, TypedDict +from collections.abc import Iterator, Mapping, Sequence, Set as AbstractSet +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Final, NoReturn, TextIO, TypedDict +from typing_extensions import TypeAlias as _TypeAlias + +from librt.internal import cache_version import mypy.semanal_main +from mypy.cache import ( + CACHE_VERSION, + CacheMeta, + ReadBuffer, + SerializedError, + WriteBuffer, + write_json, +) from mypy.checker import TypeChecker from mypy.error_formatter import OUTPUT_CHOICES, ErrorFormatter -from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error +from mypy.errors import CompileError, ErrorInfo, Errors, ErrorTuple, report_internal_error from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort from mypy.indirection import TypeIndirectionVisitor from mypy.messages import MessageBuilder -from mypy.nodes import Import, ImportAll, ImportBase, ImportFrom, MypyFile, SymbolTable, TypeInfo +from mypy.nodes import Import, ImportAll, ImportBase, ImportFrom, MypyFile, SymbolTable from mypy.partially_defined import PossiblyUndefinedVariableVisitor from mypy.semanal import SemanticAnalyzer from mypy.semanal_pass1 import SemanticAnalyzerPreAnalysis @@ -58,8 +54,9 @@ decode_python_encoding, get_mypy_comments, hash_digest, + hash_digest_bytes, is_stub_package_file, - is_sub_path, + is_sub_path_normabs, is_typeshed_file, module_prefix, read_py_file, @@ -92,9 +89,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.types import Type +from mypy.stubinfo import is_module_from_legacy_bundled_package, stub_distribution_name +from mypy.types import Type, instance_cache 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 @@ -118,8 +116,32 @@ "abc", } +# We are careful now, we can increase this in future if safe/useful. +MAX_GC_FREEZE_CYCLES = 1 + +Graph: _TypeAlias = dict[str, "State"] + + +class SCC: + """A simple class that represents a strongly connected component (import cycle).""" + + id_counter: ClassVar[int] = 0 -Graph: _TypeAlias = Dict[str, "State"] + def __init__(self, ids: set[str]) -> None: + self.id = SCC.id_counter + SCC.id_counter += 1 + # Ids of modules in this cycle. + self.mod_ids = ids + # Direct dependencies, should be populated by the caller. + self.deps: set[int] = set() + # Direct dependencies that have not been processed yet. + # Should be populated by the caller. This set may change during graph + # processing, while the above stays constant. + self.not_ready_deps: set[int] = set() + # SCCs that (directly) depend on this SCC. Note this is a list to + # make processing order more predictable. Dependents will be notified + # that they may be ready in the order in this list. + self.direct_dependents: list[int] = [] # TODO: Get rid of BuildResult. We might as well return a BuildManager. @@ -143,6 +165,10 @@ def __init__(self, manager: BuildManager, graph: Graph) -> None: self.errors: list[str] = [] # Filled in by build if desired +def build_error(msg: str) -> NoReturn: + raise CompileError([f"mypy: error: {msg}"]) + + def build( sources: list[BuildSource], options: Options, @@ -179,6 +205,9 @@ def build( # fields for callers that want the traditional API. messages = [] + # This is mostly for the benefit of tests that use builtins fixtures. + instance_cache.reset() + def default_flush_errors( filename: str | None, new_messages: list[str], is_serious: bool ) -> None: @@ -196,7 +225,7 @@ def default_flush_errors( result.errors = messages return result except CompileError as e: - # CompileErrors raised from an errors object carry all of the + # CompileErrors raised from an errors object carry all the # messages that have not been reported out by error streaming. # Patch it up to contain either none or all none of the messages, # depending on whether we are flushing errors. @@ -217,8 +246,9 @@ def _build( extra_plugins: Sequence[Plugin], ) -> BuildResult: if platform.python_implementation() == "CPython": - # This seems the most reasonable place to tune garbage collection. - gc.set_threshold(150 * 1000) + # Run gc less frequently, as otherwise we can spent a large fraction of + # cpu in gc. This seems the most reasonable place to tune garbage collection. + gc.set_threshold(200 * 1000, 30, 30) data_dir = default_data_dir() fscache = fscache or FileSystemCache() @@ -237,6 +267,9 @@ def _build( errors = Errors(options, read_source=lambda path: read_py_file(path, cached_read)) plugin, snapshot = load_plugins(options, errors, stdout, extra_plugins) + # Validate error codes after plugins are loaded. + options.process_error_codes(error_callback=build_error) + # Add catch-all .gitignore to cache dir if we created it cache_dir_existed = os.path.isdir(options.cache_dir) @@ -316,26 +349,6 @@ def normpath(path: str, options: Options) -> str: return os.path.abspath(path) -class CacheMeta(NamedTuple): - id: str - path: str - mtime: int - size: int - hash: str - dependencies: list[str] # names of imported modules - data_mtime: int # mtime of data_json - data_json: str # path of .data.json - suppressed: list[str] # dependencies that weren't imported - options: dict[str, object] | None # build options - # dep_prios and dep_lines are in parallel with dependencies + suppressed - dep_prios: list[int] - dep_lines: list[int] - interface_hash: str # hash representing the public interface - version_id: str # mypy version for cache invalidation - ignore_all: bool # if errors were ignored - plugin_data: Any # config data from plugins - - # NOTE: dependencies + suppressed == all reachable imports; # suppressed contains those reachable imports that were prevented by # silent mode or simply not found. @@ -347,34 +360,6 @@ class FgDepMeta(TypedDict): mtime: int -def cache_meta_from_dict(meta: dict[str, Any], data_json: str) -> CacheMeta: - """Build a CacheMeta object from a json metadata dictionary - - Args: - meta: JSON metadata read from the metadata cache file - data_json: Path to the .data.json file containing the AST trees - """ - sentinel: Any = None # Values to be validated by the caller - return CacheMeta( - meta.get("id", sentinel), - meta.get("path", sentinel), - int(meta["mtime"]) if "mtime" in meta else sentinel, - meta.get("size", sentinel), - meta.get("hash", sentinel), - meta.get("dependencies", []), - int(meta["data_mtime"]) if "data_mtime" in meta else sentinel, - data_json, - meta.get("suppressed", []), - meta.get("options"), - meta.get("dep_prios", []), - meta.get("dep_lines", []), - meta.get("interface_hash", ""), - meta.get("version_id", sentinel), - meta.get("ignore_all", True), - meta.get("plugin_data", None), - ) - - # Priorities used for imports. (Here, top-level includes inside a class.) # These are used to determine a more predictable order in which the # nodes in an import cycle are processed. @@ -625,6 +610,7 @@ def __init__( self.options = options self.version_id = version_id self.modules: dict[str, MypyFile] = {} + self.import_map: dict[str, set[str]] = {} self.missing_modules: set[str] = set() self.fg_deps_meta: dict[str, FgDepMeta] = {} # fg_deps holds the dependencies of every module that has been @@ -645,6 +631,7 @@ def __init__( self.incomplete_namespaces, self.errors, self.plugin, + self.import_map, ) self.all_types: dict[Expression, Type] = {} # Enabled by export_types self.indirection_detector = TypeIndirectionVisitor() @@ -664,7 +651,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?"] @@ -708,6 +695,20 @@ def __init__( # new file can be processed O(n**2) times. This cache # avoids most of this redundant work. self.ast_cache: dict[str, tuple[MypyFile, list[ErrorInfo]]] = {} + # Number of times we used GC optimization hack for fresh SCCs. + self.gc_freeze_cycles = 0 + # Mapping from SCC id to corresponding SCC instance. This is populated + # in process_graph(). + self.scc_by_id: dict[int, SCC] = {} + # Global topological order for SCCs. This exists to make order of processing + # SCCs more predictable. + self.top_order: list[int] = [] + # Stale SCCs that are queued for processing. Note that as of now we have just + # one worker, that is the same process. In the future, we will support multiple + # parallel worker processes. + self.scc_queue: list[SCC] = [] + # SCCs that have been fully processed. + self.done_sccs: set[int] = set() def dump_stats(self) -> None: if self.options.dump_build_stats: @@ -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. @@ -750,6 +751,26 @@ def getmtime(self, path: str) -> int: else: return int(self.metastore.getmtime(path)) + def correct_rel_imp(self, file: MypyFile, imp: ImportFrom | ImportAll) -> str: + """Function to correct for relative imports.""" + file_id = file.fullname + rel = imp.relative + if rel == 0: + return imp.id + if os.path.basename(file.path).startswith("__init__."): + rel -= 1 + if rel != 0: + file_id = ".".join(file_id.split(".")[:-rel]) + new_id = file_id + "." + imp.id if imp.id else file_id + + if not new_id: + self.errors.set_file(file.path, file.name, self.options) + self.errors.report( + imp.line, 0, "No parent module -- cannot perform relative import", blocker=True + ) + + return new_id + def all_imported_modules_in_file(self, file: MypyFile) -> list[tuple[int, str, int]]: """Find all reachable import statements in a file. @@ -758,27 +779,6 @@ def all_imported_modules_in_file(self, file: MypyFile) -> list[tuple[int, str, i Can generate blocking errors on bogus relative imports. """ - - def correct_rel_imp(imp: ImportFrom | ImportAll) -> str: - """Function to correct for relative imports.""" - file_id = file.fullname - rel = imp.relative - if rel == 0: - return imp.id - if os.path.basename(file.path).startswith("__init__."): - rel -= 1 - if rel != 0: - file_id = ".".join(file_id.split(".")[:-rel]) - new_id = file_id + "." + imp.id if imp.id else file_id - - if not new_id: - self.errors.set_file(file.path, file.name, self.options) - self.errors.report( - imp.line, 0, "No parent module -- cannot perform relative import", blocker=True - ) - - return new_id - res: list[tuple[int, str, int]] = [] for imp in file.imports: if not imp.is_unreachable: @@ -793,7 +793,7 @@ def correct_rel_imp(imp: ImportFrom | ImportAll) -> str: ancestors.append(part) res.append((ancestor_pri, ".".join(ancestors), imp.line)) elif isinstance(imp, ImportFrom): - cur_id = correct_rel_imp(imp) + cur_id = self.correct_rel_imp(file, imp) all_are_submodules = True # Also add any imported names that are submodules. pri = import_priority(imp, PRI_MED) @@ -803,17 +803,17 @@ def correct_rel_imp(imp: ImportFrom | ImportAll) -> str: res.append((pri, sub_id, imp.line)) else: all_are_submodules = False - # Add cur_id as a dependency, even if all of the + # Add cur_id as a dependency, even if all the # imports are submodules. Processing import from will try # to look through cur_id, so we should depend on it. - # As a workaround for for some bugs in cycle handling (#4498), - # if all of the imports are submodules, do the import at a lower + # As a workaround for some bugs in cycle handling (#4498), + # if all the imports are submodules, do the import at a lower # priority. pri = import_priority(imp, PRI_HIGH if not all_are_submodules else PRI_LOW) res.append((pri, cur_id, imp.line)) elif isinstance(imp, ImportAll): pri = import_priority(imp, PRI_HIGH) - res.append((pri, correct_rel_imp(imp), imp.line)) + res.append((pri, self.correct_rel_imp(file, imp), imp.line)) # Sort such that module (e.g. foo.bar.baz) comes before its ancestors (e.g. foo # and foo.bar) so that, if FindModuleCache finds the target module in a @@ -858,7 +858,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()} @@ -883,8 +883,6 @@ def log(self, *message: str) -> None: self.stderr.flush() def log_fine_grained(self, *message: str) -> None: - import mypy.build - if self.verbosity() >= 1: self.log("fine-grained:", *message) elif mypy.build.DEBUG_FINE_GRAINED: @@ -910,9 +908,26 @@ def add_stats(self, **kwds: Any) -> None: def stats_summary(self) -> Mapping[str, object]: return self.stats + def submit(self, sccs: list[SCC]) -> None: + """Submit a stale SCC for processing in current process.""" + self.scc_queue.extend(sccs) + + def wait_for_done(self, graph: Graph) -> tuple[list[SCC], bool]: + """Wait for a stale SCC processing (in process) to finish. + + Return next processed SCC and whether we have more in the queue. + This emulates the API we will have for parallel processing + in multiple worker processes. + """ + if not self.scc_queue: + return [], False + next_scc = self.scc_queue.pop(0) + process_stale_scc(graph, next_scc, self) + return [next_scc], bool(self.scc_queue) + -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 @@ -930,7 +945,7 @@ def write_deps_cache( ) -> None: """Write cache files for fine-grained dependencies. - Serialize fine-grained dependencies map for fine grained mode. + Serialize fine-grained dependencies map for fine-grained mode. Dependencies on some module 'm' is stored in the dependency cache file m.deps.json. This entails some spooky action at a distance: @@ -944,7 +959,7 @@ def write_deps_cache( fine-grained dependencies in a global cache file: * We take a snapshot of current sources to later check consistency between the fine-grained dependency cache and module cache metadata - * We store the mtime of all of the dependency files to verify they + * We store the mtime of all the dependency files to verify they haven't changed """ metastore = manager.metastore @@ -974,13 +989,15 @@ def write_deps_cache( if st.source_hash: hash = st.source_hash else: - assert st.meta, "Module must be either parsed or cached" - hash = st.meta.hash + if st.meta: + hash = st.meta.hash + else: + hash = "" meta_snapshot[id] = hash 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 +1065,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) @@ -1065,7 +1085,7 @@ def read_plugins_snapshot(manager: BuildManager) -> dict[str, str] | None: if snapshot is None: return None if not isinstance(snapshot, dict): - manager.log(f"Could not load plugins snapshot: cache is not a dict: {type(snapshot)}") + manager.log(f"Could not load plugins snapshot: cache is not a dict: {type(snapshot)}") # type: ignore[unreachable] return None return snapshot @@ -1079,8 +1099,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(): @@ -1107,7 +1127,7 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta] if deps_meta is None: return None meta_snapshot = deps_meta["snapshot"] - # Take a snapshot of the source hashes from all of the metas we found. + # Take a snapshot of the source hashes from all the metas we found. # (Including the ones we rejected because they were out of date.) # We use this to verify that they match up with the proto_deps. current_meta_snapshot = { @@ -1135,6 +1155,17 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta] return module_deps_metas +def _load_ff_file(file: str, manager: BuildManager, log_error: str) -> bytes | None: + t0 = time.time() + try: + data = manager.metastore.read(file) + except OSError: + manager.log(log_error + file) + return None + manager.add_stats(metastore_read_time=time.time() - t0) + return data + + def _load_json_file( file: str, manager: BuildManager, log_success: str, log_error: str ) -> dict[str, Any] | None: @@ -1148,11 +1179,11 @@ 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) - manager.add_stats(data_json_load_time=time.time() - t1) + result = json_loads(data) + manager.add_stats(data_file_load_time=time.time() - t1) except json.JSONDecodeError: manager.errors.set_file(file, None, manager.options) manager.errors.report( @@ -1228,12 +1259,11 @@ def get_cache_names(id: str, path: str, options: Options) -> tuple[str, str, str Args: id: module ID path: module path - cache_dir: cache directory - pyversion: Python version (major, minor) + options: build options Returns: - A tuple with the file names to be used for the meta JSON, the - data JSON, and the fine-grained deps JSON, respectively. + A tuple with the file names to be used for the meta file, the + data file, and the fine-grained deps JSON, respectively. """ if options.cache_map: pair = options.cache_map.get(normpath(path, options)) @@ -1246,7 +1276,7 @@ def get_cache_names(id: str, path: str, options: Options) -> tuple[str, str, str # Solve this by rewriting the paths as relative to the root dir. # This only makes sense when using the filesystem backed cache. root = _cache_dir_prefix(options) - return (os.path.relpath(pair[0], root), os.path.relpath(pair[1], root), None) + return os.path.relpath(pair[0], root), os.path.relpath(pair[1], root), None prefix = os.path.join(*id.split(".")) is_package = os.path.basename(path).startswith("__init__.py") if is_package: @@ -1255,7 +1285,26 @@ def get_cache_names(id: str, path: str, options: Options) -> tuple[str, str, str deps_json = None if options.cache_fine_grained: deps_json = prefix + ".deps.json" - return (prefix + ".meta.json", prefix + ".data.json", deps_json) + if options.fixed_format_cache: + data_suffix = ".data.ff" + meta_suffix = ".meta.ff" + else: + data_suffix = ".data.json" + meta_suffix = ".meta.json" + return prefix + meta_suffix, prefix + data_suffix, deps_json + + +def options_snapshot(id: str, manager: BuildManager) -> dict[str, object]: + """Make compact snapshot of options for a module. + + Separately store only the options we may compare individually, and take a hash + of everything else. If --debug-cache is specified, fall back to full snapshot. + """ + snapshot = manager.options.clone_for_module(id).select_options_affecting_cache() + if manager.options.debug_cache: + return snapshot + platform_opt = snapshot.pop("platform") + return {"platform": platform_opt, "other_options": hash_digest(json_dumps(snapshot))} def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | None: @@ -1271,53 +1320,62 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No valid; otherwise None. """ # TODO: May need to take more build options into account - meta_json, data_json, _ = get_cache_names(id, path, manager.options) - manager.trace(f"Looking for {id} at {meta_json}") + meta_file, data_file, _ = get_cache_names(id, path, manager.options) + manager.trace(f"Looking for {id} at {meta_file}") + meta: bytes | dict[str, Any] | None t0 = time.time() - meta = _load_json_file( - meta_json, manager, log_success=f"Meta {id} ", log_error=f"Could not load cache for {id}: " - ) + if manager.options.fixed_format_cache: + meta = _load_ff_file(meta_file, manager, log_error=f"Could not load cache for {id}: ") + if meta is None: + return None + else: + meta = _load_json_file( + meta_file, + manager, + log_success=f"Meta {id} ", + log_error=f"Could not load cache for {id}: ", + ) + if meta is None: + return None + if not isinstance(meta, dict): + manager.log( # type: ignore[unreachable] + f"Could not load cache for {id}: meta cache is not a dict: {repr(meta)}" + ) + return None t1 = time.time() - if meta is None: - return None - if not isinstance(meta, dict): - manager.log(f"Could not load cache for {id}: meta cache is not a dict: {repr(meta)}") + if isinstance(meta, bytes): + # If either low-level buffer format or high-level cache layout changed, we + # cannot use the cache files, even with --skip-version-check. + # TODO: switch to something like librt.internal.read_byte() if this is slow. + if meta[0] != cache_version() or meta[1] != CACHE_VERSION: + manager.log(f"Metadata abandoned for {id}: incompatible cache format") + return None + data_io = ReadBuffer(meta[2:]) + m = CacheMeta.read(data_io, data_file) + else: + m = CacheMeta.deserialize(meta, data_file) + if m is None: + manager.log(f"Metadata abandoned for {id}: cannot deserialize data") return None - m = cache_meta_from_dict(meta, data_json) t2 = time.time() manager.add_stats( load_meta_time=t2 - t0, load_meta_load_time=t1 - t0, load_meta_from_dict_time=t2 - t1 ) - # Don't check for path match, that is dealt with in validate_meta(). - # - # TODO: these `type: ignore`s wouldn't be necessary - # if the type annotations for CacheMeta were more accurate - # (all of these attributes can be `None`) - if ( - m.id != id - or m.mtime is None # type: ignore[redundant-expr] - or m.size is None # type: ignore[redundant-expr] - or m.dependencies is None # type: ignore[redundant-expr] - or m.data_mtime is None - ): - manager.log(f"Metadata abandoned for {id}: attributes are missing") + # Ignore cache if generated by an older mypy version. + if m.version_id != manager.version_id and not manager.options.skip_version_check: + manager.log(f"Metadata abandoned for {id}: different mypy version") return None - # Ignore cache if generated by an older mypy version. - if ( - (m.version_id != manager.version_id and not manager.options.skip_version_check) - or m.options is None - or len(m.dependencies) + len(m.suppressed) != len(m.dep_prios) - or len(m.dependencies) + len(m.suppressed) != len(m.dep_lines) - ): - manager.log(f"Metadata abandoned for {id}: new attributes are missing") + total_deps = len(m.dependencies) + len(m.suppressed) + if len(m.dep_prios) != total_deps or len(m.dep_lines) != total_deps: + manager.log(f"Metadata abandoned for {id}: broken dependencies") return None # Ignore cache if (relevant) options aren't the same. # Note that it's fine to mutilate cached_options since it's only used here. cached_options = m.options - current_options = manager.options.clone_for_module(id).select_options_affecting_cache() + current_options = options_snapshot(id, manager) if manager.options.skip_version_check: # When we're lax about version we're also lax about platform. cached_options["platform"] = current_options["platform"] @@ -1343,8 +1401,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") @@ -1381,11 +1439,11 @@ def validate_meta( bazel = manager.options.bazel assert path is not None, "Internal error: meta was provided without a path" if not manager.options.skip_cache_mtime_checks: - # Check data_json; assume if its mtime matches it's good. + # Check data_file; assume if its mtime matches it's good. try: - data_mtime = manager.getmtime(meta.data_json) + data_mtime = manager.getmtime(meta.data_file) except OSError: - manager.log(f"Metadata abandoned for {id}: failed to stat data_json") + manager.log(f"Metadata abandoned for {id}: failed to stat data_file") return None if data_mtime != meta.data_mtime: manager.log(f"Metadata abandoned for {id}: data cache is modified") @@ -1394,9 +1452,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") @@ -1436,7 +1494,8 @@ def validate_meta( qmtime, qsize, qhash = manager.quickstart_state[path] if int(qmtime) == mtime and qsize == size and qhash == meta.hash: manager.log(f"Metadata fresh (by quickstart) for {id}: file {path}") - meta = meta._replace(mtime=mtime, path=path) + meta.mtime = mtime + meta.path = path return meta t0 = time.time() @@ -1459,37 +1518,18 @@ def validate_meta( else: t0 = time.time() # Optimization: update mtime and path (otherwise, this mismatch will reappear). - meta = meta._replace(mtime=mtime, path=path) - # Construct a dict we can pass to json.dumps() (compare to write_cache()). - meta_dict = { - "id": id, - "path": path, - "mtime": mtime, - "size": size, - "hash": source_hash, - "data_mtime": meta.data_mtime, - "dependencies": meta.dependencies, - "suppressed": meta.suppressed, - "options": (manager.options.clone_for_module(id).select_options_affecting_cache()), - "dep_prios": meta.dep_prios, - "dep_lines": meta.dep_lines, - "interface_hash": meta.interface_hash, - "version_id": manager.version_id, - "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_json, _, _ = get_cache_names(id, path, manager.options) + meta.mtime = mtime + meta.path = path + meta.size = size + meta.options = options_snapshot(id, manager) + meta_file, _, _ = get_cache_names(id, path, manager.options) manager.log( "Updating mtime for {}: file {}, meta {}, mtime {}".format( - id, path, meta_json, meta.mtime + id, path, meta_file, meta.mtime ) ) + write_cache_meta(meta, manager, meta_file) t1 = time.time() - manager.metastore.write(meta_json, meta_str) # Ignore errors, just an optimization. manager.add_stats(validate_update_time=time.time() - t1, validate_munging_time=t1 - t0) return meta @@ -1507,13 +1547,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, @@ -1522,11 +1555,11 @@ def write_cache( suppressed: list[str], dep_prios: list[int], dep_lines: list[int], - old_interface_hash: str, + old_interface_hash: bytes, source_hash: str, ignore_all: bool, manager: BuildManager, -) -> tuple[str, CacheMeta | None]: +) -> tuple[bytes, tuple[CacheMeta, str] | None]: """Write cache files for a module. Note that this mypy's behavior is still correct when any given @@ -1547,38 +1580,42 @@ def write_cache( manager: the build manager (for pyversion, log/trace) Returns: - A tuple containing the interface hash and CacheMeta - corresponding to the metadata that was written (the latter may - be None if the cache could not be written). + A tuple containing the interface hash and inner tuple with CacheMeta + that should be written and path to cache file (inner tuple may be None, + if the cache data could not be written). """ metastore = manager.metastore # For Bazel we use relative paths and zero mtimes. bazel = manager.options.bazel # Obtain file paths. - meta_json, data_json, _ = get_cache_names(id, path, manager.options) - manager.log(f"Writing {id} {path} {meta_json} {data_json}") + meta_file, data_file, _ = get_cache_names(id, path, manager.options) + manager.log(f"Writing {id} {path} {meta_file} {data_file}") # Update tree.path so that in bazel mode it's made relative (since # sometimes paths leak out). if bazel: tree.path = path - # Serialize data and analyze interface - data = tree.serialize() - data_str = json_dumps(data, manager.options.debug_cache) - interface_hash = compute_hash(data_str) - plugin_data = manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=False)) + # Serialize data and analyze interface + if manager.options.fixed_format_cache: + data_io = WriteBuffer() + tree.write(data_io) + data_bytes = data_io.getvalue() + else: + data = tree.serialize() + data_bytes = json_dumps(data, manager.options.debug_cache) + interface_hash = hash_digest_bytes(data_bytes + json_dumps(plugin_data)) + # 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]: + for filename in [data_file, meta_file]: try: os.remove(filename) except OSError: @@ -1592,10 +1629,10 @@ 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_file, 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}") + manager.log(f"Error writing cache data file {data_file}") # Let's continue without writing the meta file. Analysis: # If the replace failed, we've changed nothing except left # behind an extraneous temporary file; if the replace @@ -1607,9 +1644,9 @@ def write_cache( return interface_hash, None try: - data_mtime = manager.getmtime(data_json) + data_mtime = manager.getmtime(data_file) except OSError: - manager.log(f"Error in os.stat({data_json!r}), skipping cache write") + manager.log(f"Error in os.stat({data_file!r}), skipping cache write") return interface_hash, None mtime = 0 if bazel else int(st.st_mtime) @@ -1619,57 +1656,48 @@ def write_cache( # updates made by inline config directives in the file. This is # important, or otherwise the options would never match when # verifying the cache. - options = manager.options.clone_for_module(id) assert source_hash is not None - meta = { - "id": id, - "path": path, - "mtime": mtime, - "size": size, - "hash": source_hash, - "data_mtime": data_mtime, - "dependencies": dependencies, - "suppressed": suppressed, - "options": options.select_options_affecting_cache(), - "dep_prios": dep_prios, - "dep_lines": dep_lines, - "interface_hash": interface_hash, - "version_id": manager.version_id, - "ignore_all": ignore_all, - "plugin_data": plugin_data, - } + meta = CacheMeta( + id=id, + path=path, + mtime=mtime, + size=size, + hash=source_hash, + dependencies=dependencies, + data_mtime=data_mtime, + data_file=data_file, + suppressed=suppressed, + options=options_snapshot(id, manager), + dep_prios=dep_prios, + dep_lines=dep_lines, + interface_hash=interface_hash, + version_id=manager.version_id, + ignore_all=ignore_all, + plugin_data=plugin_data, + # These two will be filled by the caller. + dep_hashes=[], + error_lines=[], + ) + return interface_hash, (meta, meta_file) + +def write_cache_meta(meta: CacheMeta, manager: BuildManager, meta_file: str) -> None: # Write meta cache file - meta_str = json_dumps(meta, manager.options.debug_cache) - if not metastore.write(meta_json, meta_str): + metastore = manager.metastore + if manager.options.fixed_format_cache: + data_io = WriteBuffer() + meta.write(data_io) + # Prefix with both low- and high-level cache format versions for future validation. + # TODO: switch to something like librt.internal.write_byte() if this is slow. + meta_bytes = bytes([cache_version(), CACHE_VERSION]) + data_io.getvalue() + else: + meta_dict = meta.serialize() + meta_bytes = json_dumps(meta_dict, manager.options.debug_cache) + if not metastore.write(meta_file, meta_bytes): # Most likely the error is the replace() call # (see https://github.com/python/mypy/issues/3215). # The next run will simply find the cache entry out of date. - manager.log(f"Error writing meta JSON file {meta_json}") - - return interface_hash, cache_meta_from_dict(meta, data_json) - - -def delete_cache(id: str, path: str, manager: BuildManager) -> None: - """Delete cache files for a module. - - The cache files for a module are deleted when mypy finds errors there. - This avoids inconsistent states with cache files from different mypy runs, - see #4043 for an example. - """ - # We don't delete .deps files on errors, since the dependencies - # are mostly generated from other files and the metadata is - # tracked separately. - meta_path, data_path, _ = get_cache_names(id, path, manager.options) - cache_paths = [meta_path, data_path] - manager.log(f"Deleting {id} {path} {' '.join(x for x in cache_paths if x)}") - - for filename in cache_paths: - try: - manager.metastore.remove(filename) - except OSError as e: - if e.errno != errno.ENOENT: - manager.log(f"Error deleting cache file {filename}: {e.strerror}") + manager.log(f"Error writing cache meta file {meta_file}") """Dependency manager. @@ -1737,26 +1765,24 @@ def delete_cache(id: str, path: str, manager: BuildManager) -> None: For single nodes, processing is simple. If the node was cached, we deserialize the cache data and fix up cross-references. Otherwise, we -do semantic analysis followed by type checking. We also handle (c) -above; if a module has valid cache data *but* any of its -dependencies was processed from source, then the module should be -processed from source. - -A relatively simple optimization (outside SCCs) we might do in the -future is as follows: if a node's cache data is valid, but one or more -of its dependencies are out of date so we have to re-parse the node -from source, once we have fully type-checked the node, we can decide -whether its symbol table actually changed compared to the cache data -(by reading the cache data and comparing it to the data we would be -writing). If there is no change we can declare the node up to date, -and any node that depends (and for which we have cached data, and -whose other dependencies are up to date) on it won't need to be -re-parsed from source. +do semantic analysis followed by type checking. Once we (re-)processed +an SCC we check whether its interface (symbol table) is still fresh +(matches previous cached value). If it is not, we consider dependent SCCs +stale so that they need to be re-parsed as well. + +Note on indirect dependencies: normally dependencies are determined from +imports, but since our interfaces are "opaque" (i.e. symbol tables can +contain cross-references as well as types identified by name), these are not +enough. We *must* also add "indirect" dependencies from symbols and types to +their definitions. For this purpose, we record all accessed symbols during +semantic analysis, and after we finished processing a module, we traverse its +type map, and for each type we find (transitively) on which named types it +depends. Import cycles ------------- -Finally we have to decide how to handle (c), import cycles. Here +Finally we have to decide how to handle (b), import cycles. Here we'll need a modified version of the original state machine (build.py), but we only need to do this per SCC, and we won't have to deal with changes to the list of nodes while we're processing it. @@ -1846,23 +1872,23 @@ class State: # Map each dependency to the line number where it is first imported dep_line_map: dict[str, int] + # Map from dependency id to its last observed interface hash + dep_hashes: dict[str, bytes] = {} + + # List of errors reported for this file last time. + error_lines: list[SerializedError] = [] + # Parent package, its parent, etc. ancestors: list[str] | None = None # List of (path, line number) tuples giving context for import import_context: list[tuple[str, int]] - # The State from which this module was imported, if any - caller_state: State | None = None - # If caller_state is set, the line number in the caller where the import occurred caller_line = 0 - # If True, indicate that the public interface of this module is unchanged - externally_same = True - # Contains a hash of the public interface in incremental mode - interface_hash: str = "" + interface_hash: bytes = b"" # Options, specialized for this file options: Options @@ -1870,9 +1896,6 @@ class State: # Whether to ignore all errors ignore_all = False - # Whether the module has an error or any of its dependencies have one. - transitive_error = False - # Errors reported before semantic analysis, to allow fine-grained # mode to keep reporting them. early_errors: list[ErrorInfo] @@ -1911,7 +1934,6 @@ def __init__( self.manager = manager State.order_counter += 1 self.order = State.order_counter - self.caller_state = caller_state self.caller_line = caller_line if caller_state: self.import_context = caller_state.import_context.copy() @@ -1973,6 +1995,11 @@ def __init__( self.priorities = {id: pri for id, pri in zip(all_deps, self.meta.dep_prios)} assert len(all_deps) == len(self.meta.dep_lines) self.dep_line_map = {id: line for id, line in zip(all_deps, self.meta.dep_lines)} + assert len(self.meta.dep_hashes) == len(self.meta.dependencies) + self.dep_hashes = { + k: v for (k, v) in zip(self.meta.dependencies, self.meta.dep_hashes) + } + self.error_lines = self.meta.error_lines if temporary: self.load_tree(temporary=True) if not manager.use_fine_grained_cache(): @@ -2000,11 +2027,6 @@ def __init__( self.parse_file(temporary=temporary) self.compute_dependencies() - @property - def xmeta(self) -> CacheMeta: - assert self.meta, "missing meta on allegedly fresh module" - return self.meta - def add_ancestors(self) -> None: if self.path is not None: _, name = os.path.split(self.path) @@ -2025,26 +2047,17 @@ def is_fresh(self) -> bool: """Return whether the cache data for this file is fresh.""" # NOTE: self.dependencies may differ from # self.meta.dependencies when a dependency is dropped due to - # suppression by silent mode. However when a suppressed + # suppression by silent mode. However, when a suppressed # dependency is added back we find out later in the process. - return ( - self.meta is not None - and self.is_interface_fresh() - and self.dependencies == self.meta.dependencies - ) - - def is_interface_fresh(self) -> bool: - return self.externally_same + return self.meta is not None and self.dependencies == self.meta.dependencies def mark_as_rechecked(self) -> None: """Marks this module as having been fully re-analyzed by the type-checker.""" self.manager.rechecked_modules.add(self.id) - def mark_interface_stale(self, *, on_errors: bool = False) -> None: + def mark_interface_stale(self) -> None: """Marks this module as having a stale public interface, and discards the cache data.""" - self.externally_same = False - if not on_errors: - self.manager.stale_modules.add(self.id) + self.manager.stale_modules.add(self.id) def check_blockers(self) -> None: """Raise CompileError if a blocking error is detected.""" @@ -2092,15 +2105,23 @@ def load_tree(self, temporary: bool = False) -> None: self.meta is not None ), "Internal error: this method must be called only for cached modules" - data = _load_json_file( - self.meta.data_json, self.manager, "Load tree ", "Could not load tree: " - ) + data: bytes | dict[str, Any] | None + if self.options.fixed_format_cache: + data = _load_ff_file(self.meta.data_file, self.manager, "Could not load tree: ") + else: + data = _load_json_file( + self.meta.data_file, self.manager, "Load tree ", "Could not load tree: " + ) if data is None: return t0 = time.time() # TODO: Assert data file wasn't changed. - self.tree = MypyFile.deserialize(data) + if isinstance(data, bytes): + data_io = ReadBuffer(data) + self.tree = MypyFile.read(data_io) + else: + self.tree = MypyFile.deserialize(data) t1 = time.time() self.manager.add_stats(deserialize_time=t1 - t0) if not temporary: @@ -2150,10 +2171,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, @@ -2245,8 +2268,10 @@ def semantic_analysis_pass1(self) -> None: # TODO: Do this while constructing the AST? self.tree.names = SymbolTable() if not self.tree.is_stub: - # Always perform some low-key variable renaming - self.tree.accept(LimitedVariableRenameVisitor()) + if not self.options.allow_redefinition_new: + # Perform some low-key variable renaming when assignments can't + # widen inferred types + self.tree.accept(LimitedVariableRenameVisitor()) if options.allow_redefinition: # Perform more renaming across the AST to allow variable redefinitions self.tree.accept(VariableRenameVisitor()) @@ -2290,6 +2315,7 @@ def compute_dependencies(self) -> None: self.suppressed_set = set() self.priorities = {} # id -> priority self.dep_line_map = {} # id -> line + self.dep_hashes = {} dep_entries = manager.all_imported_modules_in_file( self.tree ) + self.manager.plugin.get_additional_deps(self.tree) @@ -2377,23 +2403,14 @@ def finish_passes(self) -> None: # We should always patch indirect dependencies, even in full (non-incremental) builds, # because the cache still may be written, and it must be correct. - # TODO: find a more robust way to traverse *all* relevant types? - expr_types = set(self.type_map().values()) - symbol_types = set() - for _, sym, _ in self.tree.local_definitions(): - if sym.type is not None: - symbol_types.add(sym.type) - if isinstance(sym.node, TypeInfo): - # TypeInfo symbols have some extra relevant types. - symbol_types.update(sym.node.bases) - if sym.node.metaclass_type: - symbol_types.add(sym.node.metaclass_type) - if sym.node.typeddict_type: - symbol_types.add(sym.node.typeddict_type) - if sym.node.tuple_type: - symbol_types.add(sym.node.tuple_type) - self._patch_indirect_dependencies( - self.type_checker().module_refs, expr_types | symbol_types + self.patch_indirect_dependencies( + # Two possible sources of indirect dependencies: + # * Symbols not directly imported in this module but accessed via an attribute + # or via a re-export (vast majority of these recorded in semantic analysis). + # * For each expression type we need to record definitions of type components + # since "meaning" of the type may be updated when definitions are updated. + self.tree.module_refs | self.type_checker().module_refs, + set(self.type_map().values()), ) if self.options.dump_inference_stats: @@ -2423,21 +2440,17 @@ def free_state(self) -> None: self._type_checker.reset() self._type_checker = None - def _patch_indirect_dependencies(self, module_refs: set[str], types: set[Type]) -> None: - assert None not in types - valid = self.valid_references() + def patch_indirect_dependencies(self, module_refs: set[str], types: set[Type]) -> None: + assert self.ancestors is not None + existing_deps = set(self.dependencies + self.suppressed + self.ancestors) + existing_deps.add(self.id) encountered = self.manager.indirection_detector.find_modules(types) | module_refs - extra = encountered - valid - - for dep in sorted(extra): + for dep in sorted(encountered - existing_deps): if dep not in self.manager.modules: continue - if dep not in self.suppressed_set and dep not in self.manager.missing_modules: - self.add_dependency(dep) - self.priorities[dep] = PRI_INDIRECT - elif dep not in self.suppressed_set and dep in self.manager.missing_modules: - self.suppress_dependency(dep) + self.add_dependency(dep) + self.priorities[dep] = PRI_INDIRECT def compute_fine_grained_deps(self) -> dict[str, set[str]]: assert self.tree is not None @@ -2467,17 +2480,7 @@ def update_fine_grained_deps(self, deps: dict[str, set[str]]) -> None: merge_dependencies(self.compute_fine_grained_deps(), deps) type_state.update_protocol_deps(deps) - def valid_references(self) -> set[str]: - assert self.ancestors is not None - valid_refs = set(self.dependencies + self.suppressed + self.ancestors) - valid_refs.add(self.id) - - if "os" in valid_refs: - valid_refs.add("os.path") - - return valid_refs - - def write_cache(self) -> None: + def write_cache(self) -> tuple[CacheMeta, str] | None: assert self.tree is not None, "Internal error: method must be called on parsed file only" # We don't support writing cache files in fine-grained incremental mode. if ( @@ -2487,24 +2490,22 @@ def write_cache(self) -> None: ): if self.options.debug_serialize: try: - self.tree.serialize() + if self.manager.options.fixed_format_cache: + data = WriteBuffer() + self.tree.write(data) + else: + self.tree.serialize() except Exception: print(f"Error serializing {self.id}", file=self.manager.stdout) raise # Propagate to display traceback - return - is_errors = self.transitive_error - if is_errors: - delete_cache(self.id, self.path, self.manager) - self.meta = None - self.mark_interface_stale(on_errors=True) - return + return None dep_prios = self.dependency_priorities() dep_lines = self.dependency_lines() assert self.source_hash is not None assert len(set(self.dependencies)) == len( self.dependencies ), f"Duplicates in dependencies list for {self.id} ({self.dependencies})" - new_interface_hash, self.meta = write_cache( + new_interface_hash, meta_tuple = write_cache( self.id, self.path, self.tree, @@ -2523,6 +2524,7 @@ def write_cache(self) -> None: self.manager.log(f"Cached module {self.id} has changed interface") self.mark_interface_stale() self.interface_hash = new_interface_hash + return meta_tuple def verify_dependencies(self, suppressed_only: bool = False) -> None: """Report errors for import targets in modules that don't exist. @@ -2531,14 +2533,16 @@ def verify_dependencies(self, suppressed_only: bool = False) -> None: """ manager = self.manager assert self.ancestors is not None + # Strip out indirect dependencies. See comment in build.load_graph(). if suppressed_only: - all_deps = self.suppressed + all_deps = [dep for dep in self.suppressed if self.priorities.get(dep) != PRI_INDIRECT] else: - # Strip out indirect dependencies. See comment in build.load_graph(). dependencies = [ - dep for dep in self.dependencies if self.priorities.get(dep) != PRI_INDIRECT + dep + for dep in self.dependencies + self.suppressed + if self.priorities.get(dep) != PRI_INDIRECT ] - all_deps = dependencies + self.suppressed + self.ancestors + all_deps = dependencies + self.ancestors for dep in all_deps: if dep in manager.modules: continue @@ -2668,17 +2672,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 +2726,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 +2737,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 +2799,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 +2875,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 @@ -2907,6 +2906,9 @@ def dispatch(sources: list[BuildSource], manager: BuildManager, stdout: TextIO) manager.cache_enabled = False graph = load_graph(sources, manager) + for id in graph: + manager.import_map[id] = set(graph[id].dependencies + graph[id].suppressed) + t1 = time.time() manager.add_stats( graph_size=len(graph), @@ -3022,7 +3024,7 @@ def dump_graph(graph: Graph, stdout: TextIO | None = None) -> None: nodes = [] sccs = sorted_components(graph) for i, ascc in enumerate(sccs): - scc = order_ascc(graph, ascc) + scc = order_ascc(graph, ascc.mod_ids) node = NodeInfo(i, scc) nodes.append(node) inv_nodes = {} # module -> node_id @@ -3209,159 +3211,150 @@ def load_graph( if dep in graph and dep in st.suppressed_set: # Previously suppressed file is now visible st.add_dependency(dep) + # In the loop above we skip indirect dependencies, so to make indirect dependencies behave + # more consistently with regular ones, we suppress them manually here (when needed). + for st in graph.values(): + indirect = [dep for dep in st.dependencies if st.priorities.get(dep) == PRI_INDIRECT] + for dep in indirect: + if dep not in graph: + st.suppress_dependency(dep) manager.plugin.set_modules(manager.modules) return graph -def process_graph(graph: Graph, manager: BuildManager) -> None: - """Process everything in dependency order.""" - sccs = sorted_components(graph) - manager.log("Found %d SCCs; largest has %d nodes" % (len(sccs), max(len(scc) for scc in sccs))) - - fresh_scc_queue: list[list[str]] = [] +def order_ascc_ex(graph: Graph, ascc: SCC) -> list[str]: + """Apply extra heuristics on top of order_ascc(). - # We're processing SCCs from leaves (those without further - # dependencies) to roots (those from which everything else can be - # reached). + This should be used only for actual SCCs, not for "inner" SCCs + we create recursively during ordering of the SCC. Currently, this + has only some special handling for builtin SCC. + """ + scc = order_ascc(graph, ascc.mod_ids) + # Make the order of the SCC that includes 'builtins' and 'typing', + # among other things, predictable. Various things may break if + # the order changes. + if "builtins" in ascc.mod_ids: + scc = sorted(scc, reverse=True) + # If builtins is in the list, move it last. (This is a bit of + # a hack, but it's necessary because the builtins module is + # part of a small cycle involving at least {builtins, abc, + # typing}. Of these, builtins must be processed last or else + # some builtin objects will be incompletely processed.) + scc.remove("builtins") + scc.append("builtins") + return scc + + +def find_stale_sccs( + sccs: list[SCC], graph: Graph, manager: BuildManager +) -> tuple[list[SCC], list[SCC]]: + """Split a list of ready SCCs into stale and fresh. + + Fresh SCCs are those where: + * We have valid cache files for all modules in the SCC. + * There are no changes in dependencies (files removed from/added to the build). + * The interface hashes of direct dependents matches those recorded in the cache. + The first and second conditions are verified by is_fresh(). + """ + stale_sccs = [] + fresh_sccs = [] for ascc in sccs: - # Order the SCC's nodes using a heuristic. - # Note that ascc is a set, and scc is a list. - scc = order_ascc(graph, ascc) - # Make the order of the SCC that includes 'builtins' and 'typing', - # among other things, predictable. Various things may break if - # the order changes. - if "builtins" in ascc: - scc = sorted(scc, reverse=True) - # If builtins is in the list, move it last. (This is a bit of - # a hack, but it's necessary because the builtins module is - # part of a small cycle involving at least {builtins, abc, - # typing}. Of these, builtins must be processed last or else - # some builtin objects will be incompletely processed.) - scc.remove("builtins") - scc.append("builtins") - if manager.options.verbosity >= 2: - for id in scc: - manager.trace( - f"Priorities for {id}:", - " ".join( - "%s:%d" % (x, graph[id].priorities[x]) - for x in graph[id].dependencies - if x in ascc and x in graph[id].priorities - ), - ) - # Because the SCCs are presented in topological sort order, we - # don't need to look at dependencies recursively for staleness - # -- the immediate dependencies are sufficient. - stale_scc = {id for id in scc if not graph[id].is_fresh()} + stale_scc = {id for id in ascc.mod_ids if not graph[id].is_fresh()} fresh = not stale_scc - deps = set() - for id in scc: - deps.update(graph[id].dependencies) - deps -= ascc - stale_deps = {id for id in deps if id in graph and not graph[id].is_interface_fresh()} + + # Verify that interfaces of dependencies still present in graph are up-to-date (fresh). + stale_deps = set() + for id in ascc.mod_ids: + for dep in graph[id].dep_hashes: + if dep in graph and graph[dep].interface_hash != graph[id].dep_hashes[dep]: + stale_deps.add(dep) fresh = fresh and not stale_deps - undeps = set() - if fresh: - # Check if any dependencies that were suppressed according - # to the cache have been added back in this run. - # NOTE: Newly suppressed dependencies are handled by is_fresh(). - for id in scc: - undeps.update(graph[id].suppressed) - undeps &= graph.keys() - if undeps: - fresh = False + if fresh: - # All cache files are fresh. Check that no dependency's - # cache file is newer than any scc node's cache file. - oldest_in_scc = min(graph[id].xmeta.data_mtime for id in scc) - viable = {id for id in stale_deps if graph[id].meta is not None} - newest_in_deps = ( - 0 if not viable else max(graph[dep].xmeta.data_mtime for dep in viable) - ) - if manager.options.verbosity >= 3: # Dump all mtimes for extreme debugging. - all_ids = sorted(ascc | viable, key=lambda id: graph[id].xmeta.data_mtime) - for id in all_ids: - if id in scc: - if graph[id].xmeta.data_mtime < newest_in_deps: - key = "*id:" - else: - key = "id:" - else: - if graph[id].xmeta.data_mtime > oldest_in_scc: - key = "+dep:" - else: - key = "dep:" - manager.trace(" %5s %.0f %s" % (key, graph[id].xmeta.data_mtime, id)) - # If equal, give the benefit of the doubt, due to 1-sec time granularity - # (on some platforms). - if oldest_in_scc < newest_in_deps: - fresh = False - fresh_msg = f"out of date by {newest_in_deps - oldest_in_scc:.0f} seconds" - else: - fresh_msg = "fresh" - elif undeps: - fresh_msg = f"stale due to changed suppression ({' '.join(sorted(undeps))})" + fresh_msg = "fresh" elif stale_scc: fresh_msg = "inherently stale" - if stale_scc != ascc: + if stale_scc != ascc.mod_ids: fresh_msg += f" ({' '.join(sorted(stale_scc))})" if stale_deps: fresh_msg += f" with stale deps ({' '.join(sorted(stale_deps))})" else: fresh_msg = f"stale due to deps ({' '.join(sorted(stale_deps))})" - # Initialize transitive_error for all SCC members from union - # of transitive_error of dependencies. - if any(graph[dep].transitive_error for dep in deps if dep in graph): - for id in scc: - graph[id].transitive_error = True - - scc_str = " ".join(scc) + scc_str = " ".join(ascc.mod_ids) if fresh: - manager.trace(f"Queuing {fresh_msg} SCC ({scc_str})") - fresh_scc_queue.append(scc) + manager.trace(f"Found {fresh_msg} SCC ({scc_str})") + # If there is at most one file with errors we can skip the ordering to save time. + mods_with_errors = [id for id in ascc.mod_ids if graph[id].error_lines] + if len(mods_with_errors) <= 1: + scc = mods_with_errors + else: + # Use exactly the same order as for stale SCCs for stability. + scc = order_ascc_ex(graph, ascc) + for id in scc: + if graph[id].error_lines: + path = manager.errors.simplify_path(graph[id].xpath) + formatted = manager.errors.format_messages( + path, + deserialize_codes(graph[id].error_lines), + formatter=manager.error_formatter, + ) + manager.flush_errors(path, formatted, False) + fresh_sccs.append(ascc) else: - if fresh_scc_queue: - manager.log(f"Processing {len(fresh_scc_queue)} queued fresh SCCs") - # Defer processing fresh SCCs until we actually run into a stale SCC - # and need the earlier modules to be loaded. - # - # Note that `process_graph` may end with us not having processed every - # single fresh SCC. This is intentional -- we don't need those modules - # loaded if there are no more stale SCCs to be rechecked. - # - # Also note we shouldn't have to worry about transitive_error here, - # since modules with transitive errors aren't written to the cache, - # and if any dependencies were changed, this SCC would be stale. - # (Also, in quick_and_dirty mode we don't care about transitive errors.) - # - # TODO: see if it's possible to determine if we need to process only a - # _subset_ of the past SCCs instead of having to process them all. - for prev_scc in fresh_scc_queue: - process_fresh_modules(graph, prev_scc, manager) - fresh_scc_queue = [] - size = len(scc) + size = len(ascc.mod_ids) if size == 1: - manager.log(f"Processing SCC singleton ({scc_str}) as {fresh_msg}") + manager.log(f"Scheduling SCC singleton ({scc_str}) as {fresh_msg}") else: - manager.log("Processing SCC of size %d (%s) as %s" % (size, scc_str, fresh_msg)) - process_stale_scc(graph, scc, manager) + manager.log("Scheduling SCC of size %d (%s) as %s" % (size, scc_str, fresh_msg)) + stale_sccs.append(ascc) + return stale_sccs, fresh_sccs - sccs_left = len(fresh_scc_queue) - nodes_left = sum(len(scc) for scc in fresh_scc_queue) - manager.add_stats(sccs_left=sccs_left, nodes_left=nodes_left) - if sccs_left: - manager.log( - "{} fresh SCCs ({} nodes) left in queue (and will remain unprocessed)".format( - sccs_left, nodes_left - ) - ) - manager.trace(str(fresh_scc_queue)) - else: - manager.log("No fresh SCCs left in queue") +def process_graph(graph: Graph, manager: BuildManager) -> None: + """Process everything in dependency order.""" + sccs = sorted_components(graph) + manager.log( + "Found %d SCCs; largest has %d nodes" % (len(sccs), max(len(scc.mod_ids) for scc in sccs)) + ) + + scc_by_id = {scc.id: scc for scc in sccs} + manager.scc_by_id = scc_by_id + manager.top_order = [scc.id for scc in sccs] -def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_ALL) -> list[str]: + # Prime the ready list with leaf SCCs (that have no dependencies). + ready = [] + not_ready = [] + for scc in sccs: + if not scc.deps: + ready.append(scc) + else: + not_ready.append(scc) + + still_working = False + while ready or not_ready or still_working: + stale, fresh = find_stale_sccs(ready, graph, manager) + if stale: + manager.submit(stale) + still_working = True + # We eagerly walk over fresh SCCs to reach as many stale SCCs as soon + # as possible. Only when there are no fresh SCCs, we wait on scheduled stale ones. + # This strategy, similar to a naive strategy in minesweeper game, will allow us + # to leverage parallelism as much as possible. + if fresh: + done = fresh + else: + done, still_working = manager.wait_for_done(graph) + ready = [] + for done_scc in done: + for dependent in done_scc.direct_dependents: + scc_by_id[dependent].not_ready_deps.discard(done_scc.id) + if not scc_by_id[dependent].not_ready_deps: + not_ready.remove(scc_by_id[dependent]) + ready.append(scc_by_id[dependent]) + + +def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_INDIRECT) -> list[str]: """Come up with the ideal processing order within an SCC. Using the priorities assigned by all_imported_modules_in_file(), @@ -3385,7 +3378,7 @@ def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_ALL) -> In practice there are only a few priority levels (less than a dozen) and in the worst case we just carry out the same algorithm - for finding SCCs N times. Thus the complexity is no worse than + for finding SCCs N times. Thus, the complexity is no worse than the complexity of the original SCC-finding algorithm -- see strongly_connected_components() below for a reference. """ @@ -3403,7 +3396,7 @@ def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_ALL) -> # Filtered dependencies are uniform -- order by global order. return sorted(ascc, key=lambda id: -graph[id].order) pri_max = max(pri_spread) - sccs = sorted_components(graph, ascc, pri_max) + sccs = sorted_components_inner(graph, ascc, pri_max) # The recursion is bounded by the len(pri_spread) check above. return [s for ss in sccs for s in order_ascc(graph, ss, pri_max)] @@ -3411,8 +3404,8 @@ def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_ALL) -> def process_fresh_modules(graph: Graph, modules: list[str], manager: BuildManager) -> None: """Process the modules in one group of modules from their cached data. - This can be used to process an SCC of modules - This involves loading the tree from JSON and then doing various cleanups. + This can be used to process an SCC of modules. This involves loading the tree (i.e. + module symbol tables) from cache file and then fixing cross-references in the symbols. """ t0 = time.time() for id in modules: @@ -3424,11 +3417,54 @@ def process_fresh_modules(graph: Graph, modules: list[str], manager: BuildManage manager.add_stats(process_fresh_time=t2 - t0, load_tree_time=t1 - t0) -def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> None: - """Process the modules in one SCC from source code. +def process_stale_scc(graph: Graph, ascc: SCC, manager: BuildManager) -> None: + """Process the modules in one SCC from source code.""" + # First verify if all transitive dependencies are loaded in the current process. + missing_sccs = set() + sccs_to_find = ascc.deps.copy() + while sccs_to_find: + dep_scc = sccs_to_find.pop() + if dep_scc in manager.done_sccs or dep_scc in missing_sccs: + continue + missing_sccs.add(dep_scc) + sccs_to_find.update(manager.scc_by_id[dep_scc].deps) + + if missing_sccs: + # Load missing SCCs from cache. + # TODO: speed-up ordering if this causes problems for large builds. + fresh_sccs_to_load = [ + manager.scc_by_id[sid] for sid in manager.top_order if sid in missing_sccs + ] + manager.log(f"Processing {len(fresh_sccs_to_load)} fresh SCCs") + if ( + not manager.options.test_env + and platform.python_implementation() == "CPython" + and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES + ): + # When deserializing cache we create huge amount of new objects, so even + # with our generous GC thresholds, GC is still doing a lot of pointless + # work searching for garbage. So, we temporarily disable it when + # processing fresh SCCs, and then move all the new objects to the oldest + # generation with the freeze()/unfreeze() trick below. This is arguably + # a hack, but it gives huge performance wins for large third-party + # libraries, like torch. + gc.collect() + gc.disable() + for prev_scc in fresh_sccs_to_load: + manager.done_sccs.add(prev_scc.id) + process_fresh_modules(graph, sorted(prev_scc.mod_ids), manager) + if ( + not manager.options.test_env + and platform.python_implementation() == "CPython" + and manager.gc_freeze_cycles < MAX_GC_FREEZE_CYCLES + ): + manager.gc_freeze_cycles += 1 + gc.freeze() + gc.unfreeze() + gc.enable() - Exception: If quick_and_dirty is set, use the cache for fresh modules. - """ + # Process the SCC in stable order. + scc = order_ascc_ex(graph, ascc) stale = scc for id in stale: # We may already have parsed the module, or not. @@ -3442,7 +3478,7 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No assert typing_mod, "The typing module was not parsed" mypy.semanal_main.semantic_analysis_for_scc(graph, scc, manager.errors) - # Track what modules aren't yet done so we can finish them as soon + # Track what modules aren't yet done, so we can finish them as soon # as possible, saving memory. unfinished_modules = set(stale) for id in stale: @@ -3463,38 +3499,68 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No for id in stale: graph[id].generate_unused_ignore_notes() graph[id].generate_ignore_without_code_notes() - if any(manager.errors.is_errors_for_file(graph[id].xpath) for id in stale): - for id in stale: - graph[id].transitive_error = True + + # Flush errors, and write cache in two phases: first data files, then meta files. + meta_tuples = {} + errors_by_id = {} + formatted_by_id = {} for id in stale: if graph[id].xpath not in manager.errors.ignored_files: - errors = manager.errors.file_messages( - graph[id].xpath, formatter=manager.error_formatter + errors = manager.errors.file_messages(graph[id].xpath) + formatted = manager.errors.format_messages( + graph[id].xpath, errors, formatter=manager.error_formatter ) - manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False) - graph[id].write_cache() + manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), formatted, False) + errors_by_id[id] = errors + formatted_by_id[id] = formatted + meta_tuples[id] = graph[id].write_cache() graph[id].mark_as_rechecked() - - -def sorted_components( - graph: Graph, vertices: AbstractSet[str] | None = None, pri_max: int = PRI_ALL -) -> list[AbstractSet[str]]: + for id in stale: + meta_tuple = meta_tuples[id] + if meta_tuple is None: + continue + meta, meta_file = meta_tuple + meta.dep_hashes = [graph[dep].interface_hash for dep in graph[id].dependencies] + meta.error_lines = serialize_codes(errors_by_id.get(id, [])) + write_cache_meta(meta, manager, meta_file) + manager.done_sccs.add(ascc.id) + + +def prepare_sccs_full( + raw_sccs: Iterator[set[str]], edges: dict[str, list[str]] +) -> dict[SCC, set[SCC]]: + """Turn raw SCC sets into SCC objects and build dependency graph for SCCs.""" + sccs = [SCC(raw_scc) for raw_scc in raw_sccs] + scc_map = {} + for scc in sccs: + for id in scc.mod_ids: + scc_map[id] = scc + scc_deps_map: dict[SCC, set[SCC]] = {} + for scc in sccs: + for id in scc.mod_ids: + scc_deps_map.setdefault(scc, set()).update(scc_map[dep] for dep in edges[id]) + for scc in sccs: + # Remove trivial dependency on itself. + scc_deps_map[scc].discard(scc) + for dep_scc in scc_deps_map[scc]: + scc.deps.add(dep_scc.id) + scc.not_ready_deps.add(dep_scc.id) + return scc_deps_map + + +def sorted_components(graph: Graph) -> list[SCC]: """Return the graph's SCCs, topologically sorted by dependencies. The sort order is from leaves (nodes without dependencies) to roots (nodes on which no other nodes depend). - - This works for a subset of the full dependency graph too; - dependencies that aren't present in graph.keys() are ignored. """ # Compute SCCs. - if vertices is None: - vertices = set(graph) - edges = {id: deps_filtered(graph, vertices, id, pri_max) for id in vertices} - sccs = list(strongly_connected_components(vertices, edges)) + vertices = set(graph) + edges = {id: deps_filtered(graph, vertices, id, PRI_INDIRECT) for id in vertices} + scc_dep_map = prepare_sccs_full(strongly_connected_components(vertices, edges), edges) # Topsort. res = [] - for ready in topsort(prepare_sccs(sccs, edges)): + for ready in topsort(scc_dep_map): # Sort the sets in ready by reversed smallest State.order. Examples: # # - If ready is [{x}, {y}], x.order == 1, y.order == 2, we get @@ -3503,6 +3569,27 @@ def sorted_components( # - If ready is [{a, b}, {c, d}], a.order == 1, b.order == 3, # c.order == 2, d.order == 4, the sort keys become [1, 2] # and the result is [{c, d}, {a, b}]. + sorted_ready = sorted(ready, key=lambda scc: -min(graph[id].order for id in scc.mod_ids)) + for scc in sorted_ready: + for dep in scc_dep_map[scc]: + dep.direct_dependents.append(scc.id) + res.extend(sorted_ready) + return res + + +def sorted_components_inner( + graph: Graph, vertices: AbstractSet[str], pri_max: int +) -> list[AbstractSet[str]]: + """Simplified version of sorted_components() to work with sub-graphs. + + This doesn't create SCC objects, and operates with raw sets. This function + also allows filtering dependencies to take into account when building SCCs. + This is used for heuristic ordering of modules within actual SCCs. + """ + edges = {id: deps_filtered(graph, vertices, id, pri_max) for id in vertices} + sccs = list(strongly_connected_components(vertices, edges)) + res = [] + for ready in topsort(prepare_sccs(sccs, edges)): res.extend(sorted(ready, key=lambda scc: -min(graph[id].order for id in scc))) return res @@ -3543,10 +3630,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 +3653,41 @@ 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)) + + +def sources_to_bytes(sources: list[BuildSource]) -> bytes: + source_tuples = [(s.path, s.module, s.text, s.base_dir, s.followed) for s in sources] + buf = WriteBuffer() + write_json(buf, {"sources": source_tuples}) + return buf.getvalue() + + +def sccs_to_bytes(sccs: list[SCC]) -> bytes: + scc_tuples = [(list(scc.mod_ids), scc.id, list(scc.deps)) for scc in sccs] + buf = WriteBuffer() + write_json(buf, {"sccs": scc_tuples}) + return buf.getvalue() + + +def serialize_codes(errs: list[ErrorTuple]) -> list[SerializedError]: + return [ + (path, line, column, end_line, end_column, severity, message, code.code if code else None) + for path, line, column, end_line, end_column, severity, message, code in errs + ] + + +def deserialize_codes(errs: list[SerializedError]) -> list[ErrorTuple]: + return [ + ( + path, + line, + column, + end_line, + end_column, + severity, + message, + codes.error_codes.get(code) if code else None, + ) + for path, line, column, end_line, end_column, severity, message, code in errs + ] diff --git a/mypy/cache.py b/mypy/cache.py new file mode 100644 index 0000000000000..7755755898c0d --- /dev/null +++ b/mypy/cache.py @@ -0,0 +1,509 @@ +""" +This module contains high-level logic for fixed format serialization. + +Lower-level parts are implemented in C in mypyc/lib-rt/librt_internal.c +Short summary of low-level functionality: +* integers are automatically serialized as 1, 2, or 4 bytes, or arbitrary length. +* str/bytes are serialized as size (1, 2, or 4 bytes) followed by bytes buffer. +* floats are serialized as C doubles. + +At high-level we add type tags as needed so that our format is self-descriptive. +More precisely: +* False, True, and None are stored as just a tag: 0, 1, 2 correspondingly. +* builtin primitives like int/str/bytes/float are stored as their type tag followed + by bare (low-level) representation of the value. Reserved tag range for primitives is + 3 ... 19. +* generic (heterogeneous) list are stored as tag, followed by bare size, followed by + sequence of tagged values. +* homogeneous lists of primitives are stored as tag, followed by bare size, followed + by sequence of bare values. +* reserved tag range for sequence-like builtins is 20 ... 29 +* currently we have only one mapping-like format: string-keyed dictionary with heterogeneous + values. It is stored as tag, followed by bare size, followed by sequence of pairs: bare + string key followed by tagged value. +* reserved tag range for mapping-like builtins is 30 ... 39 +* there is an additional reserved tag range 40 ... 49 for any other builtin collections. +* custom classes (like types, symbols etc.) are stored as tag, followed by a sequence of + tagged field values, followed by a special end tag 255. Names of class fields are + *not* stored, the caller should know the field names and order for the given class tag. +* reserved tag range for symbols (TypeInfo, Var, etc) is 50 ... 79. +* class Instance is the only exception from the above format (since it is the most common one). + It has two extra formats: few most common instances like "builtins.object" are stored as + instance tag followed by a secondary tag, other plain non-generic instances are stored as + instance tag followed by secondary tag followed by fullname as bare string. All generic + readers must handle these. +* reserved tag range for Instance type formats is 80 ... 99, for other types it is 100 ... 149. +* tag 254 is reserved for if we would ever need to extend the tag range to indicated second tag + page. Tags 150 ... 253 are free for everything else (e.g. AST nodes etc). + +General convention is that custom classes implement write() and read() methods for FF +serialization. The write method should write both class tag and end tag. The read method +conventionally *does not* read the start tag (to simplify logic for unions). Known exceptions +are MypyFile.read() and SymbolTableNode.read(), since those two never appear in a union. + +If any of these details change, or if the structure of CacheMeta changes please +bump CACHE_VERSION below. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any, Final, Optional, Union +from typing_extensions import TypeAlias as _TypeAlias + +from librt.internal import ( + ReadBuffer as ReadBuffer, + WriteBuffer as WriteBuffer, + read_bool as read_bool, + read_bytes as read_bytes_bare, + read_float as read_float_bare, + read_int as read_int_bare, + read_str as read_str_bare, + read_tag as read_tag, + write_bool as write_bool, + write_bytes as write_bytes_bare, + write_float as write_float_bare, + write_int as write_int_bare, + write_str as write_str_bare, + write_tag as write_tag, +) +from mypy_extensions import u8 + +# High-level cache layout format +CACHE_VERSION: Final = 1 + +SerializedError: _TypeAlias = tuple[Optional[str], int, int, int, int, str, str, Optional[str]] + + +class CacheMeta: + """Class representing cache metadata for a module.""" + + def __init__( + self, + *, + id: str, + path: str, + mtime: int, + size: int, + hash: str, + dependencies: list[str], + data_mtime: int, + data_file: str, + suppressed: list[str], + options: dict[str, object], + dep_prios: list[int], + dep_lines: list[int], + dep_hashes: list[bytes], + interface_hash: bytes, + error_lines: list[SerializedError], + version_id: str, + ignore_all: bool, + plugin_data: Any, + ) -> None: + self.id = id + self.path = path + self.mtime = mtime # source file mtime + self.size = size # source file size + self.hash = hash # source file hash (as a hex string for historical reasons) + self.dependencies = dependencies # names of imported modules + self.data_mtime = data_mtime # mtime of data_file + self.data_file = data_file # path of .data.json or .data.ff + self.suppressed = suppressed # dependencies that weren't imported + self.options = options # build options snapshot + # dep_prios and dep_lines are both aligned with dependencies + suppressed + self.dep_prios = dep_prios + self.dep_lines = dep_lines + # dep_hashes list is aligned with dependencies only + self.dep_hashes = dep_hashes # list of interface_hash for dependencies + self.interface_hash = interface_hash # hash representing the public interface + self.error_lines = error_lines + self.version_id = version_id # mypy version for cache invalidation + self.ignore_all = ignore_all # if errors were ignored + self.plugin_data = plugin_data # config data from plugins + + def serialize(self) -> dict[str, Any]: + return { + "id": self.id, + "path": self.path, + "mtime": self.mtime, + "size": self.size, + "hash": self.hash, + "data_mtime": self.data_mtime, + "dependencies": self.dependencies, + "suppressed": self.suppressed, + "options": self.options, + "dep_prios": self.dep_prios, + "dep_lines": self.dep_lines, + "dep_hashes": [dep.hex() for dep in self.dep_hashes], + "interface_hash": self.interface_hash.hex(), + "error_lines": self.error_lines, + "version_id": self.version_id, + "ignore_all": self.ignore_all, + "plugin_data": self.plugin_data, + } + + @classmethod + def deserialize(cls, meta: dict[str, Any], data_file: str) -> CacheMeta | None: + try: + return CacheMeta( + id=meta["id"], + path=meta["path"], + mtime=meta["mtime"], + size=meta["size"], + hash=meta["hash"], + dependencies=meta["dependencies"], + data_mtime=meta["data_mtime"], + data_file=data_file, + suppressed=meta["suppressed"], + options=meta["options"], + dep_prios=meta["dep_prios"], + dep_lines=meta["dep_lines"], + dep_hashes=[bytes.fromhex(dep) for dep in meta["dep_hashes"]], + interface_hash=bytes.fromhex(meta["interface_hash"]), + error_lines=[tuple(err) for err in meta["error_lines"]], + version_id=meta["version_id"], + ignore_all=meta["ignore_all"], + plugin_data=meta["plugin_data"], + ) + except (KeyError, ValueError): + return None + + def write(self, data: WriteBuffer) -> None: + write_str(data, self.id) + write_str(data, self.path) + write_int(data, self.mtime) + write_int(data, self.size) + write_str(data, self.hash) + write_str_list(data, self.dependencies) + write_int(data, self.data_mtime) + write_str_list(data, self.suppressed) + write_json(data, self.options) + write_int_list(data, self.dep_prios) + write_int_list(data, self.dep_lines) + write_bytes_list(data, self.dep_hashes) + write_bytes(data, self.interface_hash) + write_errors(data, self.error_lines) + write_str(data, self.version_id) + write_bool(data, self.ignore_all) + # Plugin data may be not a dictionary, so we use + # a more generic write_json_value() here. + write_json_value(data, self.plugin_data) + + @classmethod + def read(cls, data: ReadBuffer, data_file: str) -> CacheMeta | None: + try: + return CacheMeta( + id=read_str(data), + path=read_str(data), + mtime=read_int(data), + size=read_int(data), + hash=read_str(data), + dependencies=read_str_list(data), + data_mtime=read_int(data), + data_file=data_file, + suppressed=read_str_list(data), + options=read_json(data), + dep_prios=read_int_list(data), + dep_lines=read_int_list(data), + dep_hashes=read_bytes_list(data), + interface_hash=read_bytes(data), + error_lines=read_errors(data), + version_id=read_str(data), + ignore_all=read_bool(data), + plugin_data=read_json_value(data), + ) + except ValueError: + return None + + +# Always use this type alias to refer to type tags. +Tag = u8 + +# Primitives. +LITERAL_FALSE: Final[Tag] = 0 +LITERAL_TRUE: Final[Tag] = 1 +LITERAL_NONE: Final[Tag] = 2 +LITERAL_INT: Final[Tag] = 3 +LITERAL_STR: Final[Tag] = 4 +LITERAL_BYTES: Final[Tag] = 5 +LITERAL_FLOAT: Final[Tag] = 6 +LITERAL_COMPLEX: Final[Tag] = 7 + +# Collections. +LIST_GEN: Final[Tag] = 20 +LIST_INT: Final[Tag] = 21 +LIST_STR: Final[Tag] = 22 +LIST_BYTES: Final[Tag] = 23 +TUPLE_GEN: Final[Tag] = 24 +DICT_STR_GEN: Final[Tag] = 30 + +# Misc classes. +EXTRA_ATTRS: Final[Tag] = 150 +DT_SPEC: Final[Tag] = 151 + +END_TAG: Final[Tag] = 255 + + +def read_literal(data: ReadBuffer, tag: Tag) -> int | str | bool | float: + if tag == LITERAL_INT: + return read_int_bare(data) + elif tag == LITERAL_STR: + return read_str_bare(data) + elif tag == LITERAL_FALSE: + return False + elif tag == LITERAL_TRUE: + return True + elif tag == LITERAL_FLOAT: + return read_float_bare(data) + assert False, f"Unknown literal tag {tag}" + + +# There is an intentional asymmetry between read and write for literals because +# None and/or complex values are only allowed in some contexts but not in others. +def write_literal(data: WriteBuffer, value: int | str | bool | float | complex | None) -> None: + if isinstance(value, bool): + write_bool(data, value) + elif isinstance(value, int): + write_tag(data, LITERAL_INT) + write_int_bare(data, value) + elif isinstance(value, str): + write_tag(data, LITERAL_STR) + write_str_bare(data, value) + elif isinstance(value, float): + write_tag(data, LITERAL_FLOAT) + write_float_bare(data, value) + elif isinstance(value, complex): + write_tag(data, LITERAL_COMPLEX) + write_float_bare(data, value.real) + write_float_bare(data, value.imag) + else: + write_tag(data, LITERAL_NONE) + + +def read_int(data: ReadBuffer) -> int: + assert read_tag(data) == LITERAL_INT + return read_int_bare(data) + + +def write_int(data: WriteBuffer, value: int) -> None: + write_tag(data, LITERAL_INT) + write_int_bare(data, value) + + +def read_str(data: ReadBuffer) -> str: + assert read_tag(data) == LITERAL_STR + return read_str_bare(data) + + +def write_str(data: WriteBuffer, value: str) -> None: + write_tag(data, LITERAL_STR) + write_str_bare(data, value) + + +def read_bytes(data: ReadBuffer) -> bytes: + assert read_tag(data) == LITERAL_BYTES + return read_bytes_bare(data) + + +def write_bytes(data: WriteBuffer, value: bytes) -> None: + write_tag(data, LITERAL_BYTES) + write_bytes_bare(data, value) + + +def read_int_opt(data: ReadBuffer) -> int | None: + tag = read_tag(data) + if tag == LITERAL_NONE: + return None + assert tag == LITERAL_INT + return read_int_bare(data) + + +def write_int_opt(data: WriteBuffer, value: int | None) -> None: + if value is not None: + write_tag(data, LITERAL_INT) + write_int_bare(data, value) + else: + write_tag(data, LITERAL_NONE) + + +def read_str_opt(data: ReadBuffer) -> str | None: + tag = read_tag(data) + if tag == LITERAL_NONE: + return None + assert tag == LITERAL_STR + return read_str_bare(data) + + +def write_str_opt(data: WriteBuffer, value: str | None) -> None: + if value is not None: + write_tag(data, LITERAL_STR) + write_str_bare(data, value) + else: + write_tag(data, LITERAL_NONE) + + +def read_int_list(data: ReadBuffer) -> list[int]: + assert read_tag(data) == LIST_INT + size = read_int_bare(data) + return [read_int_bare(data) for _ in range(size)] + + +def write_int_list(data: WriteBuffer, value: list[int]) -> None: + write_tag(data, LIST_INT) + write_int_bare(data, len(value)) + for item in value: + write_int_bare(data, item) + + +def read_str_list(data: ReadBuffer) -> list[str]: + assert read_tag(data) == LIST_STR + size = read_int_bare(data) + return [read_str_bare(data) for _ in range(size)] + + +def write_str_list(data: WriteBuffer, value: Sequence[str]) -> None: + write_tag(data, LIST_STR) + write_int_bare(data, len(value)) + for item in value: + write_str_bare(data, item) + + +def read_bytes_list(data: ReadBuffer) -> list[bytes]: + assert read_tag(data) == LIST_BYTES + size = read_int_bare(data) + return [read_bytes_bare(data) for _ in range(size)] + + +def write_bytes_list(data: WriteBuffer, value: Sequence[bytes]) -> None: + write_tag(data, LIST_BYTES) + write_int_bare(data, len(value)) + for item in value: + write_bytes_bare(data, item) + + +def read_str_opt_list(data: ReadBuffer) -> list[str | None]: + assert read_tag(data) == LIST_GEN + size = read_int_bare(data) + return [read_str_opt(data) for _ in range(size)] + + +def write_str_opt_list(data: WriteBuffer, value: list[str | None]) -> None: + write_tag(data, LIST_GEN) + write_int_bare(data, len(value)) + for item in value: + write_str_opt(data, item) + + +Value: _TypeAlias = Union[None, int, str, bool] + +# Our JSON format is somewhat non-standard as we distinguish lists and tuples. +# This is convenient for some internal things, like mypyc plugin and error serialization. +JsonValue: _TypeAlias = Union[ + Value, list["JsonValue"], dict[str, "JsonValue"], tuple["JsonValue", ...] +] + + +def read_json_value(data: ReadBuffer) -> JsonValue: + tag = read_tag(data) + if tag == LITERAL_NONE: + return None + if tag == LITERAL_FALSE: + return False + if tag == LITERAL_TRUE: + return True + if tag == LITERAL_INT: + return read_int_bare(data) + if tag == LITERAL_STR: + return read_str_bare(data) + if tag == LIST_GEN: + size = read_int_bare(data) + return [read_json_value(data) for _ in range(size)] + if tag == TUPLE_GEN: + size = read_int_bare(data) + return tuple(read_json_value(data) for _ in range(size)) + if tag == DICT_STR_GEN: + size = read_int_bare(data) + return {read_str_bare(data): read_json_value(data) for _ in range(size)} + assert False, f"Invalid JSON tag: {tag}" + + +def write_json_value(data: WriteBuffer, value: JsonValue) -> None: + if value is None: + write_tag(data, LITERAL_NONE) + elif isinstance(value, bool): + write_bool(data, value) + elif isinstance(value, int): + write_tag(data, LITERAL_INT) + write_int_bare(data, value) + elif isinstance(value, str): + write_tag(data, LITERAL_STR) + write_str_bare(data, value) + elif isinstance(value, list): + write_tag(data, LIST_GEN) + write_int_bare(data, len(value)) + for val in value: + write_json_value(data, val) + elif isinstance(value, tuple): + write_tag(data, TUPLE_GEN) + write_int_bare(data, len(value)) + for val in value: + write_json_value(data, val) + elif isinstance(value, dict): + write_tag(data, DICT_STR_GEN) + write_int_bare(data, len(value)) + for key in sorted(value): + write_str_bare(data, key) + write_json_value(data, value[key]) + else: + assert False, f"Invalid JSON value: {value}" + + +# These are functions for JSON *dictionaries* specifically. Unfortunately, we +# must use imprecise types here, because the callers use imprecise types. +def read_json(data: ReadBuffer) -> dict[str, Any]: + assert read_tag(data) == DICT_STR_GEN + size = read_int_bare(data) + return {read_str_bare(data): read_json_value(data) for _ in range(size)} + + +def write_json(data: WriteBuffer, value: dict[str, Any]) -> None: + write_tag(data, DICT_STR_GEN) + write_int_bare(data, len(value)) + for key in sorted(value): + write_str_bare(data, key) + write_json_value(data, value[key]) + + +def write_errors(data: WriteBuffer, errs: list[SerializedError]) -> None: + write_tag(data, LIST_GEN) + write_int_bare(data, len(errs)) + for path, line, column, end_line, end_column, severity, message, code in errs: + write_tag(data, TUPLE_GEN) + write_str_opt(data, path) + write_int(data, line) + write_int(data, column) + write_int(data, end_line) + write_int(data, end_column) + write_str(data, severity) + write_str(data, message) + write_str_opt(data, code) + + +def read_errors(data: ReadBuffer) -> list[SerializedError]: + assert read_tag(data) == LIST_GEN + result = [] + for _ in range(read_int_bare(data)): + assert read_tag(data) == TUPLE_GEN + result.append( + ( + read_str_opt(data), + read_int(data), + read_int(data), + read_int(data), + read_int(data), + read_str(data), + read_str(data), + read_str_opt(data), + ) + ) + return result diff --git a/mypy/checker.py b/mypy/checker.py index 8d77bb02eeb28..ad7eb3d355683 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4,43 +4,47 @@ import itertools from collections import defaultdict +from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet from contextlib import ExitStack, contextmanager from typing import ( - AbstractSet, Callable, - Dict, Final, Generic, - Iterable, - Iterator, - Mapping, + Literal, NamedTuple, Optional, - Sequence, - Tuple, TypeVar, Union, cast, overload, ) -from typing_extensions import TypeAlias as _TypeAlias +from typing_extensions import TypeAlias as _TypeAlias, TypeGuard import mypy.checkexpr from mypy import errorcodes as codes, join, message_registry, nodes, operators from mypy.binder import ConditionalTypeBinder, Frame, get_declaration +from mypy.checker_shared import CheckerScope, TypeCheckerSharedApi, TypeRange +from mypy.checker_state import checker_state from mypy.checkmember import ( MemberContext, - analyze_decorator_or_funcbase_access, - analyze_descriptor_access, + analyze_class_attribute_access, + analyze_instance_member_access, analyze_member_access, - type_object_type, + is_instance_var, ) from mypy.checkpattern import PatternChecker from mypy.constraints import SUPERTYPE_OF from mypy.erasetype import erase_type, erase_typevars, remove_instance_last_known_values from mypy.errorcodes import TYPE_VAR, UNUSED_AWAITABLE, UNUSED_COROUTINE, ErrorCode -from mypy.errors import Errors, ErrorWatcher, report_internal_error -from mypy.expandtype import expand_self_type, expand_type, expand_type_by_instance +from mypy.errors import ( + ErrorInfo, + Errors, + ErrorWatcher, + IterationDependentErrors, + IterationErrorWatcher, + report_internal_error, +) +from mypy.expandtype import expand_type from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash from mypy.maptype import map_instance_to_supertype from mypy.meet import is_overlapping_erased_types, is_overlapping_types, meet_types @@ -72,9 +76,11 @@ LITERAL_TYPE, MDEF, NOT_ABSTRACT, + SYMBOL_FUNCBASE_TYPES, AssertStmt, AssignmentExpr, AssignmentStmt, + AwaitExpr, Block, BreakStmt, BytesExpr, @@ -85,6 +91,7 @@ ContinueStmt, Decorator, DelStmt, + DictExpr, EllipsisExpr, Expression, ExpressionStmt, @@ -93,6 +100,7 @@ FuncBase, FuncDef, FuncItem, + GlobalDecl, IfStmt, Import, ImportAll, @@ -108,14 +116,17 @@ MypyFile, NameExpr, Node, + NonlocalDecl, OperatorAssignmentStmt, OpExpr, OverloadedFuncDef, + OverloadPart, PassStmt, PromoteExpr, RaiseStmt, RefExpr, ReturnStmt, + SetExpr, StarExpr, Statement, StrExpr, @@ -128,22 +139,23 @@ TypeAlias, TypeAliasStmt, TypeInfo, - TypeVarExpr, UnaryExpr, Var, WhileStmt, WithStmt, YieldExpr, + get_func_def, is_final_node, ) from mypy.operators import flip_ops, int_op_to_method, neg_ops from mypy.options import PRECISE_TUPLE_TYPES, Options from mypy.patterns import AsPattern, StarredPattern -from mypy.plugin import CheckerPluginInterface, Plugin +from mypy.plugin import Plugin from mypy.plugins import dataclasses as dataclasses_plugin from mypy.scope import Scope from mypy.semanal import is_trivial_body, refers_to_fullname, set_callable_name from mypy.semanal_enum import ENUM_BASES, ENUM_SPECIAL_PROPS +from mypy.semanal_shared import SemanticAnalyzerCoreInterface from mypy.sharedparse import BINARY_MAGIC_METHODS from mypy.state import state from mypy.subtypes import ( @@ -163,6 +175,7 @@ from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type, make_optional_type from mypy.typeops import ( bind_self, + can_have_shared_disjoint_base, coerce_to_literal, custom_special_method, erase_def_to_union_or_bound, @@ -174,17 +187,18 @@ is_literal_type_like, is_singleton_type, make_simplified_union, - map_type_from_supertype, true_only, try_expanding_sum_type_to_union, try_getting_int_literals_from_type, try_getting_str_literals, try_getting_str_literals_from_type, tuple_fallback, + type_object_type, ) from mypy.types import ( ANY_STRATEGY, MYPYC_NATIVE_INT_NAMES, + NOT_IMPLEMENTED_TYPE_NAMES, OVERLOAD_NAMES, AnyType, BoolTypeQuery, @@ -218,6 +232,7 @@ flatten_nested_unions, get_proper_type, get_proper_types, + instance_cache, is_literal_type, is_named_instance, ) @@ -229,22 +244,22 @@ T = TypeVar("T") -DEFAULT_LAST_PASS: Final = 1 # Pass numbers start at 0 +DEFAULT_LAST_PASS: Final = 2 # Pass numbers start at 0 # Maximum length of fixed tuple types inferred when narrowing from variadic tuples. MAX_PRECISE_TUPLE_SIZE: Final = 8 -DeferredNodeType: _TypeAlias = Union[FuncDef, LambdaExpr, OverloadedFuncDef, Decorator] +DeferredNodeType: _TypeAlias = Union[FuncDef, OverloadedFuncDef, Decorator] FineGrainedDeferredNodeType: _TypeAlias = Union[FuncDef, MypyFile, OverloadedFuncDef] # A node which is postponed to be processed during the next pass. # In normal mode one can defer functions and methods (also decorated and/or overloaded) -# and lambda expressions. Nested functions can't be deferred -- only top-level functions +# but not lambda expressions. Nested functions can't be deferred -- only top-level functions # and methods of classes not defined within a function can be deferred. class DeferredNode(NamedTuple): node: DeferredNodeType - # And its TypeInfo (for semantic analysis self type handling + # And its TypeInfo (for semantic analysis self type handling) active_typeinfo: TypeInfo | None @@ -268,14 +283,7 @@ class FineGrainedDeferredNode(NamedTuple): # (such as two references to the same variable). TODO: it would # probably be better to have the dict keyed by the nodes' literal_hash # field instead. -TypeMap: _TypeAlias = Optional[Dict[Expression, Type]] - - -# An object that represents either a precise type or a type with an upper bound; -# it is important for correct type inference with isinstance. -class TypeRange(NamedTuple): - item: Type - is_upper_bound: bool # False => precise type +TypeMap: _TypeAlias = Optional[dict[Expression, Type]] # Keeps track of partial types in a single scope. In fine-grained incremental @@ -287,7 +295,27 @@ class PartialTypeScope(NamedTuple): is_local: bool -class TypeChecker(NodeVisitor[None], CheckerPluginInterface): +class LocalTypeMap: + """Store inferred types into a temporary type map (returned). + + This can be used to perform type checking "experiments" without + affecting exported types (which are used by mypyc). + """ + + def __init__(self, chk: TypeChecker) -> None: + self.chk = chk + + def __enter__(self) -> dict[Expression, Type]: + temp_type_map: dict[Expression, Type] = {} + self.chk._type_maps.append(temp_type_map) + return temp_type_map + + def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> Literal[False]: + self.chk._type_maps.pop() + return False + + +class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi): """Mypy type checker. Type check mypy source files that have been semantically analyzed. @@ -314,12 +342,14 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface): # Helper for managing conditional types binder: ConditionalTypeBinder # Helper for type checking expressions - expr_checker: mypy.checkexpr.ExpressionChecker + _expr_checker: mypy.checkexpr.ExpressionChecker pattern_checker: PatternChecker tscope: Scope scope: CheckerScope + # Innermost enclosing type + type: TypeInfo | None # Stack of function return types return_types: list[Type] # Flags; true for dynamically typed functions @@ -329,6 +359,9 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface): # Vars for which partial type errors are already reported # (to avoid logically duplicate errors with different error context). partial_reported: set[Var] + # Short names of Var nodes whose previous inferred type has been widened via assignment. + # NOTE: The names might not be unique, they are only for debugging purposes. + widened_vars: list[str] globals: SymbolTable modules: dict[str, MypyFile] # Nodes that couldn't be checked because some types weren't available. We'll run @@ -349,11 +382,9 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface): inferred_attribute_types: dict[Var, Type] | None = None # Don't infer partial None types if we are processing assignment from Union no_partial_types: bool = False - - # The set of all dependencies (suppressed or not) that this module accesses, either - # directly or indirectly. + # Extra module references not detected during semantic analysis (these are rare cases + # e.g. access to class-level import via instance). module_refs: set[str] - # A map from variable nodes to a snapshot of the frame ids of the # frames that were active when the variable was declared. This can # be used to determine nearest common ancestor frame of a variable's @@ -366,6 +397,11 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface): # functions such as open(), etc. plugin: Plugin + # A helper state to produce unique temporary names on demand. + _unique_id: int + # Fake concrete type used when checking variance + _variance_dummy_type: Instance | None + def __init__( self, errors: Errors, @@ -389,14 +425,16 @@ def __init__( self.plugin = plugin self.tscope = Scope() self.scope = CheckerScope(tree) - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(options) self.globals = tree.names + self.type = None self.return_types = [] self.dynamic_funcs = [] self.partial_types = [] self.partial_reported = set() self.var_decl_frames = {} self.deferred_nodes = [] + self.widened_vars = [] self._type_maps = [{}] self.module_refs = set() self.pass_num = 0 @@ -404,6 +442,8 @@ def __init__( self.is_stub = tree.is_stub self.is_typeshed_stub = tree.is_typeshed_file(options) self.inferred_attribute_types = None + self.allow_constructor_cache = True + self.local_type_map = LocalTypeMap(self) # If True, process function definitions. If False, don't. This is used # for processing module top levels in fine-grained incremental mode. @@ -415,6 +455,11 @@ def __init__( # argument through various `checker` and `checkmember` functions. self._is_final_def = False + # Track when we enter an overload implementation. Some checks should not be applied + # to the implementation signature when specific overloads are available. + # Use `enter_overload_impl` to modify. + self.overload_impl_stack: list[OverloadPart] = [] + # This flag is set when we run type-check or attribute access check for the purpose # of giving a note on possibly missing "await". It is used to avoid infinite recursion. self.checking_missing_await = False @@ -425,14 +470,21 @@ def __init__( self.allow_abstract_call = False # Child checker objects for specific AST node types - self.expr_checker = mypy.checkexpr.ExpressionChecker( + self._expr_checker = mypy.checkexpr.ExpressionChecker( self, self.msg, self.plugin, per_line_checking_time_ns ) + self.pattern_checker = PatternChecker(self, self.msg, self.plugin, options) + self._unique_id = 0 + self._variance_dummy_type = None + + @property + def expr_checker(self) -> mypy.checkexpr.ExpressionChecker: + return self._expr_checker @property def type_context(self) -> list[Type | None]: - return self.expr_checker.type_context + return self._expr_checker.type_context def reset(self) -> None: """Cleanup stale state that might be left over from a typechecking run. @@ -443,17 +495,14 @@ def reset(self) -> None: # TODO: verify this is still actually worth it over creating new checkers self.partial_reported.clear() self.module_refs.clear() - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(self.options) self._type_maps[1:] = [] self._type_maps[0].clear() - self.temp_type_map = None self.expr_checker.reset() - - assert self.inferred_attribute_types is None - assert self.partial_types == [] - assert self.deferred_nodes == [] - assert len(self.scope.stack) == 1 - assert self.partial_types == [] + self.deferred_nodes = [] + self.partial_types = [] + self.inferred_attribute_types = None + self.scope = CheckerScope(self.tree) def check_first_pass(self) -> None: """Type check the entire file, but defer functions with unresolved references. @@ -466,7 +515,7 @@ def check_first_pass(self) -> None: Deferred functions will be processed by check_second_pass(). """ self.recurse_into_functions = True - with state.strict_optional_set(self.options.strict_optional): + with state.strict_optional_set(self.options.strict_optional), checker_state.set(self): self.errors.set_file( self.path, self.tree.fullname, scope=self.tscope, options=self.options ) @@ -500,14 +549,18 @@ def check_first_pass(self) -> None: ) def check_second_pass( - self, todo: Sequence[DeferredNode | FineGrainedDeferredNode] | None = None + self, + todo: Sequence[DeferredNode | FineGrainedDeferredNode] | None = None, + *, + allow_constructor_cache: bool = True, ) -> bool: """Run second or following pass of type checking. This goes through deferred nodes, returning True if there were any. """ + self.allow_constructor_cache = allow_constructor_cache self.recurse_into_functions = True - with state.strict_optional_set(self.options.strict_optional): + with state.strict_optional_set(self.options.strict_optional), checker_state.set(self): if not todo and not self.deferred_nodes: return False self.errors.set_file( @@ -536,15 +589,13 @@ def check_second_pass( return True def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> None: + self.widened_vars = [] if isinstance(node, MypyFile): self.check_top_level(node) else: self.recurse_into_functions = True with self.binder.top_frame_context(): - if isinstance(node, LambdaExpr): - self.expr_checker.accept(node) - else: - self.accept(node) + self.accept(node) def check_top_level(self, node: MypyFile) -> None: """Check only the top-level of a module, skipping function definitions.""" @@ -571,13 +622,13 @@ def defer_node(self, node: DeferredNodeType, enclosing_class: TypeInfo | None) - self.deferred_nodes.append(DeferredNode(node, enclosing_class)) def handle_cannot_determine_type(self, name: str, context: Context) -> None: - node = self.scope.top_non_lambda_function() + node = self.scope.top_level_function() if self.pass_num < self.last_pass and isinstance(node, FuncDef): # Don't report an error yet. Just defer. Note that we don't defer # lambdas because they are coupled to the surrounding function # through the binder and the inferred type of the lambda, so it # would get messy. - enclosing_class = self.scope.enclosing_class() + enclosing_class = self.scope.enclosing_class(node) self.defer_node(node, enclosing_class) # Set a marker so that we won't infer additional types in this # function. Any inferred types could be bogus, because there's at @@ -599,22 +650,58 @@ def accept_loop( else_body: Statement | None = None, *, exit_condition: Expression | None = None, + on_enter_body: Callable[[], None] | None = None, ) -> None: - """Repeatedly type check a loop body until the frame doesn't change. - If exit_condition is set, assume it must be False on exit from the loop. + """Repeatedly type check a loop body until the frame doesn't change.""" - Then check the else_body. - """ - # The outer frame accumulates the results of all iterations + # The outer frame accumulates the results of all iterations: with self.binder.frame_context(can_skip=False, conditional_frame=True): + # Check for potential decreases in the number of partial types so as not to stop the + # iteration too early: + partials_old = sum(len(pts.map) for pts in self.partial_types) + # Check if assignment widened the inferred type of a variable; in this case we + # need to iterate again (we only do one extra iteration, since this could go + # on without bound otherwise) + widened_old = len(self.widened_vars) + + iter_errors = IterationDependentErrors() + iter = 1 while True: with self.binder.frame_context(can_skip=True, break_frame=2, continue_frame=1): - self.accept(body) - if not self.binder.last_pop_changed: + if on_enter_body is not None: + on_enter_body() + + with IterationErrorWatcher(self.msg.errors, iter_errors): + self.accept(body) + + partials_new = sum(len(pts.map) for pts in self.partial_types) + widened_new = len(self.widened_vars) + # Perform multiple iterations if something changed that might affect + # inferred types. Also limit the number of iterations. The limits are + # somewhat arbitrary, but they were chosen to 1) avoid slowdown from + # multiple iterations in common cases and 2) support common, valid use + # cases. Limits are needed since otherwise we could infer infinitely + # complex types. + if ( + (partials_new == partials_old) + and (not self.binder.last_pop_changed or iter > 3) + and (widened_new == widened_old or iter > 1) + ): break + partials_old = partials_new + widened_old = widened_new + iter += 1 + if iter == 20: + raise RuntimeError("Too many iterations when checking a loop") + + self.msg.iteration_dependent_errors(iter_errors) + + # If exit_condition is set, assume it must be False on exit from the loop: if exit_condition: _, else_map = self.find_isinstance_check(exit_condition) self.push_type_map(else_map) + + # Check the else body: if else_body: self.accept(else_body) @@ -641,10 +728,40 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # HACK: Infer the type of the property. assert isinstance(defn.items[0], Decorator) self.visit_decorator(defn.items[0]) - for fdef in defn.items: + if defn.items[0].var.is_settable_property: + # Perform a reduced visit just to infer the actual setter type. + self.visit_decorator_inner(defn.setter, skip_first_item=True) + setter_type = defn.setter.var.type + # Check if the setter can accept two positional arguments. + any_type = AnyType(TypeOfAny.special_form) + fallback_setter_type = CallableType( + arg_types=[any_type, any_type], + arg_kinds=[ARG_POS, ARG_POS], + arg_names=[None, None], + ret_type=any_type, + fallback=self.named_type("builtins.function"), + ) + if setter_type and not is_subtype(setter_type, fallback_setter_type): + self.fail("Invalid property setter signature", defn.setter.func) + setter_type = self.extract_callable_type(setter_type, defn) + if not isinstance(setter_type, CallableType) or len(setter_type.arg_types) != 2: + # TODO: keep precise type for callables with tricky but valid signatures. + setter_type = fallback_setter_type + defn.items[0].var.setter_type = setter_type + if isinstance(defn.type, Overloaded): + # Update legacy property type for decorated properties. + getter_type = self.extract_callable_type(defn.items[0].var.type, defn) + if getter_type is not None: + getter_type.definition = defn.items[0] + defn.type.items[0] = getter_type + for i, fdef in enumerate(defn.items): assert isinstance(fdef, Decorator) if defn.is_property: - self.check_func_item(fdef.func, name=fdef.func.name, allow_empty=True) + assert isinstance(defn.items[0], Decorator) + settable = defn.items[0].var.is_settable_property + # Do not visit the second time the items we checked above. + if (settable and i > 1) or (not settable and i > 0): + self.check_func_item(fdef.func, name=fdef.func.name, allow_empty=True) else: # Perform full check for real overloads to infer type of all decorated # overload variants. @@ -654,7 +771,8 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: if num_abstract not in (0, len(defn.items)): self.fail(message_registry.INCONSISTENT_ABSTRACT_OVERLOAD, defn) if defn.impl: - defn.impl.accept(self) + with self.enter_overload_impl(defn.impl): + defn.impl.accept(self) if not defn.is_property: self.check_overlapping_overloads(defn) if defn.type is None: @@ -663,9 +781,26 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: assert isinstance(item, Decorator) item_type = self.extract_callable_type(item.var.type, item) if item_type is not None: + item_type.definition = item item_types.append(item_type) if item_types: defn.type = Overloaded(item_types) + elif defn.type is None: + # We store the getter type as an overall overload type, as some + # code paths are getting property type this way. + assert isinstance(defn.items[0], Decorator) + var_type = self.extract_callable_type(defn.items[0].var.type, defn) + if not isinstance(var_type, CallableType): + # Construct a fallback type, invalid types should be already reported. + any_type = AnyType(TypeOfAny.special_form) + var_type = CallableType( + arg_types=[any_type], + arg_kinds=[ARG_POS], + arg_names=[None], + ret_type=any_type, + fallback=self.named_type("builtins.function"), + ) + defn.type = Overloaded([var_type]) # Check override validity after we analyzed current definition. if defn.info: found_method_base_classes = self.check_method_override(defn) @@ -673,66 +808,85 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: defn.is_explicit_override and not found_method_base_classes and found_method_base_classes is not None + # If the class has Any fallback, we can't be certain that a method + # is really missing - it might come from unfollowed import. + and not defn.info.fallback_to_any ): self.msg.no_overridable_method(defn.name, defn) self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl) self.check_inplace_operator_method(defn) + @contextmanager + def enter_overload_impl(self, impl: OverloadPart) -> Iterator[None]: + self.overload_impl_stack.append(impl) + try: + yield + finally: + assert self.overload_impl_stack.pop() == impl + def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> CallableType | None: """Get type as seen by an overload item caller.""" 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 - ) - if isinstance(inner_type, CallableType): - outer_type = inner_type - elif isinstance(inner_type, Instance): - inner_call = get_proper_type( - analyze_member_access( - name="__call__", - typ=inner_type, - context=ctx, - is_lvalue=False, - is_super=False, - is_operator=True, - msg=self.msg, - original_type=inner_type, - chk=self, - ) + outer_type: FunctionLike | None = None + if inner_type is None or isinstance(inner_type, AnyType): + return None + if isinstance(inner_type, TypeVarLikeType): + inner_type = get_proper_type(inner_type.upper_bound) + if isinstance(inner_type, TypeType): + inner_type = get_proper_type( + self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) + ) + + if isinstance(inner_type, FunctionLike): + outer_type = inner_type + elif isinstance(inner_type, Instance): + inner_call = get_proper_type( + analyze_member_access( + name="__call__", + typ=inner_type, + context=ctx, + is_lvalue=False, + is_super=False, + is_operator=True, + original_type=inner_type, + chk=self, ) - if isinstance(inner_call, CallableType): - outer_type = inner_call - elif isinstance(inner_type, UnionType): - union_type = make_simplified_union(inner_type.items) - if isinstance(union_type, UnionType): - items = [] - for item in union_type.items: - callable_item = self.extract_callable_type(item, ctx) - if callable_item is None: - break - items.append(callable_item) - else: - joined_type = get_proper_type(join.join_type_list(items)) - if isinstance(joined_type, CallableType): - outer_type = joined_type + ) + if isinstance(inner_call, FunctionLike): + outer_type = inner_call + elif isinstance(inner_type, UnionType): + union_type = make_simplified_union(inner_type.items) + if isinstance(union_type, UnionType): + items = [] + for item in union_type.items: + callable_item = self.extract_callable_type(item, ctx) + if callable_item is None: + break + items.append(callable_item) else: - return self.extract_callable_type(union_type, ctx) - if outer_type is None: - self.msg.not_callable(inner_type, ctx) + joined_type = get_proper_type(join.join_type_list(items)) + if isinstance(joined_type, FunctionLike): + outer_type = joined_type + else: + return self.extract_callable_type(union_type, ctx) + + if outer_type is None: + self.msg.not_callable(inner_type, ctx) + return None + if isinstance(outer_type, Overloaded): + return None + + assert isinstance(outer_type, CallableType) return outer_type def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # At this point we should have set the impl already, and all remaining # items are decorators - if self.msg.errors.file in self.msg.errors.ignored_files or ( - self.is_typeshed_stub and self.options.test_env + if ( + self.options.ignore_errors + or self.msg.errors.file in self.msg.errors.ignored_files + or (self.is_typeshed_stub and self.options.test_env) ): # This is a little hacky, however, the quadratic check here is really expensive, this # method has no side effects, so we should skip it if we aren't going to report @@ -1060,46 +1214,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, @@ -1114,6 +1229,7 @@ def check_func_item( """ self.dynamic_funcs.append(defn.is_dynamic() and not type_override) + enclosing_node_deferred = self.current_node_deferred with self.enter_partial_types(is_function=True): typ = self.function_type(defn) if type_override: @@ -1125,7 +1241,7 @@ def check_func_item( raise RuntimeError("Not supported") self.dynamic_funcs.pop() - self.current_node_deferred = False + self.current_node_deferred = enclosing_node_deferred if name == "__exit__": self.check__exit__return_type(defn) @@ -1135,6 +1251,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 @@ -1147,11 +1306,12 @@ 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: old_binder = self.binder - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(self.options) with self.binder.top_frame_context(): defn.expanded.append(item) @@ -1192,7 +1352,11 @@ def check_func_def( ) if name: # Special method names - if defn.info and self.is_reverse_op_method(name): + if ( + defn.info + and self.is_reverse_op_method(name) + and defn not in self.overload_impl_stack + ): self.check_reverse_op_method(item, typ, name, defn) elif name in ("__getattr__", "__getattribute__"): self.check_getattr_method(typ, defn, name) @@ -1253,7 +1417,7 @@ def check_func_def( if typ.type_is: arg_index = 0 # For methods and classmethods, we want the second parameter - if ref_type is not None and (not defn.is_static or defn.name == "__new__"): + if ref_type is not None and defn.has_self_or_cls_argument: arg_index = 1 if arg_index < len(typ.arg_types) and not is_subtype( typ.type_is, typ.arg_types[arg_index] @@ -1267,49 +1431,19 @@ def check_func_def( ) # Store argument types. + found_self = False + if isinstance(defn, FuncDef) and not defn.is_decorated: + found_self = self.require_correct_self_argument(typ, defn) for i in range(len(typ.arg_types)): arg_type = typ.arg_types[i] - if ( - isinstance(defn, FuncDef) - and ref_type is not None - and i == 0 - and (not defn.is_static or defn.name == "__new__") - and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2] - ): - if defn.is_class or defn.name == "__new__": - ref_type = mypy.types.TypeType.make_normalized(ref_type) - if not is_same_type(arg_type, ref_type): - # This level of erasure matches the one in checkmember.check_self_arg(), - # better keep these two checks consistent. - erased = get_proper_type(erase_typevars(erase_to_bound(arg_type))) - if not is_subtype(ref_type, erased, ignore_type_params=True): - if ( - isinstance(erased, Instance) - and erased.type.is_protocol - or isinstance(erased, TypeType) - and isinstance(erased.item, Instance) - and erased.item.type.is_protocol - ): - # We allow the explicit self-type to be not a supertype of - # the current class if it is a protocol. For such cases - # the consistency check will be performed at call sites. - msg = None - elif typ.arg_names[i] in {"self", "cls"}: - msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format( - erased.str_with_options(self.options), - ref_type.str_with_options(self.options), - ) - else: - msg = message_registry.MISSING_OR_INVALID_SELF_TYPE - if msg: - self.fail(msg, defn) - elif isinstance(arg_type, TypeVarType): + if isinstance(arg_type, TypeVarType): # Refuse covariant parameter type variables # TODO: check recursively for inner type variables if ( arg_type.variance == COVARIANT and defn.name not in ("__init__", "__new__", "__post_init__") and not is_private(defn.name) # private methods are not inherited + and (i != 0 or not found_self) ): ctx: Context = arg_type if ctx.line < 0: @@ -1339,6 +1473,17 @@ def check_func_def( new_frame = self.binder.push_frame() new_frame.types[key] = narrowed_type self.binder.declarations[key] = old_binder.declarations[key] + + if self.options.allow_redefinition_new and not self.is_stub: + # Add formal argument types to the binder. + for arg in defn.arguments: + # TODO: Add these directly using a fast path (possibly "put") + v = arg.variable + if v.type is not None: + n = NameExpr(v.name) + n.node = v + self.binder.assign_type(n, v.type, v.type) + with self.scope.push_function(defn): # We suppress reachability warnings for empty generator functions # (return; yield) which have a "yield" that's unreachable by definition @@ -1352,7 +1497,19 @@ def check_func_def( # TODO: Find a way of working around this limitation if _is_empty_generator_function(item) or len(expanded) >= 2: self.binder.suppress_unreachable_warnings() - self.accept(item.body) + # When checking a third-party library, we can skip function body, + # if during semantic analysis we found that there are no attributes + # defined via self here. + if ( + not ( + self.options.ignore_errors + or self.msg.errors.file in self.msg.errors.ignored_files + ) + or self.options.preserve_asts + or not isinstance(defn, FuncDef) + or defn.has_self_attr_def + ): + self.accept(item.body) unreachable = self.binder.is_unreachable() if new_frame is not None: self.binder.pop_frame(True, 0) @@ -1448,12 +1605,79 @@ def check_func_def( self.binder = old_binder + def require_correct_self_argument(self, func: Type, defn: FuncDef) -> bool: + func = get_proper_type(func) + if not isinstance(func, CallableType): + return False + + # Do not report errors for untyped methods in classes nested in untyped funcs. + if not ( + self.options.check_untyped_defs + or len(self.dynamic_funcs) < 2 + or not self.dynamic_funcs[-2] + or not defn.is_dynamic() + ): + return bool(func.arg_types) + + with self.scope.push_function(defn): + # We temporary push the definition to get the self type as + # visible from *inside* of this function/method. + ref_type: Type | None = self.scope.active_self_type() + if ref_type is None: + return False + + if not defn.has_self_or_cls_argument or ( + func.arg_kinds and func.arg_kinds[0] in [nodes.ARG_STAR, nodes.ARG_STAR2] + ): + return False + + if not func.arg_types: + self.fail( + 'Method must have at least one argument. Did you forget the "self" argument?', defn + ) + return False + + arg_type = func.arg_types[0] + if defn.is_class or defn.name == "__new__": + ref_type = mypy.types.TypeType.make_normalized(ref_type) + if is_same_type(arg_type, ref_type): + return True + + # This level of erasure matches the one in checkmember.check_self_arg(), + # better keep these two checks consistent. + erased = get_proper_type(erase_typevars(erase_to_bound(arg_type))) + if not is_subtype(ref_type, erased, ignore_type_params=True): + if ( + isinstance(erased, Instance) + and erased.type.is_protocol + or isinstance(erased, TypeType) + and isinstance(erased.item, Instance) + and erased.item.type.is_protocol + ): + # We allow the explicit self-type to be not a supertype of + # the current class if it is a protocol. For such cases + # the consistency check will be performed at call sites. + msg = None + elif func.arg_names[0] in {"self", "cls"}: + msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format( + erased.str_with_options(self.options), ref_type.str_with_options(self.options) + ) + else: + msg = message_registry.MISSING_OR_INVALID_SELF_TYPE + if msg: + self.fail(msg, defn) + return True + def is_var_redefined_in_outer_context(self, v: Var, after_line: int) -> bool: """Can the variable be assigned to at module top level or outer function? Note that this doesn't do a full CFG analysis but uses a line number based heuristic that isn't correct in some (rare) cases. """ + if v.is_final: + # Final vars are definitely never reassigned. + return False + outers = self.tscope.outer_functions() if not outers: # Top-level function -- outer context is top level, and we can't reason about @@ -1585,7 +1809,8 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None: "but must return a subtype of", ) elif not isinstance( - get_proper_type(bound_type.ret_type), (AnyType, Instance, TupleType, UninhabitedType) + get_proper_type(bound_type.ret_type), + (AnyType, Instance, TupleType, UninhabitedType, LiteralType), ): self.fail( message_registry.NON_INSTANCE_NEW_TYPE.format( @@ -1968,13 +2193,19 @@ def check_method_override( Return a list of base classes which contain an attribute with the method name. """ + if self.options.ignore_errors or self.msg.errors.file in self.msg.errors.ignored_files: + # Method override checks may be expensive, so skip them in third-party libraries. + return None # Check against definitions in base classes. - check_override_compatibility = defn.name not in ( - "__init__", - "__new__", - "__init_subclass__", - "__post_init__", - ) and (self.options.check_untyped_defs or not defn.is_dynamic()) + check_override_compatibility = ( + defn.name not in ("__init__", "__new__", "__init_subclass__", "__post_init__") + and (self.options.check_untyped_defs or not defn.is_dynamic()) + and ( + # don't check override for synthesized __replace__ methods from dataclasses + defn.name != "__replace__" + or defn.info.metadata.get("dataclass_tag") is None + ) + ) found_method_base_classes: list[TypeInfo] = [] for base in defn.info.mro[1:]: result = self.check_method_or_accessor_override_for_base( @@ -2028,6 +2259,21 @@ def check_method_or_accessor_override_for_base( return None return found_base_method + def check_setter_type_override(self, defn: OverloadedFuncDef, base: TypeInfo) -> None: + """Check override of a setter type of a mutable attribute. + + Currently, this should be only called when either base node or the current node + is a custom settable property (i.e. where setter type is different from getter type). + Note that this check is contravariant. + """ + typ, _ = self.node_type_from_base(defn.name, defn.info, defn, setter_type=True) + original_type, _ = self.node_type_from_base(defn.name, base, defn, setter_type=True) + # The caller should handle deferrals. + assert typ is not None and original_type is not None + + if not is_subtype(original_type, typ): + self.msg.incompatible_setter_override(defn.setter, typ, original_type, base) + def check_method_override_for_base_with_name( self, defn: FuncDef | OverloadedFuncDef | Decorator, name: str, base: TypeInfo ) -> bool: @@ -2036,173 +2282,176 @@ def check_method_override_for_base_with_name( Return True if the supertype node was not analysed yet, and `defn` was deferred. """ base_attr = base.names.get(name) - if base_attr: - # The name of the method is defined in the base class. + if not base_attr: + return False + # The name of the method is defined in the base class. - # Point errors at the 'def' line (important for backward compatibility - # of type ignores). - if not isinstance(defn, Decorator): - context = defn - else: - context = defn.func - - # Construct the type of the overriding method. - # TODO: this logic is much less complete than similar one in checkmember.py - if isinstance(defn, (FuncDef, OverloadedFuncDef)): - typ: Type = self.function_type(defn) - override_class_or_static = defn.is_class or defn.is_static - override_class = defn.is_class - else: - assert defn.var.is_ready - assert defn.var.type is not None - typ = defn.var.type - override_class_or_static = defn.func.is_class or defn.func.is_static - override_class = defn.func.is_class - typ = get_proper_type(typ) - if isinstance(typ, FunctionLike) and not is_static(context): - typ = bind_self(typ, self.scope.active_self_type(), is_classmethod=override_class) - # Map the overridden method type to subtype context so that - # it can be checked for compatibility. - original_type = get_proper_type(base_attr.type) - original_node = base_attr.node - # `original_type` can be partial if (e.g.) it is originally an - # instance variable from an `__init__` block that becomes deferred. - if original_type is None or isinstance(original_type, PartialType): - if self.pass_num < self.last_pass: - # If there are passes left, defer this node until next pass, - # otherwise try reconstructing the method type from available information. + # Point errors at the 'def' line (important for backward compatibility + # of type ignores). + if not isinstance(defn, Decorator): + context = defn + else: + context = defn.func + + # Construct the type of the overriding method. + if isinstance(defn, (FuncDef, OverloadedFuncDef)): + override_class_or_static = defn.is_class or defn.is_static + else: + override_class_or_static = defn.func.is_class or defn.func.is_static + typ, _ = self.node_type_from_base(defn.name, defn.info, defn) + if typ is None: + # This may only happen if we're checking `x-redefinition` member + # and `x` itself is for some reason gone. Normally the node should + # be reachable from the containing class by its name. + # The redefinition is never removed, use this as a sanity check to verify + # the reasoning above. + assert f"{defn.name}-redefinition" in defn.info.names + return False + + original_node = base_attr.node + # `original_type` can be partial if (e.g.) it is originally an + # instance variable from an `__init__` block that becomes deferred. + supertype_ready = True + original_type, _ = self.node_type_from_base(name, base, defn) + if original_type is None: + supertype_ready = False + if self.pass_num < self.last_pass: + # If there are passes left, defer this node until next pass, + # otherwise try reconstructing the method type from available information. + # For consistency, defer an enclosing top-level function (if any). + top_level = self.scope.top_level_function() + if isinstance(top_level, FuncDef): + self.defer_node(top_level, self.scope.enclosing_class(top_level)) + else: + # Specify enclosing class explicitly, as we check type override before + # entering e.g. decorators or overloads. self.defer_node(defn, defn.info) - return True - elif isinstance(original_node, (FuncDef, OverloadedFuncDef)): - original_type = self.function_type(original_node) - elif isinstance(original_node, Decorator): - original_type = self.function_type(original_node.func) - elif isinstance(original_node, Var): - # Super type can define method as an attribute. - # See https://github.com/python/mypy/issues/10134 - - # We also check that sometimes `original_node.type` is None. - # This is the case when we use something like `__hash__ = None`. - if original_node.type is not None: - original_type = get_proper_type(original_node.type) - else: - original_type = NoneType() + return True + elif isinstance(original_node, (FuncDef, OverloadedFuncDef)): + original_type = self.function_type(original_node) + elif isinstance(original_node, Decorator): + original_type = self.function_type(original_node.func) + elif isinstance(original_node, Var): + # Super type can define method as an attribute. + # See https://github.com/python/mypy/issues/10134 + + # We also check that sometimes `original_node.type` is None. + # This is the case when we use something like `__hash__ = None`. + if original_node.type is not None: + original_type = get_proper_type(original_node.type) else: - # Will always fail to typecheck below, since we know the node is a method original_type = NoneType() - if isinstance(original_node, (FuncDef, OverloadedFuncDef)): - original_class_or_static = original_node.is_class or original_node.is_static - elif isinstance(original_node, Decorator): - fdef = original_node.func - original_class_or_static = fdef.is_class or fdef.is_static else: - original_class_or_static = False # a variable can't be class or static + # Will always fail to typecheck below, since we know the node is a method + original_type = NoneType() - if isinstance(original_type, FunctionLike): - original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base) - if original_node and is_property(original_node): - original_type = get_property_type(original_type) + always_allow_covariant = False + if is_settable_property(defn) and ( + is_settable_property(original_node) or isinstance(original_node, Var) + ): + if is_custom_settable_property(defn) or (is_custom_settable_property(original_node)): + # Unlike with getter, where we try to construct some fallback type in case of + # deferral during last_pass, we can't make meaningful setter checks if the + # supertype is not known precisely. + if supertype_ready: + always_allow_covariant = True + self.check_setter_type_override(defn, base) + + if isinstance(original_node, (FuncDef, OverloadedFuncDef)): + original_class_or_static = original_node.is_class or original_node.is_static + elif isinstance(original_node, Decorator): + fdef = original_node.func + original_class_or_static = fdef.is_class or fdef.is_static + else: + original_class_or_static = False # a variable can't be class or static - if is_property(defn): - inner: FunctionLike | None - if isinstance(typ, FunctionLike): - inner = typ - else: - inner = self.extract_callable_type(typ, context) - if inner is not None: - typ = inner - typ = get_property_type(typ) - if ( - isinstance(original_node, Var) - and not original_node.is_final - and (not original_node.is_property or original_node.is_settable_property) - and isinstance(defn, Decorator) - ): - # We only give an error where no other similar errors will be given. - if not isinstance(original_type, AnyType): - self.msg.fail( - "Cannot override writeable attribute with read-only property", - # Give an error on function line to match old behaviour. - defn.func, - code=codes.OVERRIDE, - ) + typ = get_proper_type(typ) + original_type = get_proper_type(original_type) - if isinstance(original_type, AnyType) or isinstance(typ, AnyType): - pass - elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): - # Check that the types are compatible. - self.check_override( - typ, - original_type, - defn.name, - name, - base.name, - original_class_or_static, - override_class_or_static, - context, + if ( + is_property(defn) + and isinstance(original_node, Var) + and not original_node.is_final + and (not original_node.is_property or original_node.is_settable_property) + and isinstance(defn, Decorator) + ): + # We only give an error where no other similar errors will be given. + if not isinstance(original_type, AnyType): + self.msg.fail( + "Cannot override writeable attribute with read-only property", + # Give an error on function line to match old behaviour. + defn.func, + code=codes.OVERRIDE, ) - 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 - # covariant overrides. - pass - elif ( + + if isinstance(original_type, AnyType) or isinstance(typ, AnyType): + pass + elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): + # Check that the types are compatible. + ok = self.check_override( + typ, + original_type, + defn.name, + name, + base.name if base.module_name == self.tree.fullname else base.fullname, + original_class_or_static, + 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 always_allow_covariant + 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 not self.is_writable_attribute(original_node) - and is_subtype(typ, original_type) + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + and not always_allow_covariant ): - # If the attribute is read-only, allow covariance - pass - else: - self.msg.signature_incompatible_with_supertype( - defn.name, name, base.name, context, original=original_type, override=typ + # Covariant override of mutable attribute. + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options ) - return False - - def bind_and_map_method( - self, sym: SymbolTableNode, typ: FunctionLike, sub_info: TypeInfo, super_info: TypeInfo - ) -> FunctionLike: - """Bind self-type and map type variables for a method. - - Arguments: - sym: a symbol that points to method definition - typ: method type on the definition - sub_info: class where the method is used - super_info: class where the method was defined - """ - if isinstance(sym.node, (FuncDef, OverloadedFuncDef, Decorator)) and not is_static( - sym.node + 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 + # covariant overrides. + pass + elif ( + original_node + and (not self.is_writable_attribute(original_node) or always_allow_covariant) + and is_subtype(typ, original_type) ): - if isinstance(sym.node, Decorator): - is_class_method = sym.node.func.is_class - else: - is_class_method = sym.node.is_class - - mapped_typ = cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info)) - active_self_type = self.scope.active_self_type() - if isinstance(mapped_typ, Overloaded) and active_self_type: - # If we have an overload, filter to overloads that match the self type. - # This avoids false positives for concrete subclasses of generic classes, - # see testSelfTypeOverrideCompatibility for an example. - filtered_items = [] - for item in mapped_typ.items: - if not item.arg_types: - filtered_items.append(item) - item_arg = item.arg_types[0] - if isinstance(item_arg, TypeVarType): - item_arg = item_arg.upper_bound - if is_subtype(active_self_type, item_arg): - filtered_items.append(item) - # If we don't have any filtered_items, maybe it's always a valid override - # of the superclass? However if you get to that point you're in murky type - # territory anyway, so we just preserve the type and have the behaviour match - # that of older versions of mypy. - if filtered_items: - mapped_typ = Overloaded(filtered_items) - - return bind_self(mapped_typ, active_self_type, is_class_method) + # If the attribute is read-only, allow covariance + pass else: - return cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info)) + self.msg.signature_incompatible_with_supertype( + defn.name, name, base.name, context, original=original_type, override=typ + ) + return False def get_op_other_domain(self, tp: FunctionLike) -> Type | None: if isinstance(tp, CallableType): @@ -2230,7 +2479,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: @@ -2304,8 +2553,9 @@ def check_override( override_ids = override.type_var_ids() type_name = None - if isinstance(override.definition, FuncDef): - type_name = override.definition.info.name + definition = get_func_def(override) + if isinstance(definition, FuncDef): + type_name = definition.info.name def erase_override(t: Type) -> Type: return erase_typevars(t, ids_to_erase=override_ids) @@ -2327,10 +2577,22 @@ def erase_override(t: Type) -> Type: else: continue if not is_subtype(original_arg_type, erase_override(override_arg_type)): - if isinstance(node, FuncDef) and not node.is_property: - context: Context = node.arguments[i + len(override.bound_args)] - else: - context = node + context: Context = node + if ( + isinstance(node, FuncDef) + and not node.is_property + and ( + not node.is_decorated # fast path + # allow trivial decorators like @classmethod and @override + or not (sym := node.info.get(node.name)) + or not isinstance(sym.node, Decorator) + or not sym.node.decorators + ) + ): + # If there's any decorator, we can no longer map arguments 1:1 reliably. + arg_node = node.arguments[i + override.bound()] + if arg_node.line != -1: + context = arg_node self.msg.argument_incompatible_with_supertype( i + 1, name, @@ -2380,6 +2642,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. @@ -2412,9 +2675,15 @@ def visit_class_def(self, defn: ClassDef) -> None: for base in typ.mro[1:]: if base.is_final: self.fail(message_registry.CANNOT_INHERIT_FROM_FINAL.format(base.name), defn) - with self.tscope.class_scope(defn.info), self.enter_partial_types(is_class=True): + if not can_have_shared_disjoint_base(typ.bases): + self.fail(message_registry.INCOMPATIBLE_DISJOINT_BASES.format(typ.name), defn) + with ( + self.tscope.class_scope(defn.info), + self.enter_partial_types(is_class=True), + self.enter_class(defn.info), + ): old_binder = self.binder - self.binder = ConditionalTypeBinder() + self.binder = ConditionalTypeBinder(self.options) with self.binder.top_frame_context(): with self.scope.push_class(defn.info): self.accept(defn.defs) @@ -2471,6 +2740,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) @@ -2478,6 +2749,15 @@ def visit_class_def(self, defn: ClassDef) -> None: self.check_enum(defn) infer_class_variances(defn.info) + @contextmanager + def enter_class(self, type: TypeInfo) -> Iterator[None]: + original_type = self.type + self.type = type + try: + yield + finally: + self.type = original_type + def check_final_deletable(self, typ: TypeInfo) -> None: # These checks are only for mypyc. Only perform some checks that are easier # to implement here than in mypyc. @@ -2534,30 +2814,47 @@ 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(is_same_type(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_OVERRIDDEN, 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) def check_final_enum(self, defn: ClassDef, base: TypeInfo) -> None: - for sym in base.names.values(): - if self.is_final_enum_value(sym): - self.fail(f'Cannot extend enum with existing members: "{base.name}"', defn) - break + if base.enum_members: + self.fail(f'Cannot extend enum with existing members: "{base.name}"', defn) def is_final_enum_value(self, sym: SymbolTableNode) -> bool: if isinstance(sym.node, (FuncBase, Decorator)): @@ -2653,17 +2950,19 @@ def check_protocol_variance(self, defn: ClassDef) -> None: info = defn.info object_type = Instance(info.mro[-1], []) tvars = info.defn.type_vars + if self._variance_dummy_type is None: + _, dummy_info = self.make_fake_typeinfo("", "Dummy", "Dummy", []) + self._variance_dummy_type = Instance(dummy_info, []) + dummy = self._variance_dummy_type for i, tvar in enumerate(tvars): if not isinstance(tvar, TypeVarType): # Variance of TypeVarTuple and ParamSpec is underspecified by PEPs. continue up_args: list[Type] = [ - object_type if i == j else AnyType(TypeOfAny.special_form) - for j, _ in enumerate(tvars) + object_type if i == j else dummy.copy_modified() for j, _ in enumerate(tvars) ] down_args: list[Type] = [ - UninhabitedType() if i == j else AnyType(TypeOfAny.special_form) - for j, _ in enumerate(tvars) + UninhabitedType() if i == j else dummy.copy_modified() for j, _ in enumerate(tvars) ] up, down = Instance(info, up_args), Instance(info, down_args) # TODO: add advanced variance checks for recursive protocols @@ -2683,42 +2982,20 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None: return # Verify that inherited attributes are compatible. mro = typ.mro[1:] - for i, base in enumerate(mro): + all_names = {name for base in mro for name in base.names} + for name in sorted(all_names - typ.names.keys()): + # Sort for reproducible message order. # Attributes defined in both the type and base are skipped. # Normal checks for attribute compatibility should catch any problems elsewhere. - non_overridden_attrs = base.names.keys() - typ.names.keys() - for name in non_overridden_attrs: - if is_private(name): - continue - for base2 in mro[i + 1 :]: - # We only need to check compatibility of attributes from classes not - # in a subclass relationship. For subclasses, normal (single inheritance) - # checks suffice (these are implemented elsewhere). - if name in base2.names and base2 not in base.mro: - self.check_compatibility(name, base, base2, typ) - - def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None: - if sym.type is not None: - return sym.type - if isinstance(sym.node, FuncBase): - return self.function_type(sym.node) - if isinstance(sym.node, TypeInfo): - if sym.node.typeddict_type: - # We special-case TypedDict, because they don't define any constructor. - return self.expr_checker.typeddict_callable(sym.node) - else: - return type_object_type(sym.node, self.named_type) - if isinstance(sym.node, TypeVarExpr): - # Use of TypeVars is rejected in an expression/runtime context, so - # we don't need to check supertype compatibility for them. - return AnyType(TypeOfAny.special_form) - if isinstance(sym.node, TypeAlias): - with self.msg.filter_errors(): - # Suppress any errors, they will be given when analyzing the corresponding node. - # Here we may have incorrect options and location context. - return self.expr_checker.alias_type_in_runtime_context(sym.node, ctx=sym.node) - # TODO: handle more node kinds here. - return None + if is_private(name): + continue + # Compare the first base defining a name with the rest. + # Remaining bases may not be pairwise compatible as the first base provides + # the used definition. + i, base = next((i, base) for i, base in enumerate(mro) if name in base.names) + for base2 in mro[i + 1 :]: + if name in base2.names and base2 not in base.mro: + self.check_compatibility(name, base, base2, typ) def check_compatibility( self, name: str, base1: TypeInfo, base2: TypeInfo, ctx: TypeInfo @@ -2748,47 +3025,47 @@ class C(B, A[int]): ... # this is unsafe because... return first = base1.names[name] second = base2.names[name] - first_type = get_proper_type(self.determine_type_of_member(first)) - second_type = get_proper_type(self.determine_type_of_member(second)) + # Specify current_class explicitly as this function is called after leaving the class. + first_type, _ = self.node_type_from_base(name, base1, ctx, current_class=ctx) + second_type, _ = self.node_type_from_base(name, base2, ctx, current_class=ctx) # TODO: use more principled logic to decide is_subtype() vs is_equivalent(). # We should rely on mutability of superclass node, not on types being Callable. + # (in particular handle settable properties with setter type different from getter). - # start with the special case that Instance can be a subtype of FunctionLike - call = None - if isinstance(first_type, Instance): - call = find_member("__call__", first_type, first_type, is_operator=True) - if call and isinstance(second_type, FunctionLike): - second_sig = self.bind_and_map_method(second, second_type, ctx, base2) - ok = is_subtype(call, second_sig, ignore_pos_arg_names=True) - elif isinstance(first_type, FunctionLike) and isinstance(second_type, FunctionLike): - if first_type.is_type_obj() and second_type.is_type_obj(): + p_first_type = get_proper_type(first_type) + p_second_type = get_proper_type(second_type) + if isinstance(p_first_type, FunctionLike) and isinstance(p_second_type, FunctionLike): + if p_first_type.is_type_obj() and p_second_type.is_type_obj(): # For class objects only check the subtype relationship of the classes, # since we allow incompatible overrides of '__init__'/'__new__' ok = is_subtype( - left=fill_typevars_with_any(first_type.type_object()), - right=fill_typevars_with_any(second_type.type_object()), + left=fill_typevars_with_any(p_first_type.type_object()), + right=fill_typevars_with_any(p_second_type.type_object()), ) else: - # First bind/map method types when necessary. - first_sig = self.bind_and_map_method(first, first_type, ctx, base1) - second_sig = self.bind_and_map_method(second, second_type, ctx, base2) - ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True) + assert first_type and second_type + ok = is_subtype(first_type, second_type, ignore_pos_arg_names=True) elif first_type and second_type: - if isinstance(first.node, Var): - first_type = expand_self_type(first.node, first_type, fill_typevars(ctx)) - if isinstance(second.node, Var): - second_type = expand_self_type(second.node, second_type, fill_typevars(ctx)) - ok = is_equivalent(first_type, second_type) - if not ok: - second_node = base2[name].node + if second.node is not None and not self.is_writable_attribute(second.node): + ok = is_subtype(first_type, second_type) + else: + ok = is_equivalent(first_type, second_type) + if ok: if ( - isinstance(second_type, FunctionLike) - and second_node is not None - and is_property(second_node) + first.node + and second.node + and self.is_writable_attribute(second.node) + and is_property(first.node) + and isinstance(first.node, Decorator) + and not isinstance(p_second_type, AnyType) ): - second_type = get_property_type(second_type) - ok = is_subtype(first_type, second_type) + self.msg.fail( + f'Cannot override writeable attribute "{name}" in base "{base2.name}"' + f' with read-only property in base "{base1.name}"', + ctx, + code=codes.OVERRIDE, + ) else: if first_type is None: self.msg.cannot_determine_type_in_base(name, base1.name, ctx) @@ -2819,25 +3096,23 @@ def check_metaclass_compatibility(self, typ: TypeInfo) -> None: ): return # Reasonable exceptions from this check - metaclasses = [ - entry.metaclass_type - for entry in typ.mro[1:-1] - if entry.metaclass_type - and not is_named_instance(entry.metaclass_type, "builtins.type") - ] - if not metaclasses: - return - if typ.metaclass_type is not None and all( - is_subtype(typ.metaclass_type, meta) for meta in metaclasses + if typ.metaclass_type is None and any( + base.type.metaclass_type is not None for base in typ.bases ): - return - self.fail( - "Metaclass conflict: the metaclass of a derived class must be " - "a (non-strict) subclass of the metaclasses of all its bases", - typ, - ) + self.fail( + "Metaclass conflict: the metaclass of a derived class must be " + "a (non-strict) subclass of the metaclasses of all its bases", + typ, + code=codes.METACLASS, + ) + explanation = typ.explain_metaclass_conflict() + if explanation: + self.note(explanation, typ, code=codes.METACLASS) 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: @@ -2884,6 +3159,8 @@ def visit_block(self, b: Block) -> None: break else: self.accept(s) + # Clear expression cache after each statement to avoid unlimited growth. + self.expr_checker.expr_cache.clear() def should_report_unreachable_issues(self) -> bool: return ( @@ -2904,13 +3181,15 @@ def is_noop_for_reachability(self, s: Statement) -> bool: """ if isinstance(s, AssertStmt) and is_false_literal(s.expr): return True + elif isinstance(s, ReturnStmt) and is_literal_not_implemented(s.expr): + return True elif isinstance(s, (RaiseStmt, PassStmt)): return True elif isinstance(s, ExpressionStmt): if isinstance(s.expr, EllipsisExpr): return True elif isinstance(s.expr, CallExpr): - with self.expr_checker.msg.filter_errors(): + with self.expr_checker.msg.filter_errors(filter_revealed_type=True): typ = get_proper_type( self.expr_checker.accept( s.expr, allow_none_return=True, always_allow_any=True @@ -2926,6 +3205,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. @@ -2992,7 +3272,7 @@ def check_assignment( ) else: self.try_infer_partial_generic_type_from_assignment(lvalue, rvalue, "=") - lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue) + lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue, rvalue) # If we're assigning to __getattr__ or similar methods, check that the signature is # valid. if isinstance(lvalue, NameExpr) and lvalue.node: @@ -3009,7 +3289,7 @@ def check_assignment( else: self.check_getattr_method(signature, lvalue, name) - if name == "__slots__": + if name == "__slots__" and self.scope.active_class() is not None: typ = lvalue_type or self.expr_checker.accept(rvalue) self.check_slots_definition(typ, lvalue) if name == "__match_args__" and inferred is not None: @@ -3020,16 +3300,6 @@ def check_assignment( if active_class and dataclasses_plugin.is_processed_dataclass(active_class): self.fail(message_registry.DATACLASS_POST_INIT_MUST_BE_A_FUNCTION, rvalue) - # Defer PartialType's super type checking. - if ( - isinstance(lvalue, RefExpr) - and not (isinstance(lvalue_type, PartialType) and lvalue_type.type is None) - and not (isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__") - ): - if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue): - # We hit an error on this line; don't check for any others - return - if isinstance(lvalue, MemberExpr) and lvalue.name == "__match_args__": self.fail(message_registry.CANNOT_MODIFY_MATCH_ARGS, lvalue) @@ -3043,7 +3313,9 @@ def check_assignment( return var = lvalue_type.var - if is_valid_inferred_type(rvalue_type, is_lvalue_final=var.is_final): + if is_valid_inferred_type( + rvalue_type, self.options, is_lvalue_final=var.is_final + ): partial_types = self.find_partial_types(var) if partial_types is not None: if not self.current_node_deferred: @@ -3056,15 +3328,19 @@ def check_assignment( del partial_types[var] lvalue_type = var.type else: - # Try to infer a partial type. No need to check the return value, as - # an error will be reported elsewhere. - self.infer_partial_type(lvalue_type.var, lvalue, rvalue_type) - # Handle None PartialType's super type checking here, after it's resolved. - if isinstance(lvalue, RefExpr) and self.check_compatibility_all_supers( - lvalue, lvalue_type, rvalue - ): - # We hit an error on this line; don't check for any others - return + # Try to infer a partial type. + if not self.infer_partial_type(var, lvalue, rvalue_type): + # If that also failed, give up and let the caller know that we + # cannot read their mind. The definition site will be reported later. + # Calling .put() directly because the newly inferred type is + # not a subtype of None - we are not looking for narrowing + fallback = self.inference_error_fallback_type(rvalue_type) + self.binder.put(lvalue, fallback) + # Same as self.set_inference_error_fallback_type but inlined + # to avoid computing fallback twice. + # We are replacing partial now, so the variable type + # should remain optional. + self.set_inferred_type(var, lvalue, make_optional_type(fallback)) elif ( is_literal_none(rvalue) and isinstance(lvalue, NameExpr) @@ -3079,7 +3355,7 @@ def check_assignment( ): # Ignore member access to modules instance_type = self.expr_checker.accept(lvalue.expr) rvalue_type, lvalue_type, infer_lvalue_type = self.check_member_assignment( - instance_type, lvalue_type, rvalue, context=rvalue + lvalue, instance_type, lvalue_type, rvalue, context=rvalue ) else: # Hacky special case for assigning a literal None @@ -3089,7 +3365,8 @@ def check_assignment( # unpleasant, and a generalization of this would # be an improvement! if ( - is_literal_none(rvalue) + not self.options.allow_redefinition_new + and is_literal_none(rvalue) and isinstance(lvalue, NameExpr) and lvalue.kind == LDEF and isinstance(lvalue.node, Var) @@ -3109,7 +3386,12 @@ def check_assignment( lvalue_type = make_optional_type(lvalue_type) self.set_inferred_type(lvalue.node, lvalue, lvalue_type) - rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, context=rvalue) + rvalue_type, lvalue_type = self.check_simple_assignment( + lvalue_type, rvalue, context=rvalue, inferred=inferred, lvalue=lvalue + ) + # The above call may update inferred variable type. Prevent further + # inference. + inferred = None # Special case: only non-abstract non-protocol classes can be assigned to # variables with explicit type Type[A], where A is protocol or abstract. @@ -3133,30 +3415,65 @@ def check_assignment( if rvalue_type and infer_lvalue_type and not isinstance(lvalue_type, PartialType): # 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) + self.binder.assign_type(lvalue, rvalue_type, lvalue_type) + 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 ( + self.options.allow_redefinition_new + and lvalue_type is not None + and not isinstance(lvalue_type, PartialType) + ): + # TODO: Can we use put() here? + self.binder.assign_type(lvalue, lvalue_type, lvalue_type) elif index_lvalue: self.check_indexed_assignment(index_lvalue, rvalue, lvalue) if inferred: - type_context = self.get_variable_type_context(inferred) + type_context = self.get_variable_type_context(inferred, rvalue) 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) self.infer_variable_type(inferred, lvalue, rvalue_type, rvalue) self.check_assignment_to_slots(lvalue) + if isinstance(lvalue, RefExpr) and not ( + isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__" + ): + # We check override here at the end after storing the inferred type, since + # override check will try to access the current attribute via symbol tables + # (like a regular attribute access). + self.check_compatibility_all_supers(lvalue, rvalue) # (type, operator) tuples for augmented assignments supported with partial types partial_type_augmented_ops: Final = {("builtins.list", "+"), ("builtins.set", "|")} - def get_variable_type_context(self, inferred: Var) -> Type | None: + def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type | None: type_contexts = [] if inferred.info: for base in inferred.info.mro[1:]: - base_type, base_node = self.lvalue_type_from_base(inferred, base) + if inferred.name not in base.names: + continue + # For inference within class body, get supertype attribute as it would look on + # a class object for lambdas overriding methods, etc. + base_node = base.names[inferred.name].node + base_type, _ = self.node_type_from_base( + inferred.name, + base, + inferred, + is_class=is_method(base_node) + or isinstance(base_node, Var) + and not is_instance_var(base_node), + ) if ( base_type and not (isinstance(base_node, Var) and base_node.invalid_partial_type) @@ -3165,6 +3482,12 @@ def get_variable_type_context(self, inferred: Var) -> Type | None: type_contexts.append(base_type) # Use most derived supertype as type context if available. if not type_contexts: + if inferred.name == "__slots__" and self.scope.active_class() is not None: + str_type = self.named_type("builtins.str") + return self.named_generic_type("typing.Iterable", [str_type]) + if inferred.name == "__all__" and self.scope.is_top_level(): + str_type = self.named_type("builtins.str") + return self.named_generic_type("typing.Sequence", [str_type]) return None candidate = type_contexts[0] for other in type_contexts: @@ -3214,22 +3537,30 @@ def try_infer_partial_generic_type_from_assignment( rvalue_type = self.expr_checker.accept(rvalue) rvalue_type = get_proper_type(rvalue_type) if isinstance(rvalue_type, Instance): - if rvalue_type.type == typ.type and is_valid_inferred_type(rvalue_type): + if rvalue_type.type == typ.type and is_valid_inferred_type( + rvalue_type, self.options + ): var.type = rvalue_type del partial_types[var] elif isinstance(rvalue_type, AnyType): var.type = fill_typevars_with_any(typ.type) del partial_types[var] - def check_compatibility_all_supers( - self, lvalue: RefExpr, lvalue_type: Type | None, rvalue: Expression - ) -> bool: + def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) -> None: lvalue_node = lvalue.node # Check if we are a class variable with at least one base class if ( isinstance(lvalue_node, Var) - and lvalue.kind in (MDEF, None) - and len(lvalue_node.info.bases) > 0 # None for Vars defined via self + # If we have explicit annotation, there is no point in checking the override + # for each assignment, so we check only for the first one. + # TODO: for some reason annotated attributes on self are stored as inferred vars. + and ( + lvalue_node.line == lvalue.line + or lvalue_node.is_inferred + and not lvalue_node.explicit_self_type + ) + and lvalue.kind in (MDEF, None) # None for Vars defined via self + and len(lvalue_node.info.bases) > 0 ): for base in lvalue_node.info.mro[1:]: tnode = base.names.get(lvalue_node.name) @@ -3245,6 +3576,23 @@ def check_compatibility_all_supers( direct_bases = lvalue_node.info.direct_base_classes() last_immediate_base = direct_bases[-1] if direct_bases else None + # The historical behavior for inferred vars was to compare rvalue type against + # the type declared in a superclass. To preserve this behavior, we temporarily + # store the rvalue type on the variable. + actual_lvalue_type = None + if lvalue_node.is_inferred and not lvalue_node.explicit_self_type: + # Don't use partial types as context, similar to regular code path. + ctx = lvalue_node.type if not isinstance(lvalue_node.type, PartialType) else None + rvalue_type = self.expr_checker.accept(rvalue, ctx) + actual_lvalue_type = lvalue_node.type + lvalue_node.type = rvalue_type + lvalue_type, _ = self.node_type_from_base(lvalue_node.name, lvalue_node.info, lvalue) + if lvalue_node.is_inferred and not lvalue_node.explicit_self_type: + lvalue_node.type = actual_lvalue_type + + if not lvalue_type: + return + for base in lvalue_node.info.mro[1:]: # The type of "__slots__" and some other attributes usually doesn't need to # be compatible with a base class. We'll still check the type of "__slots__" @@ -3257,147 +3605,136 @@ def check_compatibility_all_supers( if is_private(lvalue_node.name): continue - base_type, base_node = self.lvalue_type_from_base(lvalue_node, base) + base_type, base_node = self.node_type_from_base(lvalue_node.name, base, lvalue) + # TODO: if the r.h.s. is a descriptor, we should check setter override as well. + custom_setter = is_custom_settable_property(base_node) if isinstance(base_type, PartialType): base_type = None if base_type: assert base_node is not None if not self.check_compatibility_super( - lvalue, lvalue_type, rvalue, base, base_type, base_node + lvalue_type, + rvalue, + base, + base_type, + base_node, + always_allow_covariant=custom_setter, ): # Only show one error per variable; even if other # base classes are also incompatible - return True + return + if lvalue_type and custom_setter: + base_type, _ = self.node_type_from_base( + lvalue_node.name, base, lvalue, setter_type=True + ) + # Setter type for a custom property must be ready if + # the getter type is ready. + assert base_type is not None + if not is_subtype(base_type, lvalue_type): + self.msg.incompatible_setter_override( + lvalue, lvalue_type, base_type, base + ) + return if base is last_immediate_base: # At this point, the attribute was found to be compatible with all # immediate parents. break - return False def check_compatibility_super( self, - lvalue: RefExpr, - lvalue_type: Type | None, + compare_type: Type, rvalue: Expression, base: TypeInfo, base_type: Type, base_node: Node, + always_allow_covariant: bool, ) -> bool: - lvalue_node = lvalue.node - assert isinstance(lvalue_node, Var) - - # Do not check whether the rvalue is compatible if the - # lvalue had a type defined; this is handled by other - # parts, and all we have to worry about in that case is - # that lvalue is compatible with the base class. - compare_node = None - if lvalue_type: - compare_type = lvalue_type - compare_node = lvalue.node - else: - compare_type = self.expr_checker.accept(rvalue, base_type) - if isinstance(rvalue, NameExpr): - compare_node = rvalue.node - if isinstance(compare_node, Decorator): - compare_node = compare_node.func - - base_type = get_proper_type(base_type) - compare_type = get_proper_type(compare_type) - if compare_type: - if isinstance(base_type, CallableType) and isinstance(compare_type, CallableType): - base_static = is_node_static(base_node) - compare_static = is_node_static(compare_node) - - # In case compare_static is unknown, also check - # if 'definition' is set. The most common case for - # this is with TempNode(), where we lose all - # information about the real rvalue node (but only get - # the rvalue type) - if compare_static is None and compare_type.definition: - compare_static = is_node_static(compare_type.definition) - - # Compare against False, as is_node_static can return None - if base_static is False and compare_static is False: - # Class-level function objects and classmethods become bound - # methods: the former to the instance, the latter to the - # class - base_type = bind_self(base_type, self.scope.active_self_type()) - compare_type = bind_self(compare_type, self.scope.active_self_type()) - - # If we are a static method, ensure to also tell the - # lvalue it now contains a static method - if base_static and compare_static: - lvalue_node.is_staticmethod = True - + # TODO: check __set__() type override for custom descriptors. + # TODO: for descriptors check also class object access override. + ok = self.check_subtype( + compare_type, + base_type, + rvalue, + message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, + "expression has type", + f'base class "{base.name}" defined the type as', + ) + if ( + ok + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(base_node) + and not always_allow_covariant + ): ok = self.check_subtype( - compare_type, base_type, + compare_type, rvalue, - message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, - "expression has type", + message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE, f'base class "{base.name}" defined the type as', + "expression has type", ) - if ( - ok - and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes - and self.is_writable_attribute(base_node) - ): - ok = self.check_subtype( - base_type, - compare_type, - rvalue, - message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE, - f'base class "{base.name}" defined the type as', - "expression has type", - ) - return ok - return True + return ok - def lvalue_type_from_base( - self, expr_node: Var, base: TypeInfo - ) -> tuple[Type | None, Node | None]: - """For a NameExpr that is part of a class, walk all base classes and try - to find the first class that defines a Type for the same name.""" - expr_name = expr_node.name - base_var = base.names.get(expr_name) - - if base_var: - base_node = base_var.node - base_type = base_var.type - if isinstance(base_node, Var) and base_type is not None: - base_type = expand_self_type(base_node, base_type, fill_typevars(expr_node.info)) - if isinstance(base_node, Decorator): - base_node = base_node.func - base_type = base_node.type - - if base_type: - if not has_no_typevars(base_type): - self_type = self.scope.active_self_type() - assert self_type is not None, "Internal error: base lookup outside class" - if isinstance(self_type, TupleType): - instance = tuple_fallback(self_type) - else: - instance = self_type - itype = map_instance_to_supertype(instance, base) - base_type = expand_type_by_instance(base_type, itype) - - base_type = get_proper_type(base_type) - if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef): - # If we are a property, return the Type of the return - # value, not the Callable - if base_node.is_property: - base_type = get_proper_type(base_type.ret_type) - if isinstance(base_type, FunctionLike) and isinstance( - base_node, OverloadedFuncDef - ): - # Same for properties with setter - if base_node.is_property: - base_type = base_type.items[0].ret_type + def node_type_from_base( + self, + name: str, + base: TypeInfo, + context: Context, + *, + setter_type: bool = False, + is_class: bool = False, + current_class: TypeInfo | None = None, + ) -> tuple[Type | None, SymbolNode | None]: + """Find a type for a name in base class. + + Return the type found and the corresponding node defining the name or None + for both if the name is not defined in base or the node type is not known (yet). + The type returned is already properly mapped/bound to the subclass. + If setter_type is True, return setter types for settable properties (otherwise the + getter type is returned). + """ + base_node = base.names.get(name) - return base_type, base_node + # TODO: defer current node if the superclass node is not ready. + if ( + not base_node + or isinstance(base_node.node, (Var, Decorator)) + and not base_node.type + or isinstance(base_node.type, PartialType) + and base_node.type.type is not None + ): + return None, None - return None, None + if current_class is None: + self_type = self.scope.current_self_type() + else: + self_type = fill_typevars(current_class) + assert self_type is not None, "Internal error: base lookup outside class" + if isinstance(self_type, TupleType): + instance = tuple_fallback(self_type) + else: + instance = self_type + + mx = MemberContext( + is_lvalue=setter_type, + is_super=False, + is_operator=mypy.checkexpr.is_operator_method(name), + original_type=self_type, + context=context, + chk=self, + suppress_errors=True, + ) + # TODO: we should not filter "cannot determine type" errors here. + with self.msg.filter_errors(filter_deprecated=True): + if is_class: + fallback = instance.type.metaclass_type or mx.named_type("builtins.type") + base_type = analyze_class_attribute_access( + instance, name, mx, mcs_fallback=fallback, override_info=base + ) + else: + base_type = analyze_instance_member_access(name, instance, mx, base) + return base_type, base_node.node def check_compatibility_classvar_super( self, node: Var, base: TypeInfo, base_node: Node | None @@ -3489,7 +3826,7 @@ def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExp else: lvs = [s.lvalue] is_final_decl = s.is_final_def if isinstance(s, AssignmentStmt) else False - if is_final_decl and self.scope.active_class(): + if is_final_decl and (active_class := self.scope.active_class()): lv = lvs[0] assert isinstance(lv, RefExpr) if lv.node is not None: @@ -3503,6 +3840,9 @@ def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExp # then we already reported the error about missing r.h.s. isinstance(s, AssignmentStmt) and s.type is not None + # Avoid extra error message for NamedTuples, + # they were reported during semanal + and not active_class.is_named_tuple ): self.msg.final_without_value(s) for lv in lvs: @@ -3531,6 +3871,9 @@ def check_assignment_to_slots(self, lvalue: Lvalue) -> None: return inst = get_proper_type(self.expr_checker.accept(lvalue.expr)) + if isinstance(inst, TypeVarType) and inst.id.is_self(): + # Unwrap self type + inst = get_proper_type(inst.upper_bound) if not isinstance(inst, Instance): return if inst.type.slots is None: @@ -3563,7 +3906,7 @@ def is_assignable_slot(self, lvalue: Lvalue, typ: Type | None) -> bool: typ = get_proper_type(typ) if typ is None or isinstance(typ, AnyType): - return True # Any can be literally anything, like `@propery` + return True # Any can be literally anything, like `@property` if isinstance(typ, Instance): # When working with instances, we need to know if they contain # `__set__` special method. Like `@property` does. @@ -3660,7 +4003,7 @@ def check_assignment_to_multiple_lvalues( if rvalue_needed > 0: rvalues = ( rvalues[0:iterable_start] - + [TempNode(iterable_type) for i in range(rvalue_needed)] + + [TempNode(iterable_type, context=rval) for _ in range(rvalue_needed)] + rvalues[iterable_end + 1 :] ) @@ -3819,14 +4162,14 @@ def check_multi_assignment_from_union( for t, lv in zip(transposed, self.flatten_lvalues(lvalues)): # We can access _type_maps directly since temporary type maps are # only created within expressions. - t.append(self._type_maps[0].pop(lv, AnyType(TypeOfAny.special_form))) + t.append(self._type_maps[-1].pop(lv, AnyType(TypeOfAny.special_form))) union_types = tuple(make_simplified_union(col) for col in transposed) for expr, items in assignments.items(): # Bind a union of types collected in 'assignments' to every expression. if isinstance(expr, StarExpr): expr = expr.expr - # TODO: See todo in binder.py, ConditionalTypeBinder.assign_type + # TODO: See comment in binder.py, ConditionalTypeBinder.assign_type # It's unclear why the 'declared_type' param is sometimes 'None' clean_items: list[tuple[Type, Type]] = [] for type, declared_type in items: @@ -3838,7 +4181,6 @@ def check_multi_assignment_from_union( expr, make_simplified_union(list(types)), make_simplified_union(list(declared_types)), - False, ) for union, lv in zip(union_types, self.flatten_lvalues(lvalues)): # Properly store the inferred types. @@ -4040,7 +4382,9 @@ def check_multi_assignment_from_iterable( else: self.msg.type_not_iterable(rvalue_type, context) - def check_lvalue(self, lvalue: Lvalue) -> tuple[Type | None, IndexExpr | None, Var | None]: + def check_lvalue( + self, lvalue: Lvalue, rvalue: Expression | None = None + ) -> tuple[Type | None, IndexExpr | None, Var | None]: lvalue_type = None index_lvalue = None inferred = None @@ -4058,10 +4402,16 @@ def check_lvalue(self, lvalue: Lvalue) -> tuple[Type | None, IndexExpr | None, V elif isinstance(lvalue, IndexExpr): index_lvalue = lvalue elif isinstance(lvalue, MemberExpr): - lvalue_type = self.expr_checker.analyze_ordinary_member_access(lvalue, True) + lvalue_type = self.expr_checker.analyze_ordinary_member_access(lvalue, True, rvalue) self.store_type(lvalue, lvalue_type) elif isinstance(lvalue, NameExpr): lvalue_type = self.expr_checker.analyze_ref_expr(lvalue, lvalue=True) + if ( + self.options.allow_redefinition_new + and isinstance(lvalue.node, Var) + and lvalue.node.is_inferred + ): + inferred = lvalue.node self.store_type(lvalue, lvalue_type) elif isinstance(lvalue, (TupleExpr, ListExpr)): types = [ @@ -4102,15 +4452,20 @@ def infer_variable_type( if isinstance(init_type, DeletedType): self.msg.deleted_as_rvalue(init_type, context) elif ( - not is_valid_inferred_type(init_type, is_lvalue_final=name.is_final) + not is_valid_inferred_type( + init_type, + self.options, + is_lvalue_final=name.is_final, + is_lvalue_member=isinstance(lvalue, MemberExpr), + ) and not self.no_partial_types ): # We cannot use the type of the initialization expression for full type # inference (it's not specific enough), but we might be able to give # partial type which will be made more specific later. A partial type # gets generated in assignment like 'x = []' where item type is not known. - if not self.infer_partial_type(name, lvalue, init_type): - self.msg.need_annotation_for_var(name, context, self.options.python_version) + if name.name != "_" and not self.infer_partial_type(name, lvalue, init_type): + self.msg.need_annotation_for_var(name, context, self.options) self.set_inference_error_fallback_type(name, lvalue, init_type) elif ( isinstance(lvalue, MemberExpr) @@ -4120,7 +4475,7 @@ def infer_variable_type( and not is_same_type(self.inferred_attribute_types[lvalue.def_var], init_type) ): # Multiple, inconsistent types inferred for an attribute. - self.msg.need_annotation_for_var(name, context, self.options.python_version) + self.msg.need_annotation_for_var(name, context, self.options) name.type = AnyType(TypeOfAny.from_error) else: # Infer type of the target. @@ -4129,10 +4484,16 @@ def infer_variable_type( init_type = strip_type(init_type) self.set_inferred_type(name, lvalue, init_type) + if self.options.allow_redefinition_new: + self.binder.assign_type(lvalue, init_type, init_type) def infer_partial_type(self, name: Var, lvalue: Lvalue, init_type: Type) -> bool: init_type = get_proper_type(init_type) - if isinstance(init_type, NoneType): + if isinstance(init_type, NoneType) and ( + isinstance(lvalue, MemberExpr) or not self.options.allow_redefinition_new + ): + # When using --allow-redefinition-new, None types aren't special + # when inferring simple variable types. partial_type = PartialType(None, name) elif isinstance(init_type, Instance): fullname = init_type.type.fullname @@ -4204,6 +4565,7 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None: if var and not self.current_node_deferred: var.type = type var.is_inferred = True + var.is_ready = True if var not in self.var_decl_frames: # Used for the hack to improve optional type inference in conditionals self.var_decl_frames[var] = {frame.id for frame in self.binder.frames} @@ -4212,12 +4574,32 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None: if lvalue.def_var is not None: self.inferred_attribute_types[lvalue.def_var] = type self.store_type(lvalue, type) + p_type = get_proper_type(type) + definition = None + if isinstance(p_type, CallableType): + definition = p_type.definition + elif isinstance(p_type, Overloaded): + # Randomly select first item, if items are different, there will + # be an error during semantic analysis. + definition = p_type.items[0].definition + if definition: + if is_node_static(definition): + var.is_staticmethod = True + elif is_classmethod_node(definition): + var.is_classmethod = True + elif is_property(definition): + var.is_property = True + if isinstance(p_type, Overloaded): + # TODO: in theory we can have a property with a deleter only. + var.is_settable_property = True + assert isinstance(definition, Decorator), definition + var.setter_type = definition.var.setter_type def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None: """Store best known type for variable if type inference failed. If a program ignores error on type inference error, the variable should get some - inferred type so that if can used later on in the program. Example: + inferred type so that it can used later on in the program. Example: x = [] # type: ignore x.append(1) # Should be ok! @@ -4242,7 +4624,9 @@ def simple_rvalue(self, rvalue: Expression) -> bool: if isinstance(rvalue, (IntExpr, StrExpr, BytesExpr, FloatExpr, RefExpr)): return True if isinstance(rvalue, CallExpr): - if isinstance(rvalue.callee, RefExpr) and isinstance(rvalue.callee.node, FuncBase): + if isinstance(rvalue.callee, RefExpr) and isinstance( + rvalue.callee.node, SYMBOL_FUNCBASE_TYPES + ): typ = rvalue.callee.node.type if isinstance(typ, CallableType): return not typ.variables @@ -4260,21 +4644,66 @@ def check_simple_assignment( rvalue_name: str = "expression", *, notes: list[str] | None = None, - ) -> Type: + lvalue: Expression | None = None, + inferred: Var | None = None, + ) -> tuple[Type, Type | None]: if self.is_stub and isinstance(rvalue, EllipsisExpr): # '...' is always a valid initializer in a stub. - return AnyType(TypeOfAny.special_form) + return AnyType(TypeOfAny.special_form), lvalue_type else: always_allow_any = lvalue_type is not None and not isinstance( get_proper_type(lvalue_type), AnyType ) + if inferred is None or is_typeddict_type_context(lvalue_type): + type_context = lvalue_type + else: + type_context = None rvalue_type = self.expr_checker.accept( - rvalue, lvalue_type, always_allow_any=always_allow_any + rvalue, type_context=type_context, always_allow_any=always_allow_any ) + if ( + lvalue_type is not None + and type_context is None + and not is_valid_inferred_type(rvalue_type, self.options) + ): + # Inference in an empty type context didn't produce a valid type, so + # try using lvalue type as context instead. + rvalue_type = self.expr_checker.accept( + rvalue, type_context=lvalue_type, always_allow_any=always_allow_any + ) + if not is_valid_inferred_type(rvalue_type, self.options) and inferred is not None: + self.msg.need_annotation_for_var(inferred, context, self.options) + rvalue_type = rvalue_type.accept(SetNothingToAny()) + + if ( + isinstance(lvalue, NameExpr) + and inferred is not None + and inferred.type is not None + and not inferred.is_final + ): + new_inferred = remove_instance_last_known_values(rvalue_type) + if not is_same_type(inferred.type, new_inferred): + # Should we widen the inferred type or the lvalue? Variables defined + # at module level or class bodies can't be widened in functions, or + # in another module. + if not self.refers_to_different_scope(lvalue): + lvalue_type = make_simplified_union([inferred.type, new_inferred]) + if not is_same_type(lvalue_type, inferred.type) and not isinstance( + inferred.type, PartialType + ): + # Widen the type to the union of original and new type. + self.widened_vars.append(inferred.name) + self.set_inferred_type(inferred, lvalue, lvalue_type) + self.binder.put(lvalue, rvalue_type) + # TODO: A bit hacky, maybe add a binder method that does put and + # updates declaration? + lit = literal_hash(lvalue) + if lit is not None: + self.binder.declarations[lit] = lvalue_type if ( isinstance(get_proper_type(lvalue_type), UnionType) # Skip literal types, as they have special logic (for better errors). - and not isinstance(get_proper_type(rvalue_type), LiteralType) + and not is_literal_type_like(rvalue_type) and not self.simple_rvalue(rvalue) ): # Try re-inferring r.h.s. in empty context, and use that if it @@ -4282,7 +4711,7 @@ def check_simple_assignment( # may cause some perf impact, plus we want to partially preserve # the old behavior. This helps with various practical examples, see # e.g. testOptionalTypeNarrowedByGenericCall. - with self.msg.filter_errors() as local_errors, self.local_type_map() as type_map: + with self.msg.filter_errors() as local_errors, self.local_type_map as type_map: alt_rvalue_type = self.expr_checker.accept( rvalue, None, always_allow_any=always_allow_any ) @@ -4290,7 +4719,7 @@ def check_simple_assignment( not local_errors.has_new_errors() # Skip Any type, since it is special cased in binder. and not isinstance(get_proper_type(alt_rvalue_type), AnyType) - and is_valid_inferred_type(alt_rvalue_type) + and is_valid_inferred_type(alt_rvalue_type, self.options) and is_proper_subtype(alt_rvalue_type, rvalue_type) ): rvalue_type = alt_rvalue_type @@ -4310,10 +4739,27 @@ def check_simple_assignment( f"{lvalue_name} has type", notes=notes, ) - return rvalue_type + return rvalue_type, lvalue_type + + def refers_to_different_scope(self, name: NameExpr) -> bool: + if name.kind == LDEF: + # TODO: Consider reference to outer function as a different scope? + return False + elif self.scope.top_level_function() is not None: + # A non-local reference from within a function must refer to a different scope + return True + elif name.kind == GDEF and name.fullname.rpartition(".")[0] != self.tree.fullname: + # Reference to global definition from another module + return True + return False def check_member_assignment( - self, instance_type: Type, attribute_type: Type, rvalue: Expression, context: Context + self, + lvalue: MemberExpr, + instance_type: Type, + set_lvalue_type: Type, + rvalue: Expression, + context: Context, ) -> tuple[Type, Type, bool]: """Type member assignment. @@ -4322,115 +4768,27 @@ def check_member_assignment( Return the inferred rvalue_type, inferred lvalue_type, and whether to use the binder for this assignment. - - Note: this method exists here and not in checkmember.py, because we need to take - care about interaction between binder and __set__(). """ instance_type = get_proper_type(instance_type) - attribute_type = get_proper_type(attribute_type) # Descriptors don't participate in class-attribute access if (isinstance(instance_type, FunctionLike) and instance_type.is_type_obj()) or isinstance( instance_type, TypeType ): - rvalue_type = self.check_simple_assignment(attribute_type, rvalue, context) - return rvalue_type, attribute_type, True + rvalue_type, _ = self.check_simple_assignment(set_lvalue_type, rvalue, context) + return rvalue_type, set_lvalue_type, True - if not isinstance(attribute_type, Instance): - # TODO: support __set__() for union types. - rvalue_type = self.check_simple_assignment(attribute_type, rvalue, context) - return rvalue_type, attribute_type, True - - mx = MemberContext( - is_lvalue=False, - is_super=False, - is_operator=False, - original_type=instance_type, - context=context, - self_type=None, - msg=self.msg, - chk=self, - ) - get_type = analyze_descriptor_access(attribute_type, mx) - 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, - # (which allow you to override the descriptor with any value), but preserves - # the type of accessing the attribute (even after the override). - rvalue_type = self.check_simple_assignment(get_type, rvalue, context) - return rvalue_type, get_type, True - - dunder_set = attribute_type.type.get_method("__set__") - if dunder_set is None: - self.fail( - message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format( - attribute_type.str_with_options(self.options) - ), - context, - ) - return AnyType(TypeOfAny.from_error), get_type, False - - bound_method = analyze_decorator_or_funcbase_access( - defn=dunder_set, - itype=attribute_type, - info=attribute_type.type, - self_type=attribute_type, - name="__set__", - mx=mx, - ) - typ = map_instance_to_supertype(attribute_type, dunder_set.info) - dunder_set_type = expand_type_by_instance(bound_method, typ) - - callable_name = self.expr_checker.method_fullname(attribute_type, "__set__") - dunder_set_type = self.expr_checker.transform_callee_type( - callable_name, - dunder_set_type, - [TempNode(instance_type, context=context), rvalue], - [nodes.ARG_POS, nodes.ARG_POS], - context, - object_type=attribute_type, - ) - - # For non-overloaded setters, the result should be type-checked like a regular assignment. - # Hence, we first only try to infer the type by using the rvalue as type context. - type_context = rvalue - with self.msg.filter_errors(): - _, inferred_dunder_set_type = self.expr_checker.check_call( - dunder_set_type, - [TempNode(instance_type, context=context), type_context], - [nodes.ARG_POS, nodes.ARG_POS], - context, - object_type=attribute_type, - callable_name=callable_name, + with self.msg.filter_errors(filter_deprecated=True): + get_lvalue_type = self.expr_checker.analyze_ordinary_member_access( + lvalue, is_lvalue=False ) - # And now we in fact type check the call, to show errors related to wrong arguments - # count, etc., replacing the type context for non-overloaded setters only. - inferred_dunder_set_type = get_proper_type(inferred_dunder_set_type) - if isinstance(inferred_dunder_set_type, CallableType): - type_context = TempNode(AnyType(TypeOfAny.special_form), context=context) - self.expr_checker.check_call( - dunder_set_type, - [TempNode(instance_type, context=context), type_context], - [nodes.ARG_POS, nodes.ARG_POS], - context, - object_type=attribute_type, - callable_name=callable_name, - ) - - # 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 - ): - return AnyType(TypeOfAny.from_error), get_type, False - - set_type = inferred_dunder_set_type.arg_types[1] - # Special case: if the rvalue_type is a subtype of both '__get__' and '__set__' types, - # and '__get__' type is narrower than '__set__', then we invoke the binder to narrow type + # Special case: if the rvalue_type is a subtype of '__get__' type, and + # '__get__' type is narrower than '__set__', then we invoke the binder to narrow type # by this assignment. Technically, this is not safe, but in practice this is # what a user expects. - rvalue_type = self.check_simple_assignment(set_type, rvalue, context) - infer = is_subtype(rvalue_type, get_type) and is_subtype(get_type, set_type) - return rvalue_type if infer else set_type, get_type, infer + rvalue_type, _ = self.check_simple_assignment(set_lvalue_type, rvalue, context) + rvalue_type = rvalue_type if is_subtype(rvalue_type, get_lvalue_type) else get_lvalue_type + return rvalue_type, set_lvalue_type, is_subtype(get_lvalue_type, set_lvalue_type) def check_indexed_assignment( self, lvalue: IndexExpr, rvalue: Expression, context: Context @@ -4458,6 +4816,21 @@ def check_indexed_assignment( if isinstance(res_type, UninhabitedType) and not res_type.ambiguous: self.binder.unreachable() + def replace_partial_type( + self, var: Var, new_type: Type, partial_types: dict[Var, Context] + ) -> None: + """Replace the partial type of var with a non-partial type.""" + var.type = new_type + # Updating a partial type should invalidate expression caches. + self.binder.version += 1 + del partial_types[var] + if self.options.allow_redefinition_new: + # When using --allow-redefinition-new, binder tracks all types of + # simple variables. + n = NameExpr(var.name) + n.node = var + self.binder.assign_type(n, new_type, new_type) + def try_infer_partial_type_from_indexed_assignment( self, lvalue: IndexExpr, rvalue: Expression ) -> None: @@ -4485,8 +4858,8 @@ def try_infer_partial_type_from_indexed_assignment( key_type = self.expr_checker.accept(lvalue.index) value_type = self.expr_checker.accept(rvalue) if ( - is_valid_inferred_type(key_type) - and is_valid_inferred_type(value_type) + is_valid_inferred_type(key_type, self.options) + and is_valid_inferred_type(value_type, self.options) and not self.current_node_deferred and not ( typename == "collections.defaultdict" @@ -4494,8 +4867,8 @@ def try_infer_partial_type_from_indexed_assignment( and not is_equivalent(value_type, var.type.value_type) ) ): - var.type = self.named_generic_type(typename, [key_type, value_type]) - del partial_types[var] + new_type = self.named_generic_type(typename, [key_type, value_type]) + self.replace_partial_type(var, new_type, partial_types) def type_requires_usage(self, typ: Type) -> tuple[str, ErrorCode] | None: """Some types require usage in all cases. The classic example is @@ -4531,8 +4904,44 @@ def visit_return_stmt(self, s: ReturnStmt) -> None: self.check_return_stmt(s) self.binder.unreachable() + def infer_context_dependent( + self, expr: Expression, type_ctx: Type, allow_none_func_call: bool + ) -> ProperType: + """Infer type of an expression with fallback to empty type context.""" + with self.msg.filter_errors( + filter_errors=True, filter_deprecated=True, save_filtered_errors=True + ) as msg: + with self.local_type_map as type_map: + typ = get_proper_type( + self.expr_checker.accept( + expr, type_ctx, allow_none_return=allow_none_func_call + ) + ) + if not msg.has_new_errors(): + self.store_types(type_map) + return typ + + # If there are errors with the original type context, try re-inferring in empty context. + original_messages = msg.filtered_errors() + original_type_map = type_map + with self.msg.filter_errors( + filter_errors=True, filter_deprecated=True, save_filtered_errors=True + ) as msg: + with self.local_type_map as type_map: + alt_typ = get_proper_type( + self.expr_checker.accept(expr, None, allow_none_return=allow_none_func_call) + ) + if not msg.has_new_errors() and is_subtype(alt_typ, type_ctx): + self.store_types(type_map) + return alt_typ + + # If empty fallback didn't work, use results from the original type context. + self.msg.add_errors(original_messages) + self.store_types(original_type_map) + return typ + def check_return_stmt(self, s: ReturnStmt) -> None: - defn = self.scope.top_function() + defn = self.scope.current_function() if defn is not None: if defn.is_generator: return_type = self.get_generator_return_type( @@ -4544,7 +4953,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None: return_type = self.return_types[-1] return_type = get_proper_type(return_type) - is_lambda = isinstance(self.scope.top_function(), LambdaExpr) + is_lambda = isinstance(defn, LambdaExpr) if isinstance(return_type, UninhabitedType): # Avoid extra error messages for failed inference in lambdas if not is_lambda and not return_type.ambiguous: @@ -4563,11 +4972,26 @@ def check_return_stmt(self, s: ReturnStmt) -> None: allow_none_func_call = is_lambda or declared_none_return or declared_any_return # Return with a value. - typ = get_proper_type( - self.expr_checker.accept( - s.expr, return_type, allow_none_return=allow_none_func_call + if ( + isinstance(s.expr, (CallExpr, ListExpr, TupleExpr, DictExpr, SetExpr, OpExpr)) + or isinstance(s.expr, AwaitExpr) + and isinstance(s.expr.expr, CallExpr) + ): + # For expressions that (strongly) depend on type context (i.e. those that + # are handled like a function call), we allow fallback to empty type context + # in case of errors, this improves user experience in some cases, + # see e.g. testReturnFallbackInference. + typ = self.infer_context_dependent(s.expr, return_type, allow_none_func_call) + else: + typ = get_proper_type( + self.expr_checker.accept( + 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 in NOT_IMPLEMENTED_TYPE_NAMES: + typ = AnyType(TypeOfAny.special_form) if defn.is_async_generator: self.fail(message_registry.RETURN_IN_ASYNC_GENERATOR, s) @@ -4642,11 +5066,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: @@ -4670,7 +5094,7 @@ def visit_operator_assignment_stmt(self, s: OperatorAssignmentStmt) -> None: inplace, method = infer_operator_assignment_method(lvalue_type, s.op) 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) + rvalue_type, _ = self.expr_checker.check_op(method, lvalue_type, s.rvalue, s) if not is_subtype(rvalue_type, lvalue_type): self.msg.incompatible_operator_assignment(s.op, s) else: @@ -4691,7 +5115,9 @@ def visit_assert_stmt(self, s: AssertStmt) -> None: # If this is asserting some isinstance check, bind that type in the following code true_map, else_map = self.find_isinstance_check(s.expr) if s.msg is not None: - self.expr_checker.analyze_cond_branch(else_map, s.msg, None) + self.expr_checker.analyze_cond_branch( + else_map, s.msg, None, suppress_unreachable_errors=False + ) self.push_type_map(true_map) def visit_raise_stmt(self, s: RaiseStmt) -> None: @@ -4723,8 +5149,23 @@ 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 in NOT_IMPLEMENTED_TYPE_NAMES) or ( + isinstance(e, CallExpr) + and isinstance(e.callee, RefExpr) + and e.callee.fullname == "builtins.NotImplemented" + ): + 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.""" + + iter_errors = None + # Our enclosing frame will get the result if the try/except falls through. # This one gets all possible states after the try block exited abnormally # (by exception, return, break, etc.) @@ -4739,7 +5180,9 @@ def visit_try_stmt(self, s: TryStmt) -> None: self.visit_try_without_finally(s, try_frame=bool(s.finally_body)) if s.finally_body: # First we check finally_body is type safe on all abnormal exit paths - self.accept(s.finally_body) + iter_errors = IterationDependentErrors() + with IterationErrorWatcher(self.msg.errors, iter_errors): + self.accept(s.finally_body) if s.finally_body: # Then we try again for the more restricted set of options @@ -4753,8 +5196,11 @@ def visit_try_stmt(self, s: TryStmt) -> None: # type checks in both contexts, but only the resulting types # from the latter context affect the type state in the code # that follows the try statement.) + assert iter_errors is not None if not self.binder.is_unreachable(): - self.accept(s.finally_body) + with IterationErrorWatcher(self.msg.errors, iter_errors): + self.accept(s.finally_body) + self.msg.iteration_dependent_errors(iter_errors) def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None: """Type check a try statement, ignoring the finally block. @@ -4792,8 +5238,13 @@ def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None: # try/except block. source = var.name if isinstance(var.node, Var): - var.node.type = DeletedType(source=source) - self.binder.cleanse(var) + new_type = DeletedType(source=source) + var.node.type = new_type + if self.options.allow_redefinition_new: + # TODO: Should we use put() here? + self.binder.assign_type(var, new_type, new_type) + if not self.options.allow_redefinition_new: + self.binder.cleanse(var) if s.else_body: self.accept(s.else_body) @@ -4879,8 +5330,14 @@ def visit_for_stmt(self, s: ForStmt) -> None: iterator_type, item_type = self.analyze_iterable_item_type(s.expr) s.inferred_item_type = item_type s.inferred_iterator_type = iterator_type - self.analyze_index_variables(s.index, item_type, s.index_type is None, s) - self.accept_loop(s.body, s.else_body) + + self.accept_loop( + s.body, + s.else_body, + on_enter_body=lambda: self.analyze_index_variables( + s.index, item_type, s.index_type is None, s + ), + ) def analyze_async_iterable_item_type(self, expr: Expression) -> tuple[Type, Type]: """Analyse async iterable expression and return iterator and iterator item types.""" @@ -4992,7 +5449,7 @@ def visit_del_stmt(self, s: DelStmt) -> None: for elt in flatten(s.expr): if isinstance(elt, NameExpr): self.binder.assign_type( - elt, DeletedType(source=elt.name), get_declaration(elt), False + elt, DeletedType(source=elt.name), get_declaration(elt) ) def visit_decorator(self, e: Decorator) -> None: @@ -5004,7 +5461,9 @@ def visit_decorator(self, e: Decorator) -> None: return self.visit_decorator_inner(e) - def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None: + def visit_decorator_inner( + self, e: Decorator, allow_empty: bool = False, skip_first_item: bool = False + ) -> None: if self.recurse_into_functions: with self.tscope.function_scope(e.func): self.check_func_item(e.func, name=e.func.name, allow_empty=allow_empty) @@ -5012,17 +5471,24 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None # Process decorators from the inside out to determine decorated signature, which # may be different from the declared signature. sig: Type = self.function_type(e.func) - for d in reversed(e.decorators): + non_trivial_decorator = False + # For settable properties skip the first decorator (that is @foo.setter). + for d in reversed(e.decorators[1:] if skip_first_item else e.decorators): + if refers_to_fullname(d, "abc.abstractmethod"): + # This is a hack to avoid spurious errors because of incomplete type + # of @abstractmethod in the test fixtures. + continue if refers_to_fullname(d, OVERLOAD_NAMES): if not allow_empty: self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, e) continue + non_trivial_decorator = True dec = self.expr_checker.accept(d) - temp = self.temp_node(sig, context=e) + temp = self.temp_node(sig, context=d) fullname = None if isinstance(d, RefExpr): fullname = d.fullname or None - # if this is a expression like @b.a where b is an object, get the type of b + # if this is an expression like @b.a where b is an object, get the type of b, # so we can pass it the method hook in the plugins object_type: Type | None = None if fullname is None and isinstance(d, MemberExpr) and self.has_type(d.expr): @@ -5032,8 +5498,12 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None sig, t2 = self.expr_checker.check_call( dec, [temp], [nodes.ARG_POS], e, callable_name=fullname, object_type=object_type ) - self.check_untyped_after_decorator(sig, e.func) + if non_trivial_decorator: + self.check_untyped_after_decorator(sig, e.func) + self.require_correct_self_argument(sig, e.func) sig = set_callable_name(sig, e.func) + if isinstance(sig, CallableType): + sig.definition = e e.var.type = sig e.var.is_ready = True if e.func.is_property: @@ -5041,15 +5511,18 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None if len([k for k in sig.arg_kinds if k.is_required()]) > 1: self.msg.fail("Too many arguments for property", e) self.check_incompatible_property_override(e) - # For overloaded functions we already checked override for overload as a whole. - if allow_empty: + # For overloaded functions/properties we already checked override for overload as a whole. + if allow_empty or skip_first_item: return - if e.func.info and not e.func.is_dynamic() and not e.is_overload: + if e.func.info and not e.is_overload: found_method_base_classes = self.check_method_override(e) if ( e.func.is_explicit_override and not found_method_base_classes and found_method_base_classes is not None + # If the class has Any fallback, we can't be certain that a method + # is really missing - it might come from unfollowed import. + and not e.func.info.fallback_to_any ): self.msg.no_overridable_method(e.func.name, e.func) self.check_explicit_override_decorator(e.func, found_method_base_classes) @@ -5058,6 +5531,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: @@ -5065,6 +5542,7 @@ def check_for_untyped_decorator( self.options.disallow_untyped_decorators and is_typed_callable(func.type) and is_untyped_decorator(dec_type) + and not self.current_node_deferred ): self.msg.typed_function_untyped_decorator(func.name, dec_expr) @@ -5120,7 +5598,7 @@ def visit_with_stmt(self, s: WithStmt) -> None: self.accept(s.body) def check_untyped_after_decorator(self, typ: Type, func: FuncDef) -> None: - if not self.options.disallow_any_decorated or self.is_stub: + if not self.options.disallow_any_decorated or self.is_stub or self.current_node_deferred: return if mypy.checkexpr.has_any_type(typ): @@ -5166,20 +5644,11 @@ def visit_continue_stmt(self, s: ContinueStmt) -> None: self.binder.handle_continue() return - def visit_match_stmt(self, s: MatchStmt) -> None: - named_subject: Expression - if isinstance(s.subject, CallExpr): - # Create a dummy subject expression to handle cases where a match statement's subject - # is not a literal value. This lets us correctly narrow types and check exhaustivity - # This is hack! - id = s.subject.callee.fullname if isinstance(s.subject.callee, RefExpr) else "" - name = "dummy-match-" + id - v = Var(name) - named_subject = NameExpr(name) - named_subject.node = v - else: - named_subject = s.subject - + def visit_match_stmt(self, s: MatchStmt) -> None: + # In sync with similar actions elsewhere, narrow the target if + # we are matching an AssignmentExpr + unwrapped_subject = collapse_walrus(s.subject) + named_subject = self._make_named_statement_for_match(s, unwrapped_subject) with self.binder.frame_context(can_skip=False, fall_through=0): subject_type = get_proper_type(self.expr_checker.accept(s.subject)) @@ -5196,6 +5665,7 @@ def visit_match_stmt(self, s: MatchStmt) -> None: inferred_types = self.infer_variable_types_from_type_maps(type_maps) # The second pass narrows down the types and type checks bodies. + unmatched_types: TypeMap = None for p, g, b in zip(s.patterns, s.guards, s.bodies): current_subject_type = self.expr_checker.narrow_type_from_binder( named_subject, subject_type @@ -5205,18 +5675,29 @@ 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 ) + # Maybe the subject type can be inferred from constraints on + # its attribute/item? + if pattern_map and named_subject in pattern_map: + pattern_map[unwrapped_subject] = pattern_map[named_subject] + if else_map and named_subject in else_map: + else_map[unwrapped_subject] = else_map[named_subject] + pattern_map = self.propagate_up_typemap_info(pattern_map) + else_map = self.propagate_up_typemap_info(else_map) 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)) @@ -5242,17 +5723,40 @@ 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) + unmatched_types = else_map + + if unmatched_types is not None and not self.current_node_deferred: + for typ in unmatched_types.values(): + self.msg.match_statement_inexhaustive_match(typ, s) # This is needed due to a quirk in frame_context. Without it types will stay narrowed # after the match. with self.binder.frame_context(can_skip=False, fall_through=2): pass + def _make_named_statement_for_match(self, s: MatchStmt, subject: Expression) -> Expression: + """Construct a fake NameExpr for inference if a match clause is complex.""" + if self.binder.can_put_directly(subject): + # Already named - we should infer type of it as given + return subject + elif s.subject_dummy is not None: + return s.subject_dummy + else: + # Create a dummy subject expression to handle cases where a match statement's subject + # is not a literal value. This lets us correctly narrow types and check exhaustivity + # This is hack! + name = self.new_unique_dummy_name("match") + v = Var(name) + named_subject = NameExpr(name) + named_subject.node = v + s.subject_dummy = named_subject + return named_subject + def _get_recursive_sub_patterns_map( self, expr: Expression, typ: Type ) -> dict[Expression, Type]: @@ -5267,17 +5771,21 @@ def _get_recursive_sub_patterns_map( return sub_patterns_map - def infer_variable_types_from_type_maps(self, type_maps: list[TypeMap]) -> dict[Var, Type]: - all_captures: dict[Var, list[tuple[NameExpr, Type]]] = defaultdict(list) + def infer_variable_types_from_type_maps( + self, type_maps: list[TypeMap] + ) -> dict[SymbolNode, Type]: + # Type maps may contain variables inherited from previous code which are not + # necessary `Var`s (e.g. a function defined earlier with the same name). + all_captures: dict[SymbolNode, list[tuple[NameExpr, Type]]] = defaultdict(list) for tm in type_maps: if tm is not None: for expr, typ in tm.items(): if isinstance(expr, NameExpr): node = expr.node - assert isinstance(node, Var) + assert node is not None all_captures[node].append((expr, typ)) - inferred_types: dict[Var, Type] = {} + inferred_types: dict[SymbolNode, Type] = {} for var, captures in all_captures.items(): already_exists = False types: list[Type] = [] @@ -5287,6 +5795,8 @@ def infer_variable_types_from_type_maps(self, type_maps: list[TypeMap]) -> dict[ previous_type, _, _ = self.check_lvalue(expr) if previous_type is not None: already_exists = True + if isinstance(expr.node, Var) and expr.node.is_final: + self.msg.cant_assign_to_final(expr.name, False, expr) if self.check_subtype( typ, previous_type, @@ -5301,20 +5811,26 @@ def infer_variable_types_from_type_maps(self, type_maps: list[TypeMap]) -> dict[ new_type = UnionType.make_union(types) # Infer the union type at the first occurrence first_occurrence, _ = captures[0] + # If it didn't exist before ``match``, it's a Var. + assert isinstance(var, Var) inferred_types[var] = new_type self.infer_variable_type(var, first_occurrence, new_type, first_occurrence) return inferred_types - def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, Type]) -> None: + def remove_capture_conflicts( + self, type_map: TypeMap, inferred_types: dict[SymbolNode, Type] + ) -> None: if type_map: for expr, typ in list(type_map.items()): if isinstance(expr, NameExpr): node = expr.node - assert isinstance(node, Var) if node not in inferred_types or not is_subtype(typ, inferred_types[node]): del type_map[expr] def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + if o.alias_node: + self.check_typevar_defaults(o.alias_node.alias_tvars) + with self.msg.filter_errors(): self.expr_checker.accept(o.value) @@ -5353,13 +5869,9 @@ def intersect_instances( theoretical subclass of the instances the user may be trying to use the generated intersection can serve as a placeholder. - This function will create a fresh subclass every time you call it, - even if you pass in the exact same arguments. So this means calling - `self.intersect_intersection([inst_1, inst_2], ctx)` twice will result - in instances of two distinct subclasses of inst_1 and inst_2. - - This is by design: we want each ad-hoc intersection to be unique since - they're supposed represent some other unknown subclass. + This function will create a fresh subclass the first time you call it. + So this means calling `self.intersect_intersection([inst_1, inst_2], ctx)` + twice will return the same subclass of inst_1 and inst_2. Returns None if creating the subclass is impossible (e.g. due to MRO errors or incompatible signatures). If we do successfully create @@ -5392,24 +5904,27 @@ def _get_base_classes(instances_: tuple[Instance, Instance]) -> list[Instance]: return base_classes_ def _make_fake_typeinfo_and_full_name( - base_classes_: list[Instance], curr_module_: MypyFile + base_classes_: list[Instance], curr_module_: MypyFile, options: Options ) -> tuple[TypeInfo, str]: - names_list = pretty_seq([x.type.name for x in base_classes_], "and") - short_name = f"" - full_name_ = gen_unique_name(short_name, curr_module_.names) - cdef, info_ = self.make_fake_typeinfo( - curr_module_.fullname, full_name_, short_name, base_classes_ - ) - return info_, full_name_ + names = [format_type_bare(x, options=options, verbosity=2) for x in base_classes_] + name = f"" + if (symbol := curr_module_.names.get(name)) is not None: + assert isinstance(symbol.node, TypeInfo) + return symbol.node, name + cdef, info_ = self.make_fake_typeinfo(curr_module_.fullname, name, name, base_classes_) + return info_, name base_classes = _get_base_classes(instances) - # We use the pretty_names_list for error messages but can't - # use it for the real name that goes into the symbol table - # because it can have dots in it. + # We use the pretty_names_list for error messages but for the real name that goes + # into the symbol table because it is not specific enough. pretty_names_list = pretty_seq( format_type_distinctly(*base_classes, options=self.options, bare=True), "and" ) + if not can_have_shared_disjoint_base(base_classes): + errors.append((pretty_names_list, "have distinct disjoint bases")) + return None + new_errors = [] for base in base_classes: if base.type.is_final: @@ -5419,13 +5934,17 @@ def _make_fake_typeinfo_and_full_name( return None try: - info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) + info, full_name = _make_fake_typeinfo_and_full_name( + base_classes, curr_module, self.options + ) with self.msg.filter_errors() as local_errors: self.check_multiple_inheritance(info) if local_errors.has_new_errors(): # "class A(B, C)" unsafe, now check "class A(C, B)": base_classes = _get_base_classes(instances[::-1]) - info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) + info, full_name = _make_fake_typeinfo_and_full_name( + base_classes, curr_module, self.options + ) with self.msg.filter_errors() as local_errors: self.check_multiple_inheritance(info) info.is_intersection = True @@ -5525,7 +6044,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 @@ -5766,6 +6285,8 @@ def is_type_call(expr: CallExpr) -> bool: def combine_maps(list_maps: list[TypeMap]) -> TypeMap: """Combine all typemaps in list_maps into one typemap""" + if all(m is None for m in list_maps): + return None result_map = {} for d in list_maps: if d is not None: @@ -5848,21 +6369,26 @@ def find_isinstance_check_helper( attr = try_getting_str_literals(node.args[1], self.lookup_type(node.args[1])) if literal(expr) == LITERAL_TYPE and attr and len(attr) == 1: return self.hasattr_type_maps(expr, self.lookup_type(expr), attr[0]) - elif isinstance(node.callee, RefExpr): - if node.callee.type_guard is not None or node.callee.type_is is not None: + else: + type_is, type_guard = None, None + called_type = self.lookup_type_or_none(node.callee) + if called_type is not None: + called_type = get_proper_type(called_type) + # TODO: there are some more cases in check_call() to handle. + # If the callee is an instance, try to extract TypeGuard/TypeIs from its __call__ method. + if isinstance(called_type, Instance): + call = find_member("__call__", called_type, called_type, is_operator=True) + if call is not None: + called_type = get_proper_type(call) + if isinstance(called_type, CallableType): + type_is, type_guard = called_type.type_is, called_type.type_guard + + # If the callee is a RefExpr, extract TypeGuard/TypeIs directly. + if isinstance(node.callee, RefExpr): + type_is, type_guard = node.callee.type_is, node.callee.type_guard + if type_guard is not None or type_is is not None: # TODO: Follow *args, **kwargs if node.arg_kinds[0] != nodes.ARG_POS: - # the first argument might be used as a kwarg - called_type = get_proper_type(self.lookup_type(node.callee)) - - # TODO: there are some more cases in check_call() to handle. - if isinstance(called_type, Instance): - call = find_member( - "__call__", called_type, called_type, is_operator=True - ) - if call is not None: - called_type = get_proper_type(call) - # *assuming* the overloaded function is correct, there's a couple cases: # 1) The first argument has different names, but is pos-only. We don't # care about this case, the argument must be passed positionally. @@ -5875,9 +6401,7 @@ def find_isinstance_check_helper( # we want the idx-th variable to be narrowed expr = collapse_walrus(node.args[idx]) else: - kind = ( - "guard" if node.callee.type_guard is not None else "narrower" - ) + kind = "guard" if type_guard is not None else "narrower" self.fail( message_registry.TYPE_GUARD_POS_ARG_REQUIRED.format(kind), node ) @@ -5888,192 +6412,24 @@ def find_isinstance_check_helper( # considered "always right" (i.e. even if the types are not overlapping). # Also note that a care must be taken to unwrap this back at read places # where we use this to narrow down declared type. - if node.callee.type_guard is not None: - return {expr: TypeGuardedType(node.callee.type_guard)}, {} + if type_guard is not None: + return {expr: TypeGuardedType(type_guard)}, {} else: - assert node.callee.type_is is not None + assert type_is is not None return conditional_types_to_typemaps( expr, *self.conditional_types_with_intersection( self.lookup_type(expr), - [TypeRange(node.callee.type_is, is_upper_bound=False)], + [TypeRange(type_is, is_upper_bound=False)], expr, + consider_runtime_isinstance=False, ), ) 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) - ) 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, - ) - - # 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 = {} @@ -6160,6 +6516,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. @@ -6223,7 +6765,7 @@ def refine_parent_types(self, expr: Expression, expr_type: Type) -> Mapping[Expr # and create function that will try replaying the same lookup # operation against arbitrary types. if isinstance(expr, MemberExpr): - parent_expr = collapse_walrus(expr.expr) + parent_expr = self._propagate_walrus_assignments(expr.expr, output) parent_type = self.lookup_type_or_none(parent_expr) member_name = expr.name @@ -6236,7 +6778,6 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None: is_lvalue=False, is_super=False, is_operator=False, - msg=self.msg, original_type=new_parent_type, chk=self, in_literal_context=False, @@ -6247,9 +6788,10 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None: return member_type elif isinstance(expr, IndexExpr): - parent_expr = collapse_walrus(expr.base) + parent_expr = self._propagate_walrus_assignments(expr.base, output) parent_type = self.lookup_type_or_none(parent_expr) + self._propagate_walrus_assignments(expr.index, output) index_type = self.lookup_type_or_none(expr.index) if index_type is None: return output @@ -6323,6 +6865,24 @@ def replay_lookup(new_parent_type: ProperType) -> Type | None: expr = parent_expr expr_type = output[parent_expr] = make_simplified_union(new_parent_types) + def _propagate_walrus_assignments( + self, expr: Expression, type_map: dict[Expression, Type] + ) -> Expression: + """Add assignments from walrus expressions to inferred types. + + Only considers nested assignment exprs, does not recurse into other types. + This may be added later if necessary by implementing a dedicated visitor. + """ + if isinstance(expr, AssignmentExpr): + if isinstance(expr.value, AssignmentExpr): + self._propagate_walrus_assignments(expr.value, type_map) + assigned_type = self.lookup_type_or_none(expr.value) + parent_expr = collapse_walrus(expr) + if assigned_type is not None: + type_map[parent_expr] = assigned_type + return parent_expr + return expr + def refine_identity_comparison_expression( self, operands: list[Expression], @@ -6473,25 +7033,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)): + + # 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, {} + 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?""" @@ -6859,7 +7430,7 @@ def check_subtype( if extra_info: msg = msg.with_additional_msg(" (" + ", ".join(extra_info) + ")") - self.fail(msg, context) + error = self.fail(msg, context) for note in notes: self.msg.note(note, context, code=msg.code) if note_msg: @@ -6868,9 +7439,9 @@ def check_subtype( if ( isinstance(supertype, Instance) and supertype.type.is_protocol - and isinstance(subtype, (CallableType, Instance, TupleType, TypedDictType)) + and isinstance(subtype, (CallableType, Instance, TupleType, TypedDictType, TypeType)) ): - self.msg.report_protocol_problems(subtype, supertype, context, code=msg.code) + self.msg.report_protocol_problems(subtype, supertype, context, parent_error=error) if isinstance(supertype, CallableType) and isinstance(subtype, Instance): call = find_member("__call__", subtype, subtype, is_operator=True) if call: @@ -6936,13 +7507,36 @@ def named_type(self, name: str) -> Instance: For example, named_type('builtins.object') produces the 'object' type. """ + if name == "builtins.str": + if instance_cache.str_type is None: + instance_cache.str_type = self._named_type(name) + return instance_cache.str_type + if name == "builtins.function": + if instance_cache.function_type is None: + instance_cache.function_type = self._named_type(name) + return instance_cache.function_type + if name == "builtins.int": + if instance_cache.int_type is None: + instance_cache.int_type = self._named_type(name) + return instance_cache.int_type + if name == "builtins.bool": + if instance_cache.bool_type is None: + instance_cache.bool_type = self._named_type(name) + return instance_cache.bool_type + if name == "builtins.object": + if instance_cache.object_type is None: + instance_cache.object_type = self._named_type(name) + return instance_cache.object_type + return self._named_type(name) + + def _named_type(self, name: str) -> Instance: # Assume that the name refers to a type. sym = self.lookup_qualified(name) node = sym.node if isinstance(node, TypeAlias): assert isinstance(node.target, Instance) # type: ignore[misc] node = node.target.type - assert isinstance(node, TypeInfo) + assert isinstance(node, TypeInfo), node any_type = AnyType(TypeOfAny.from_omitted_generics) return Instance(node, [any_type] * len(node.defn.type_vars)) @@ -6961,7 +7555,7 @@ def lookup_typeinfo(self, fullname: str) -> TypeInfo: # Assume that the name refers to a class. sym = self.lookup_qualified(fullname) node = sym.node - assert isinstance(node, TypeInfo) + assert isinstance(node, TypeInfo), node return node def type_type(self) -> Instance: @@ -6995,18 +7589,6 @@ def lookup_type(self, node: Expression) -> Type: def store_types(self, d: dict[Expression, Type]) -> None: self._type_maps[-1].update(d) - @contextmanager - def local_type_map(self) -> Iterator[dict[Expression, Type]]: - """Store inferred types into a temporary type map (returned). - - This can be used to perform type checking "experiments" without - affecting exported types (which are used by mypyc). - """ - temp_type_map: dict[Expression, Type] = {} - self._type_maps.append(temp_type_map) - yield temp_type_map - self._type_maps.pop() - def in_checked_function(self) -> bool: """Should we type-check the current function? @@ -7113,7 +7695,7 @@ def enter_partial_types( var.type = NoneType() else: if var not in self.partial_reported and not permissive: - self.msg.need_annotation_for_var(var, context, self.options.python_version) + self.msg.need_annotation_for_var(var, context, self.options) self.partial_reported.add(var) if var.type: fixed = fixup_partial_type(var.type) @@ -7140,9 +7722,7 @@ def handle_partial_var_type( if in_scope: context = partial_types[node] if is_local or not self.options.allow_untyped_globals: - self.msg.need_annotation_for_var( - node, context, self.options.python_version - ) + self.msg.need_annotation_for_var(node, context, self.options) self.partial_reported.add(node) else: # Defer the node -- we might get a better type in the outer scope @@ -7199,12 +7779,11 @@ def temp_node(self, t: Type, context: Context | None = None) -> TempNode: def fail( self, msg: str | ErrorMessage, context: Context, *, code: ErrorCode | None = None - ) -> None: + ) -> ErrorInfo: """Produce an error message.""" if isinstance(msg, ErrorMessage): - self.msg.fail(msg.value, context, code=msg.code) - return - self.msg.fail(msg, context, code=code) + return self.msg.fail(msg.value, context, code=msg.code) + return self.msg.fail(msg, context, code=code) def note( self, @@ -7236,12 +7815,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.""" @@ -7280,11 +7859,19 @@ def conditional_types_with_intersection( type_ranges: list[TypeRange] | None, ctx: Context, default: None = None, + *, + consider_runtime_isinstance: bool = True, ) -> tuple[Type | None, Type | None]: ... @overload def conditional_types_with_intersection( - self, expr_type: Type, type_ranges: list[TypeRange] | None, ctx: Context, default: Type + self, + expr_type: Type, + type_ranges: list[TypeRange] | None, + ctx: Context, + default: Type, + *, + consider_runtime_isinstance: bool = True, ) -> tuple[Type, Type]: ... def conditional_types_with_intersection( @@ -7293,8 +7880,15 @@ def conditional_types_with_intersection( type_ranges: list[TypeRange] | None, ctx: Context, default: Type | None = None, + *, + consider_runtime_isinstance: bool = True, ) -> tuple[Type | None, Type | None]: - initial_types = conditional_types(expr_type, type_ranges, default) + initial_types = conditional_types( + expr_type, + type_ranges, + default, + consider_runtime_isinstance=consider_runtime_isinstance, + ) # For some reason, doing "yes_map, no_map = conditional_types_to_typemaps(...)" # doesn't work: mypyc will decide that 'yes_map' is of type None if we try. yes_type: Type | None = initial_types[0] @@ -7356,6 +7950,10 @@ def is_writable_attribute(self, node: Node) -> bool: return False def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None: + """Get the type(s) resulting from an isinstance check. + + Returns an empty list for isinstance(x, ()). + """ if isinstance(expr, OpExpr) and expr.op == "|": left = self.get_isinstance_type(expr.left) if left is None and is_literal_none(expr.left): @@ -7370,9 +7968,13 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None: types: list[TypeRange] = [] for typ in all_types: if isinstance(typ, FunctionLike) and typ.is_type_obj(): - # Type variables may be present -- erase them, which is the best - # we can do (outside disallowing them here). - erased_type = erase_typevars(typ.items[0].ret_type) + # If a type is generic, `isinstance` can only narrow its variables to Any. + any_parameterized = fill_typevars_with_any(typ.type_object()) + # Tuples may have unattended type variables among their items + if isinstance(any_parameterized, TupleType): + erased_type = erase_typevars(any_parameterized) + else: + erased_type = any_parameterized types.append(TypeRange(erased_type, is_upper_bound=False)) elif isinstance(typ, TypeType): # Type[A] means "any type that is a subtype of A" rather than "precisely type A" @@ -7391,11 +7993,6 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None: types.append(TypeRange(typ, is_upper_bound=False)) else: # we didn't see an actual type, but rather a variable with unknown value return None - if not types: - # this can happen if someone has empty tuple as 2nd argument to isinstance - # strictly speaking, we should return UninhabitedType but for simplicity we will simply - # refuse to do any type inference for now - return None return types def is_literal_enum(self, n: Expression) -> bool: @@ -7456,7 +8053,9 @@ def add_any_attribute_to_type(self, typ: Type, name: str) -> Type: fallback = typ.fallback.copy_with_extra_attr(name, any_type) return typ.copy_modified(fallback=fallback) if isinstance(typ, TypeType) and isinstance(typ.item, Instance): - return TypeType.make_normalized(self.add_any_attribute_to_type(typ.item, name)) + return TypeType.make_normalized( + self.add_any_attribute_to_type(typ.item, name), is_type_form=typ.is_type_form + ) if isinstance(typ, TypeVarType): return typ.copy_modified( upper_bound=self.add_any_attribute_to_type(typ.upper_bound, name), @@ -7520,10 +8119,9 @@ 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, original_type=typ, chk=self, # This is not a real attribute lookup so don't mess with deferring nodes. @@ -7535,6 +8133,146 @@ 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 is_defined_in_stub(self, typ: Instance, /) -> bool: + return self.modules[typ.type.module_name].is_stub + + 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 + and not any( + node.fullname == p or node.fullname.startswith(f"{p}.") + for p in self.options.deprecated_calls_exclude + ) + ): + warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail + warn(deprecated, context, code=codes.DEPRECATED) + + def new_unique_dummy_name(self, namespace: str) -> str: + """Generate a name that is guaranteed to be unique for this TypeChecker instance.""" + name = f"dummy-{namespace}-{self._unique_id}" + self._unique_id += 1 + return name + + # leafs + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: + return None + + def visit_global_decl(self, o: GlobalDecl, /) -> None: + return None + + +class TypeCheckerAsSemanticAnalyzer(SemanticAnalyzerCoreInterface): + """ + Adapts TypeChecker to the SemanticAnalyzerCoreInterface, + allowing most type expressions to be parsed during the TypeChecker pass. + + See ExpressionChecker.try_parse_as_type_expression() to understand how this + class is used. + """ + + _chk: TypeChecker + _names: dict[str, SymbolTableNode] + did_fail: bool + + def __init__(self, chk: TypeChecker, names: dict[str, SymbolTableNode]) -> None: + self._chk = chk + self._names = names + self.did_fail = False + + def lookup_qualified( + self, name: str, ctx: Context, suppress_errors: bool = False + ) -> SymbolTableNode | None: + sym = self._names.get(name) + # All names being looked up should have been previously gathered, + # even if the related SymbolTableNode does not refer to a valid SymbolNode + assert sym is not None, name + return sym + + def lookup_fully_qualified(self, fullname: str, /) -> SymbolTableNode: + ret = self.lookup_fully_qualified_or_none(fullname) + assert ret is not None, fullname + return ret + + def lookup_fully_qualified_or_none(self, fullname: str, /) -> SymbolTableNode | None: + try: + return self._chk.lookup_qualified(fullname) + except KeyError: + return None + + def fail( + self, + msg: str, + ctx: Context, + serious: bool = False, + *, + blocker: bool = False, + code: ErrorCode | None = None, + ) -> None: + self.did_fail = True + + def note(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None: + pass + + def incomplete_feature_enabled(self, feature: str, ctx: Context) -> bool: + if feature not in self._chk.options.enable_incomplete_feature: + self.fail("__ignored__", ctx) + return False + return True + + def record_incomplete_ref(self) -> None: + pass + + def defer(self, debug_context: Context | None = None, force_progress: bool = False) -> None: + pass + + def is_incomplete_namespace(self, fullname: str) -> bool: + return False + + @property + def final_iteration(self) -> bool: + return True + + def is_future_flag_set(self, flag: str) -> bool: + return self._chk.tree.is_future_flag_set(flag) + + @property + def is_stub_file(self) -> bool: + return self._chk.tree.is_stub + + def is_func_scope(self) -> bool: + # Return arbitrary value. + # + # This method is currently only used to decide whether to pair + # a fail() message with a note() message or not. Both of those + # message types are ignored. + return False + + @property + def type(self) -> TypeInfo | None: + return self._chk.type + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" @@ -7548,66 +8286,118 @@ def visit_type_var(self, t: TypeVarType) -> None: @overload def conditional_types( - current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: None = None + current_type: Type, + proposed_type_ranges: list[TypeRange] | None, + default: None = None, + *, + consider_runtime_isinstance: bool = True, ) -> tuple[Type | None, Type | None]: ... @overload def conditional_types( - current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: Type + current_type: Type, + proposed_type_ranges: list[TypeRange] | None, + default: Type, + *, + consider_runtime_isinstance: bool = True, ) -> tuple[Type, Type]: ... def conditional_types( - current_type: Type, proposed_type_ranges: list[TypeRange] | None, default: Type | None = None + current_type: Type, + proposed_type_ranges: list[TypeRange] | None, + default: Type | None = None, + *, + consider_runtime_isinstance: bool = True, ) -> tuple[Type | None, Type | None]: """Takes in the current type and a proposed type of an expression. - Returns a 2-tuple: The first element is the proposed type, if the expression - can be the proposed type. The second element is the type it would hold - if it was not the proposed type, if any. UninhabitedType means unreachable. - None means no new information can be inferred. If default is set it is returned - instead.""" - if proposed_type_ranges: - if len(proposed_type_ranges) == 1: - target = proposed_type_ranges[0].item - target = get_proper_type(target) - if isinstance(target, LiteralType) and ( - target.is_enum_literal() or isinstance(target.value, bool) - ): - enum_name = target.fallback.type.fullname - current_type = try_expanding_sum_type_to_union(current_type, enum_name) + Returns a 2-tuple: + The first element is the proposed type, if the expression can be the proposed type. + (or default, if default is set and the expression is a subtype of the proposed type). + The second element is the type it would hold if it was not the proposed type, if any. + (or default, if default is set and the expression is not a subtype of the proposed type). + + UninhabitedType means unreachable. + None means no new information can be inferred. + """ + if proposed_type_ranges is None: + # An isinstance check, but we don't understand the type + return current_type, default + + if not proposed_type_ranges: + # This is the case for `if isinstance(x, ())` which always returns False. + return UninhabitedType(), default + + if len(proposed_type_ranges) == 1: + # expand e.g. bool -> Literal[True] | Literal[False] + target = proposed_type_ranges[0].item + target = get_proper_type(target) + if isinstance(target, LiteralType) and ( + target.is_enum_literal() or isinstance(target.value, bool) + ): + enum_name = target.fallback.type.fullname + current_type = try_expanding_sum_type_to_union(current_type, enum_name) + + proper_type = get_proper_type(current_type) + # factorize over union types: isinstance(A|B, C) -> yes = A_yes | B_yes + if isinstance(proper_type, UnionType): + result: list[tuple[Type | None, Type | None]] = [ + conditional_types( + union_item, + proposed_type_ranges, + default=union_item, + consider_runtime_isinstance=consider_runtime_isinstance, + ) + for union_item in get_proper_types(proper_type.items) + ] + # separate list of tuples into two lists + yes_types, no_types = zip(*result) + proposed_type = make_simplified_union([t for t in yes_types if t is not None]) + else: proposed_items = [type_range.item for type_range in proposed_type_ranges] proposed_type = make_simplified_union(proposed_items) - if isinstance(proposed_type, AnyType): - # We don't really know much about the proposed type, so we shouldn't - # attempt to narrow anything. Instead, we broaden the expr to Any to - # avoid false positives - return proposed_type, default - elif not any( - type_range.is_upper_bound for type_range in proposed_type_ranges - ) 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 - ): - # Expression is never of any type in proposed_type_ranges - return UninhabitedType(), default - else: - # we can only restrict when the type is precise, not bounded - proposed_precise_type = UnionType.make_union( - [ - type_range.item - for type_range in proposed_type_ranges - if not type_range.is_upper_bound - ] + + if isinstance(proper_type, AnyType): + return proposed_type, current_type + elif isinstance(proposed_type, AnyType): + # We don't really know much about the proposed type, so we shouldn't + # attempt to narrow anything. Instead, we broaden the expr to Any to + # avoid false positives + return proposed_type, default + elif not any(type_range.is_upper_bound for type_range in proposed_type_ranges) and ( + # concrete subtypes + is_proper_subtype(current_type, proposed_type, ignore_promotions=True) + # structural subtypes + or ( + ( + isinstance(proposed_type, CallableType) + or (isinstance(proposed_type, Instance) and proposed_type.type.is_protocol) ) - remaining_type = restrict_subtype_away(current_type, proposed_precise_type) - return proposed_type, remaining_type + and is_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, ignore_promotions=True): + # Expression is never of any type in proposed_type_ranges + return UninhabitedType(), default else: - # An isinstance check, but we don't understand the type - return current_type, default + # we can only restrict when the type is precise, not bounded + proposed_precise_type = UnionType.make_union( + [ + type_range.item + for type_range in proposed_type_ranges + if not type_range.is_upper_bound + ] + ) + remaining_type = restrict_subtype_away( + current_type, + proposed_precise_type, + consider_runtime_isinstance=consider_runtime_isinstance, + ) + return proposed_type, remaining_type def conditional_types_to_typemaps( @@ -7625,7 +8415,7 @@ def conditional_types_to_typemaps( assert typ is not None maps.append({expr: typ}) - return cast(Tuple[TypeMap, TypeMap], tuple(maps)) + return cast(tuple[TypeMap, TypeMap], tuple(maps)) def gen_unique_name(base: str, table: SymbolTable) -> str: @@ -7653,7 +8443,7 @@ def is_literal_none(n: Expression) -> bool: return isinstance(n, NameExpr) and n.fullname == "builtins.None" -def is_literal_not_implemented(n: Expression) -> bool: +def is_literal_not_implemented(n: Expression | None) -> bool: return isinstance(n, NameExpr) and n.fullname == "builtins.NotImplemented" @@ -8086,7 +8876,9 @@ def _find_inplace_method(inst: Instance, method: str, operator: str) -> str | No return None -def is_valid_inferred_type(typ: Type, is_lvalue_final: bool = False) -> bool: +def is_valid_inferred_type( + typ: Type, options: Options, is_lvalue_final: bool = False, is_lvalue_member: bool = False +) -> bool: """Is an inferred type valid and needs no further refinement? Examples of invalid types include the None type (when we are not assigning @@ -8105,7 +8897,7 @@ def is_valid_inferred_type(typ: Type, is_lvalue_final: bool = False) -> bool: # type could either be NoneType or an Optional type, depending on # the context. This resolution happens in leave_partial_types when # we pop a partial types scope. - return is_lvalue_final + return is_lvalue_final or (not is_lvalue_member and options.allow_redefinition_new) elif isinstance(proper_type, UninhabitedType): return False return not typ.accept(InvalidInferredTypes()) @@ -8133,6 +8925,10 @@ def visit_type_var(self, t: TypeVarType) -> bool: # multi-step type inference. return t.id.is_meta_var() + def visit_tuple_type(self, t: TupleType, /) -> bool: + # Exclude fallback to avoid bogus "need type annotation" errors + return self.query_types(t.items) + class SetNothingToAny(TypeTranslator): """Replace all ambiguous Uninhabited types with Any (to avoid spurious extra errors).""" @@ -8148,79 +8944,28 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: return t.copy_modified(args=[a.accept(self) for a in t.args]) -def is_node_static(node: Node | None) -> bool | None: - """Find out if a node describes a static function method.""" +def is_classmethod_node(node: SymbolNode | None) -> bool | None: + """Find out if a node describes a classmethod.""" + if isinstance(node, Decorator): + node = node.func + if isinstance(node, FuncDef): + return node.is_class + if isinstance(node, Var): + return node.is_classmethod + return None + +def is_node_static(node: SymbolNode | None) -> bool | None: + """Find out if a node describes a static function method.""" + if isinstance(node, Decorator): + node = node.func if isinstance(node, FuncDef): return node.is_static - if isinstance(node, Var): return node.is_staticmethod - return None -class CheckerScope: - # We keep two stacks combined, to maintain the relative order - stack: list[TypeInfo | FuncItem | MypyFile] - - def __init__(self, module: MypyFile) -> None: - self.stack = [module] - - def top_function(self) -> FuncItem | None: - for e in reversed(self.stack): - if isinstance(e, FuncItem): - return e - return None - - def top_non_lambda_function(self) -> FuncItem | None: - for e in reversed(self.stack): - if isinstance(e, FuncItem) and not isinstance(e, LambdaExpr): - return e - return None - - def active_class(self) -> TypeInfo | None: - if isinstance(self.stack[-1], TypeInfo): - return self.stack[-1] - return None - - def enclosing_class(self) -> TypeInfo | None: - """Is there a class *directly* enclosing this function?""" - top = self.top_function() - assert top, "This method must be called from inside a function" - index = self.stack.index(top) - assert index, "CheckerScope stack must always start with a module" - enclosing = self.stack[index - 1] - if isinstance(enclosing, TypeInfo): - return enclosing - return None - - def active_self_type(self) -> Instance | TupleType | None: - """An instance or tuple type representing the current class. - - This returns None unless we are in class body or in a method. - In particular, inside a function nested in method this returns None. - """ - info = self.active_class() - if not info and self.top_function(): - info = self.enclosing_class() - if info: - return fill_typevars(info) - return None - - @contextmanager - def push_function(self, item: FuncItem) -> Iterator[None]: - self.stack.append(item) - yield - self.stack.pop() - - @contextmanager - def push_class(self, info: TypeInfo) -> Iterator[None]: - self.stack.append(info) - yield - self.stack.pop() - - TKey = TypeVar("TKey") TValue = TypeVar("TValue") @@ -8333,7 +9078,7 @@ def group_comparison_operands( x0 == x1 == x2 < x3 < x4 is x5 is x6 is not x7 is not x8 - If we get these expressions in a pairwise way (e.g. by calling ComparisionExpr's + If we get these expressions in a pairwise way (e.g. by calling ComparisonExpr's 'pairwise()' method), we get the following as input: [('==', x0, x1), ('==', x1, x2), ('<', x2, x3), ('<', x3, x4), @@ -8464,6 +9209,8 @@ def is_static(func: FuncBase | Decorator) -> bool: def is_property(defn: SymbolNode) -> bool: + if isinstance(defn, FuncDef): + return defn.is_property if isinstance(defn, Decorator): return defn.func.is_property if isinstance(defn, OverloadedFuncDef): @@ -8472,6 +9219,37 @@ def is_property(defn: SymbolNode) -> bool: return False +def is_settable_property(defn: SymbolNode | None) -> TypeGuard[OverloadedFuncDef]: + if isinstance(defn, OverloadedFuncDef): + if defn.items and isinstance(defn.items[0], Decorator): + return defn.items[0].func.is_property + return False + + +def is_custom_settable_property(defn: SymbolNode | None) -> bool: + """Check if a node is a settable property with a non-trivial setter type. + + By non-trivial here we mean that it is known (i.e. definition was already type + checked), it is not Any, and it is different from the property getter type. + """ + if defn is None: + return False + if not is_settable_property(defn): + return False + first_item = defn.items[0] + assert isinstance(first_item, Decorator) + if not first_item.var.is_settable_property: + return False + var = first_item.var + if var.type is None or var.setter_type is None or isinstance(var.type, PartialType): + # The caller should defer in case of partial types or not ready variables. + return False + setter_type = var.setter_type.arg_types[1] + if isinstance(get_proper_type(setter_type), AnyType): + return False + return not is_same_type(get_property_type(get_proper_type(var.type)), setter_type) + + def get_property_type(t: ProperType) -> ProperType: if isinstance(t, CallableType): return get_proper_type(t.ret_type) @@ -8650,3 +9428,18 @@ def _ambiguous_enum_variants(types: list[Type]) -> set[str]: else: result.add("") return result + + +def is_typeddict_type_context(lvalue_type: Type | None) -> bool: + if lvalue_type is None: + return False + lvalue_proper = get_proper_type(lvalue_type) + return isinstance(lvalue_proper, TypedDictType) + + +def is_method(node: SymbolNode | None) -> bool: + if isinstance(node, OverloadedFuncDef): + return not node.is_property + if isinstance(node, Decorator): + return not node.var.is_property + return isinstance(node, FuncDef) diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py new file mode 100644 index 0000000000000..0014d2c6fc880 --- /dev/null +++ b/mypy/checker_shared.py @@ -0,0 +1,354 @@ +"""Shared definitions used by different parts of type checker.""" + +from __future__ import annotations + +from abc import abstractmethod +from collections.abc import Iterator, Sequence +from contextlib import contextmanager +from typing import NamedTuple, overload + +from mypy_extensions import trait + +from mypy.errorcodes import ErrorCode +from mypy.errors import ErrorWatcher +from mypy.message_registry import ErrorMessage +from mypy.nodes import ( + ArgKind, + Context, + Expression, + FuncItem, + LambdaExpr, + MypyFile, + Node, + RefExpr, + SymbolNode, + TypeInfo, + Var, +) +from mypy.plugin import CheckerPluginInterface, Plugin +from mypy.types import ( + CallableType, + Instance, + LiteralValue, + Overloaded, + PartialType, + TupleType, + Type, + TypedDictType, + TypeType, +) +from mypy.typevars import fill_typevars + + +# An object that represents either a precise type or a type with an upper bound; +# it is important for correct type inference with isinstance. +class TypeRange(NamedTuple): + item: Type + is_upper_bound: bool # False => precise type + + +@trait +class ExpressionCheckerSharedApi: + @abstractmethod + def accept( + self, + node: Expression, + type_context: Type | None = None, + allow_none_return: bool = False, + always_allow_any: bool = False, + is_callee: bool = False, + ) -> Type: + raise NotImplementedError + + @abstractmethod + def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: + raise NotImplementedError + + @abstractmethod + def check_call( + self, + callee: Type, + args: list[Expression], + arg_kinds: list[ArgKind], + context: Context, + arg_names: Sequence[str | None] | None = None, + callable_node: Expression | None = None, + callable_name: str | None = None, + object_type: Type | None = None, + original_type: Type | None = None, + ) -> tuple[Type, Type]: + raise NotImplementedError + + @abstractmethod + def transform_callee_type( + self, + callable_name: str | None, + callee: Type, + args: list[Expression], + arg_kinds: list[ArgKind], + context: Context, + arg_names: Sequence[str | None] | None = None, + object_type: Type | None = None, + ) -> Type: + raise NotImplementedError + + @abstractmethod + def method_fullname(self, object_type: Type, method_name: str) -> str | None: + raise NotImplementedError + + @abstractmethod + def check_method_call_by_name( + self, + method: str, + base_type: Type, + args: list[Expression], + arg_kinds: list[ArgKind], + context: Context, + original_type: Type | None = None, + ) -> tuple[Type, Type]: + raise NotImplementedError + + @abstractmethod + def visit_typeddict_index_expr( + self, td_type: TypedDictType, index: Expression, setitem: bool = False + ) -> tuple[Type, set[str]]: + raise NotImplementedError + + @abstractmethod + def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type: + raise NotImplementedError + + @abstractmethod + def analyze_static_reference( + self, + node: SymbolNode, + ctx: Context, + is_lvalue: bool, + *, + include_modules: bool = True, + suppress_errors: bool = False, + ) -> Type: + raise NotImplementedError + + +@trait +class TypeCheckerSharedApi(CheckerPluginInterface): + plugin: Plugin + module_refs: set[str] + scope: CheckerScope + checking_missing_await: bool + allow_constructor_cache: bool + + @property + @abstractmethod + def expr_checker(self) -> ExpressionCheckerSharedApi: + raise NotImplementedError + + @abstractmethod + def named_type(self, name: str) -> Instance: + raise NotImplementedError + + @abstractmethod + def lookup_typeinfo(self, fullname: str) -> TypeInfo: + raise NotImplementedError + + @abstractmethod + def lookup_type(self, node: Expression) -> Type: + raise NotImplementedError + + @abstractmethod + def handle_cannot_determine_type(self, name: str, context: Context) -> None: + raise NotImplementedError + + @abstractmethod + def handle_partial_var_type( + self, typ: PartialType, is_lvalue: bool, node: Var, context: Context + ) -> Type: + raise NotImplementedError + + @overload + @abstractmethod + def check_subtype( + self, + subtype: Type, + supertype: Type, + context: Context, + msg: str, + subtype_label: str | None = None, + supertype_label: str | None = None, + *, + notes: list[str] | None = None, + code: ErrorCode | None = None, + outer_context: Context | None = None, + ) -> bool: ... + + @overload + @abstractmethod + def check_subtype( + self, + subtype: Type, + supertype: Type, + context: Context, + msg: ErrorMessage, + subtype_label: str | None = None, + supertype_label: str | None = None, + *, + notes: list[str] | None = None, + outer_context: Context | None = None, + ) -> bool: ... + + # Unfortunately, mypyc doesn't support abstract overloads yet. + @abstractmethod + def check_subtype( + self, + subtype: Type, + supertype: Type, + context: Context, + msg: str | ErrorMessage, + subtype_label: str | None = None, + supertype_label: str | None = None, + *, + notes: list[str] | None = None, + code: ErrorCode | None = None, + outer_context: Context | None = None, + ) -> bool: + raise NotImplementedError + + @abstractmethod + def get_final_context(self) -> bool: + raise NotImplementedError + + @overload + @abstractmethod + def conditional_types_with_intersection( + self, + expr_type: Type, + type_ranges: list[TypeRange] | None, + ctx: Context, + default: None = None, + ) -> tuple[Type | None, Type | None]: ... + + @overload + @abstractmethod + def conditional_types_with_intersection( + self, expr_type: Type, type_ranges: list[TypeRange] | None, ctx: Context, default: Type + ) -> tuple[Type, Type]: ... + + # Unfortunately, mypyc doesn't support abstract overloads yet. + @abstractmethod + def conditional_types_with_intersection( + self, + expr_type: Type, + type_ranges: list[TypeRange] | None, + ctx: Context, + default: Type | None = None, + ) -> tuple[Type | None, Type | None]: + raise NotImplementedError + + @abstractmethod + def check_deprecated(self, node: Node | None, context: Context) -> None: + raise NotImplementedError + + @abstractmethod + def warn_deprecated(self, node: Node | None, context: Context) -> None: + raise NotImplementedError + + @abstractmethod + def type_is_iterable(self, type: Type) -> bool: + raise NotImplementedError + + @abstractmethod + def iterable_item_type( + self, it: Instance | CallableType | TypeType | Overloaded, context: Context + ) -> Type: + raise NotImplementedError + + @abstractmethod + @contextmanager + def checking_await_set(self) -> Iterator[None]: + raise NotImplementedError + + @abstractmethod + def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None: + raise NotImplementedError + + @abstractmethod + def add_any_attribute_to_type(self, typ: Type, name: str) -> Type: + raise NotImplementedError + + @abstractmethod + def is_defined_in_stub(self, typ: Instance, /) -> bool: + raise NotImplementedError + + +class CheckerScope: + # We keep two stacks combined, to maintain the relative order + stack: list[TypeInfo | FuncItem | MypyFile] + + def __init__(self, module: MypyFile) -> None: + self.stack = [module] + + def current_function(self) -> FuncItem | None: + for e in reversed(self.stack): + if isinstance(e, FuncItem): + return e + return None + + def top_level_function(self) -> FuncItem | None: + """Return top-level non-lambda function.""" + for e in self.stack: + if isinstance(e, FuncItem) and not isinstance(e, LambdaExpr): + return e + return None + + def active_class(self) -> TypeInfo | None: + if isinstance(self.stack[-1], TypeInfo): + return self.stack[-1] + return None + + def enclosing_class(self, func: FuncItem | None = None) -> TypeInfo | None: + """Is there a class *directly* enclosing this function?""" + func = func or self.current_function() + assert func, "This method must be called from inside a function" + index = self.stack.index(func) + assert index, "CheckerScope stack must always start with a module" + enclosing = self.stack[index - 1] + if isinstance(enclosing, TypeInfo): + return enclosing + return None + + def active_self_type(self) -> Instance | TupleType | None: + """An instance or tuple type representing the current class. + + This returns None unless we are in class body or in a method. + In particular, inside a function nested in method this returns None. + """ + info = self.active_class() + if not info and self.current_function(): + info = self.enclosing_class() + if info: + return fill_typevars(info) + return None + + def current_self_type(self) -> Instance | TupleType | None: + """Same as active_self_type() but handle functions nested in methods.""" + for item in reversed(self.stack): + if isinstance(item, TypeInfo): + return fill_typevars(item) + return None + + def is_top_level(self) -> bool: + """Is current scope top-level (no classes or functions)?""" + return len(self.stack) == 1 + + @contextmanager + def push_function(self, item: FuncItem) -> Iterator[None]: + self.stack.append(item) + yield + self.stack.pop() + + @contextmanager + def push_class(self, info: TypeInfo) -> Iterator[None]: + self.stack.append(info) + yield + self.stack.pop() diff --git a/mypy/checker_state.py b/mypy/checker_state.py new file mode 100644 index 0000000000000..9b988ad18ba4f --- /dev/null +++ b/mypy/checker_state.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from collections.abc import Iterator +from contextlib import contextmanager +from typing import Final + +from mypy.checker_shared import TypeCheckerSharedApi + +# This is global mutable state. Don't add anything here unless there's a very +# good reason. + + +class TypeCheckerState: + # Wrap this in a class since it's faster that using a module-level attribute. + + def __init__(self, type_checker: TypeCheckerSharedApi | None) -> None: + # Value varies by file being processed + self.type_checker = type_checker + + @contextmanager + def set(self, value: TypeCheckerSharedApi) -> Iterator[None]: + saved = self.type_checker + self.type_checker = value + try: + yield + finally: + self.type_checker = saved + + +checker_state: Final = TypeCheckerState(type_checker=None) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 98e6eb6a7fc30..03ebc5058cee9 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -6,24 +6,28 @@ import itertools import time from collections import defaultdict -from contextlib import contextmanager -from typing import Callable, ClassVar, Final, Iterable, Iterator, List, Optional, Sequence, cast -from typing_extensions import TypeAlias as _TypeAlias, assert_never, overload +from collections.abc import Iterable, Iterator, Sequence +from contextlib import contextmanager, nullcontext +from typing import Callable, ClassVar, Final, Optional, cast, overload +from typing_extensions import TypeAlias as _TypeAlias, assert_never import mypy.checker import mypy.errorcodes as codes from mypy import applytype, erasetype, join, message_registry, nodes, operators, types from mypy.argmap import ArgTypeExpander, map_actuals_to_formals, map_formals_to_actuals -from mypy.checkmember import analyze_member_access, freeze_all_type_vars, type_object_type +from mypy.checker_shared import ExpressionCheckerSharedApi +from mypy.checkmember import analyze_member_access, has_operator from mypy.checkstrformat import StringFormatterChecker +from mypy.constant_fold import constant_fold_expr from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars -from mypy.errors import ErrorWatcher, report_internal_error +from mypy.errors import ErrorInfo, ErrorWatcher, report_internal_error from mypy.expandtype import ( expand_type, expand_type_by_instance, freshen_all_functions_type_vars, freshen_function_type_vars, ) +from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.infer import ArgumentInferContext, infer_function_type_arguments, infer_type_arguments from mypy.literals import literal from mypy.maptype import map_instance_to_supertype @@ -40,6 +44,7 @@ LITERAL_TYPE, REVEAL_LOCALS, REVEAL_TYPE, + UNBOUND_IMPORTED, ArgKind, AssertTypeExpr, AssignmentExpr, @@ -65,11 +70,13 @@ LambdaExpr, ListComprehension, ListExpr, + MaybeTypeExpression, MemberExpr, MypyFile, NamedTupleExpr, NameExpr, NewTypeExpr, + NotParsed, OpExpr, OverloadedFuncDef, ParamSpecExpr, @@ -84,19 +91,23 @@ StrExpr, SuperExpr, SymbolNode, + SymbolTableNode, TempNode, TupleExpr, TypeAlias, TypeAliasExpr, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeInfo, TypeVarExpr, + TypeVarLikeExpr, TypeVarTupleExpr, UnaryExpr, Var, YieldExpr, YieldFromExpr, + get_member_expr_fullname, ) from mypy.options import PRECISE_TUPLE_TYPES from mypy.plugin import ( @@ -115,8 +126,14 @@ is_subtype, non_method_protocol_members, ) -from mypy.traverser import has_await_expression +from mypy.traverser import ( + all_name_and_member_expressions, + has_await_expression, + has_str_expression, +) +from mypy.tvar_scope import TypeVarLikeScope from mypy.typeanal import ( + TypeAnalyser, check_for_explicit_any, fix_instance, has_any_from_unimported_type, @@ -131,6 +148,7 @@ erase_to_union_or_bound, false_only, fixup_partial_type, + freeze_all_type_vars, function_type, get_all_type_vars, get_type_vars, @@ -141,6 +159,7 @@ try_expanding_sum_type_to_union, try_getting_str_literals, tuple_fallback, + type_object_type, ) from mypy.types import ( LITERAL_TYPE_NAMES, @@ -168,6 +187,7 @@ TypeOfAny, TypeType, TypeVarId, + TypeVarLikeType, TypeVarTupleType, TypeVarType, UnboundType, @@ -180,6 +200,7 @@ get_proper_type, get_proper_types, has_recursive_types, + has_type_vars, is_named_instance, split_with_prefix_and_suffix, ) @@ -191,7 +212,6 @@ ) from mypy.typestate import type_state from mypy.typevars import fill_typevars -from mypy.util import split_module_names from mypy.visitor import ExpressionVisitor # Type of callback user for checking individual function arguments. See @@ -241,38 +261,6 @@ def allow_fast_container_literal(t: Type) -> bool: ) -def extract_refexpr_names(expr: RefExpr) -> set[str]: - """Recursively extracts all module references from a reference expression. - - Note that currently, the only two subclasses of RefExpr are NameExpr and - MemberExpr.""" - output: set[str] = set() - while isinstance(expr.node, MypyFile) or expr.fullname: - if isinstance(expr.node, MypyFile) and expr.fullname: - # If it's None, something's wrong (perhaps due to an - # import cycle or a suppressed error). For now we just - # skip it. - output.add(expr.fullname) - - if isinstance(expr, NameExpr): - is_suppressed_import = isinstance(expr.node, Var) and expr.node.is_suppressed_import - if isinstance(expr.node, TypeInfo): - # Reference to a class or a nested class - output.update(split_module_names(expr.node.module_name)) - elif "." in expr.fullname and not is_suppressed_import: - # Everything else (that is not a silenced import within a class) - output.add(expr.fullname.rsplit(".", 1)[0]) - break - elif isinstance(expr, MemberExpr): - if isinstance(expr.expr, RefExpr): - expr = expr.expr - else: - break - else: - raise AssertionError(f"Unknown RefExpr subclass: {type(expr)}") - return output - - class Finished(Exception): """Raised if we can terminate overload argument check early (no match).""" @@ -291,7 +279,7 @@ class UseReverse(enum.Enum): USE_REVERSE_NEVER: Final = UseReverse.NEVER -class ExpressionChecker(ExpressionVisitor[Type]): +class ExpressionChecker(ExpressionVisitor[Type], ExpressionCheckerSharedApi): """Expression type checker. This class works closely together with checker.TypeChecker. @@ -310,6 +298,8 @@ class ExpressionChecker(ExpressionVisitor[Type]): strfrm_checker: StringFormatterChecker plugin: Plugin + _arg_infer_context_cache: ArgumentInferContext | None + def __init__( self, chk: mypy.checker.TypeChecker, @@ -333,7 +323,7 @@ def __init__( # TODO: refactor this to use a pattern similar to one in # multiassign_from_union, or maybe even combine the two? self.type_overrides: dict[Expression, Type] = {} - self.strfrm_checker = StringFormatterChecker(self, self.chk, self.msg) + self.strfrm_checker = StringFormatterChecker(self.chk, self.msg) self.resolved_type = {} @@ -344,17 +334,29 @@ def __init__( self.is_callee = False type_state.infer_polymorphic = not self.chk.options.old_type_inference + self._arg_infer_context_cache = None + self.expr_cache: dict[ + tuple[Expression, Type | None], + tuple[int, Type, list[ErrorInfo], dict[Expression, Type]], + ] = {} + self.in_lambda_expr = False + + self._literal_true: Instance | None = None + self._literal_false: Instance | None = None + def reset(self) -> None: self.resolved_type = {} + self.expr_cache.clear() def visit_name_expr(self, e: NameExpr) -> Type: """Type check a name expression. It can be of any kind: local, member or global. """ - 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 @@ -369,9 +371,8 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result = self.analyze_var_ref(node, e) if isinstance(result, PartialType): result = self.chk.handle_partial_var_type(result, lvalue, node, e) - elif isinstance(node, FuncDef): - # Reference to a global function. - result = function_type(node, self.named_type("builtins.function")) + elif isinstance(node, Decorator): + result = self.analyze_var_ref(node.var, e) elif isinstance(node, OverloadedFuncDef): if node.type is None: if self.chk.in_checked_function() and node.items: @@ -379,16 +380,15 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result = AnyType(TypeOfAny.from_error) else: result = node.type - elif isinstance(node, TypeInfo): - # Reference to a type object. - if node.typeddict_type: - # We special-case TypedDict, because they don't define any constructor. - result = self.typeddict_callable(node) - elif node.fullname == "types.NoneType": - # We special case NoneType, because its stub definition is not related to None. - result = TypeType(NoneType()) - else: - result = type_object_type(node, self.named_type) + elif isinstance(node, (FuncDef, TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)): + result = self.analyze_static_reference(node, e, e.is_alias_rvalue or lvalue) + else: + if isinstance(node, PlaceholderNode): + assert False, f"PlaceholderNode {node.fullname!r} leaked to checker" + # Unknown reference; use any type implicitly to avoid + # generating extra type errors. + result = AnyType(TypeOfAny.from_error) + if isinstance(node, TypeInfo): if isinstance(result, CallableType) and isinstance( # type: ignore[misc] result.ret_type, Instance ): @@ -396,34 +396,60 @@ 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): - # Reference to a module object. - result = self.module_type(node) - elif isinstance(node, Decorator): - result = self.analyze_var_ref(node.var, e) + assert result is not None + return result + + def analyze_static_reference( + self, + node: SymbolNode, + ctx: Context, + is_lvalue: bool, + *, + include_modules: bool = True, + suppress_errors: bool = False, + ) -> Type: + """ + This is the version of analyze_ref_expr() that doesn't do any deferrals. + + This function can be used by member access to "static" attributes. For example, + when accessing module attributes in protocol checks, or accessing attributes of + special kinds (like TypeAlias, TypeInfo, etc.) on an instance or class object. + # TODO: merge with analyze_ref_expr() when we are confident about performance. + """ + if isinstance(node, (Var, Decorator, OverloadedFuncDef)): + return node.type or AnyType(TypeOfAny.special_form) + elif isinstance(node, FuncDef): + return function_type(node, self.named_type("builtins.function")) + elif isinstance(node, TypeInfo): + # Reference to a type object. + if node.typeddict_type: + # We special-case TypedDict, because they don't define any constructor. + return self.typeddict_callable(node) + elif node.fullname == "types.NoneType": + # We special case NoneType, because its stub definition is not related to None. + return TypeType(NoneType()) + else: + return type_object_type(node, self.named_type) elif isinstance(node, TypeAlias): # Something that refers to a type alias appears in runtime context. # Note that we suppress bogus errors for alias redefinitions, # they are already reported in semanal.py. - result = self.alias_type_in_runtime_context( - node, ctx=e, alias_definition=e.is_alias_rvalue or lvalue - ) + with self.msg.filter_errors() if suppress_errors else nullcontext(): + return self.alias_type_in_runtime_context( + node, ctx=ctx, alias_definition=is_lvalue + ) elif isinstance(node, TypeVarExpr): return self.named_type("typing.TypeVar") elif isinstance(node, (ParamSpecExpr, TypeVarTupleExpr)): - result = self.object_type() - else: - if isinstance(node, PlaceholderNode): - assert False, f"PlaceholderNode {node.fullname!r} leaked to checker" - # Unknown reference; use any type implicitly to avoid - # generating extra type errors. - result = AnyType(TypeOfAny.from_error) - assert result is not None - return result + return self.object_type() + elif isinstance(node, MypyFile): + # Reference to a module object. + return self.module_type(node) if include_modules else AnyType(TypeOfAny.special_form) + return AnyType(TypeOfAny.from_error) def analyze_var_ref(self, var: Var, context: Context) -> Type: if var.type: @@ -450,21 +476,23 @@ def module_type(self, node: MypyFile) -> Instance: # In test cases might 'types' may not be available. # Fall back to a dummy 'object' type instead to # avoid a crash. - result = self.named_type("builtins.object") - module_attrs = {} + # Make a copy so that we don't set extra_attrs (below) on a shared instance. + result = self.named_type("builtins.object").copy_modified() + module_attrs: dict[str, Type] = {} immutable = set() for name, n in node.names.items(): if not n.module_public: continue if isinstance(n.node, Var) and n.node.is_final: immutable.add(name) - typ = self.chk.determine_type_of_member(n) - if typ: - module_attrs[name] = typ + if n.node is None: + module_attrs[name] = AnyType(TypeOfAny.from_error) else: # TODO: what to do about nested module references? # They are non-trivial because there may be import cycles. - module_attrs[name] = AnyType(TypeOfAny.special_form) + module_attrs[name] = self.analyze_static_reference( + n.node, n.node, False, include_modules=False, suppress_errors=True + ) result.extra_attrs = ExtraAttrs(module_attrs, immutable, node.fullname) return result @@ -535,7 +563,10 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> and not node.node.no_args and not ( isinstance(union_target := get_proper_type(node.node.target), UnionType) - and union_target.uses_pep604_syntax + and ( + union_target.uses_pep604_syntax + or self.chk.options.python_version >= (3, 10) + ) ) ): self.msg.type_arguments_not_allowed(e) @@ -639,11 +670,12 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> return ret_type def check_str_format_call(self, e: CallExpr) -> None: - """More precise type checking for str.format() calls on literals.""" + """More precise type checking for str.format() calls on literals and folded constants.""" assert isinstance(e.callee, MemberExpr) format_value = None - if isinstance(e.callee.expr, StrExpr): - format_value = e.callee.expr.value + folded_callee_expr = constant_fold_expr(e.callee.expr, "") + if isinstance(folded_callee_expr, str): + format_value = folded_callee_expr elif self.chk.has_type(e.callee.expr): typ = get_proper_type(self.chk.lookup_type(e.callee.expr)) if ( @@ -840,7 +872,7 @@ def validate_typeddict_kwargs( # Having an optional key not explicitly declared by a ** unpacked # TypedDict is unsafe, it may be an (incompatible) subtype at runtime. # TODO: catch the cases where a declared key is overridden by a subsequent - # ** item without it (and not again overriden with complete ** item). + # ** item without it (and not again overridden with complete ** item). self.msg.non_required_keys_absent_with_star(absent_keys, last_star_found) return result, always_present_keys @@ -953,19 +985,11 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType: assert info.special_alias is not None target = info.special_alias.target assert isinstance(target, ProperType) and isinstance(target, TypedDictType) - expected_types = list(target.items.values()) - kinds = [ArgKind.ARG_NAMED] * len(expected_types) - names = list(target.items.keys()) - return CallableType( - expected_types, - kinds, - names, - target, - self.named_type("builtins.type"), - variables=info.defn.type_vars, - ) + return self.typeddict_callable_from_context(target, info.defn.type_vars) - def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType: + def typeddict_callable_from_context( + self, callee: TypedDictType, variables: Sequence[TypeVarLikeType] | None = None + ) -> CallableType: return CallableType( list(callee.items.values()), [ @@ -975,6 +999,8 @@ def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType list(callee.items.keys()), callee, self.named_type("builtins.type"), + variables=variables, + is_bound=True, ) def check_typeddict_call_with_kwargs( @@ -1034,7 +1060,7 @@ def check_typeddict_call_with_kwargs( # We don't show any errors, just infer types in a generic TypedDict type, # a custom error message will be given below, if there are errors. - with self.msg.filter_errors(), self.chk.local_type_map(): + with self.msg.filter_errors(), self.chk.local_type_map: orig_ret_type, _ = self.check_callable_call( infer_callee, # We use first expression for each key to infer type variables of a generic @@ -1120,8 +1146,7 @@ def try_infer_partial_type(self, e: CallExpr) -> None: typ = self.try_infer_partial_value_type_from_call(e, callee.name, var) # Var may be deleted from partial_types in try_infer_partial_value_type_from_call if typ is not None and var in partial_types: - var.type = typ - del partial_types[var] + self.chk.replace_partial_type(var, typ, partial_types) elif isinstance(callee.expr, IndexExpr) and isinstance(callee.expr.base, RefExpr): # Call 'x[y].method(...)'; may infer type of 'x' if it's a partial defaultdict. if callee.expr.analyzed is not None: @@ -1139,12 +1164,12 @@ def try_infer_partial_type(self, e: CallExpr) -> None: if value_type is not None: # Infer key type. key_type = self.accept(index) - if mypy.checker.is_valid_inferred_type(key_type): + if mypy.checker.is_valid_inferred_type(key_type, self.chk.options): # Store inferred partial type. assert partial_type.type is not None typename = partial_type.type.fullname - var.type = self.chk.named_generic_type(typename, [key_type, value_type]) - del partial_types[var] + new_type = self.chk.named_generic_type(typename, [key_type, value_type]) + self.chk.replace_partial_type(var, new_type, partial_types) def get_partial_var(self, ref: RefExpr) -> tuple[Var, dict[Var, Context]] | None: var = ref.node @@ -1179,7 +1204,7 @@ def try_infer_partial_value_type_from_call( and e.arg_kinds == [ARG_POS] ): item_type = self.accept(e.args[0]) - if mypy.checker.is_valid_inferred_type(item_type): + if mypy.checker.is_valid_inferred_type(item_type, self.chk.options): return self.chk.named_generic_type(typename, [item_type]) elif ( typename in self.container_args @@ -1191,7 +1216,7 @@ def try_infer_partial_value_type_from_call( arg_typename = arg_type.type.fullname if arg_typename in self.container_args[typename][methodname]: if all( - mypy.checker.is_valid_inferred_type(item_type) + mypy.checker.is_valid_inferred_type(item_type, self.chk.options) for item_type in arg_type.args ): return self.chk.named_generic_type(typename, list(arg_type.args)) @@ -1400,7 +1425,7 @@ def is_generic_decorator_overload_call( return None if not isinstance(get_proper_type(callee_type.ret_type), CallableType): return None - with self.chk.local_type_map(): + with self.chk.local_type_map: with self.msg.filter_errors(): arg_type = get_proper_type(self.accept(args[0], type_context=None)) if isinstance(arg_type, Overloaded): @@ -1490,17 +1515,16 @@ def check_call_expr_with_callee_type( def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str) -> Type: """Type check calling a member expression where the base type is a union.""" res: list[Type] = [] - for typ in object_type.relevant_items(): + for typ in flatten_nested_unions(object_type.relevant_items()): # Member access errors are already reported when visiting the member expression. with self.msg.filter_errors(): item = analyze_member_access( member, typ, e, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, original_type=object_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -1588,7 +1612,6 @@ def check_call( is_lvalue=False, is_super=False, is_operator=True, - msg=self.msg, original_type=original_type or callee, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -1632,6 +1655,10 @@ def check_call( object_type, original_type=callee, ) + elif isinstance(callee, UninhabitedType): + ret = UninhabitedType() + ret.ambiguous = callee.ambiguous + return callee, ret else: return self.msg.not_callable(callee, context), AnyType(TypeOfAny.from_error) @@ -1712,14 +1739,6 @@ def check_callable_call( return AnyType(TypeOfAny.from_error), callee seen_unpack = True - formal_to_actual = map_actuals_to_formals( - arg_kinds, - arg_names, - callee.arg_kinds, - callee.arg_names, - lambda i: self.accept(args[i]), - ) - # This is tricky: return type may contain its own type variables, like in # def [S] (S) -> def [T] (T) -> tuple[S, T], so we need to update their ids # to avoid possible id clashes if this call itself appears in a generic @@ -1730,27 +1749,29 @@ def check_callable_call( freeze_all_type_vars(fresh_ret_type) callee = callee.copy_modified(ret_type=fresh_ret_type) + if callee.is_generic(): + callee = freshen_function_type_vars(callee) + callee = self.infer_function_type_arguments_using_context(callee, context) + + formal_to_actual = map_actuals_to_formals( + arg_kinds, + arg_names, + callee.arg_kinds, + callee.arg_names, + lambda i: self.accept(args[i]), + ) + if callee.is_generic(): need_refresh = any( isinstance(v, (ParamSpecType, TypeVarTupleType)) for v in callee.variables ) - callee = freshen_function_type_vars(callee) - callee = self.infer_function_type_arguments_using_context(callee, context) - if need_refresh: - # Argument kinds etc. may have changed due to - # ParamSpec or TypeVarTuple variables being replaced with an arbitrary - # number of arguments; recalculate actual-to-formal map - formal_to_actual = map_actuals_to_formals( - arg_kinds, - arg_names, - callee.arg_kinds, - callee.arg_names, - lambda i: self.accept(args[i]), - ) callee = self.infer_function_type_arguments( callee, args, arg_kinds, arg_names, formal_to_actual, need_refresh, context ) if need_refresh: + # Argument kinds etc. may have changed due to + # ParamSpec or TypeVarTuple variables being replaced with an arbitrary + # number of arguments; recalculate actual-to-formal map formal_to_actual = map_actuals_to_formals( arg_kinds, arg_names, @@ -1959,7 +1980,7 @@ def infer_arg_types_in_context( if not t: res[i] = self.accept(args[i]) assert all(tp is not None for tp in res) - return cast(List[Type], res) + return cast(list[Type], res) def infer_function_type_arguments_using_context( self, callable: CallableType, error_context: Context @@ -1999,7 +2020,12 @@ def infer_function_type_arguments_using_context( # variables in an expression are inferred at the same time. # (And this is hard, also we need to be careful with lambdas that require # two passes.) - if isinstance(ret_type, TypeVarType): + proper_ret = get_proper_type(ret_type) + if ( + isinstance(proper_ret, TypeVarType) + or isinstance(proper_ret, UnionType) + and all(isinstance(get_proper_type(u), TypeVarType) for u in proper_ret.items) + ): # Another special case: the return type is a type variable. If it's unrestricted, # we could infer a too general type for the type variable if we use context, # and this could result in confusing and spurious type errors elsewhere. @@ -2210,6 +2236,11 @@ def infer_function_type_arguments_pass2( if isinstance(arg, (NoneType, UninhabitedType)) or has_erased_component(arg): inferred_args[i] = None callee_type = self.apply_generic_arguments(callee_type, inferred_args, context) + + if not callee_type.is_generic(): + # Fast path, second pass can't give new information. + return callee_type, [] + if need_refresh: formal_to_actual = map_actuals_to_formals( arg_kinds, @@ -2237,9 +2268,11 @@ def infer_function_type_arguments_pass2( return callee_type, inferred_args def argument_infer_context(self) -> ArgumentInferContext: - return ArgumentInferContext( - self.chk.named_type("typing.Mapping"), self.chk.named_type("typing.Iterable") - ) + if self._arg_infer_context_cache is None: + self._arg_infer_context_cache = ArgumentInferContext( + self.chk.named_type("typing.Mapping"), self.chk.named_type("typing.Iterable") + ) + return self._arg_infer_context_cache def get_arg_infer_passes( self, @@ -2298,10 +2331,10 @@ def apply_inferred_arguments( # Report error if some of the variables could not be solved. In that # case assume that all variables have type Any to avoid extra # bogus error messages. - for i, inferred_type in enumerate(inferred_args): + for inferred_type, tv in zip(inferred_args, callee_type.variables): if not inferred_type or has_erased_component(inferred_type): # Could not infer a non-trivial type for a type variable. - self.msg.could_not_infer_type_arguments(callee_type, i + 1, context) + self.msg.could_not_infer_type_arguments(callee_type, tv, context) inferred_args = [AnyType(TypeOfAny.from_error)] * len(inferred_args) # Apply the inferred types to the function type. In this case the # return type must be CallableType, since we give the right number of type @@ -2344,7 +2377,8 @@ def check_argument_count( # Check for too many or few values for formals. for i, kind in enumerate(callee.arg_kinds): - if kind.is_required() and not formal_to_actual[i] and not is_unexpected_arg_error: + mapped_args = formal_to_actual[i] + if kind.is_required() and not mapped_args and not is_unexpected_arg_error: # No actual for a mandatory formal if kind.is_positional(): self.msg.too_few_arguments(callee, context, actual_names) @@ -2355,24 +2389,36 @@ def check_argument_count( self.msg.missing_named_argument(callee, context, argname) ok = False elif not kind.is_star() and is_duplicate_mapping( - formal_to_actual[i], actual_types, actual_kinds + mapped_args, actual_types, actual_kinds ): if self.chk.in_checked_function() or isinstance( - get_proper_type(actual_types[formal_to_actual[i][0]]), TupleType + get_proper_type(actual_types[mapped_args[0]]), TupleType ): self.msg.duplicate_argument_value(callee, i, context) ok = False elif ( kind.is_named() - and formal_to_actual[i] - and actual_kinds[formal_to_actual[i][0]] not in [nodes.ARG_NAMED, nodes.ARG_STAR2] + and mapped_args + and actual_kinds[mapped_args[0]] not in [nodes.ARG_NAMED, nodes.ARG_STAR2] ): # 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]: - self.msg.too_few_arguments(callee, context, actual_names) - ok = False + elif callee.param_spec() is not None: + if not mapped_args and callee.special_sig != "partial": + self.msg.too_few_arguments(callee, context, actual_names) + ok = False + elif len(mapped_args) > 1: + paramspec_entries = sum( + isinstance(get_proper_type(actual_types[k]), ParamSpecType) + for k in mapped_args + ) + if actual_kinds[mapped_args[0]] == nodes.ARG_STAR and paramspec_entries > 1: + self.msg.fail("ParamSpec.args should only be passed once", context) + ok = False + if actual_kinds[mapped_args[0]] == nodes.ARG_STAR2 and paramspec_entries > 1: + self.msg.fail("ParamSpec.kwargs should only be passed once", context) + ok = False return ok def check_for_extra_actual_arguments( @@ -2470,6 +2516,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]) @@ -2559,18 +2616,7 @@ def check_argument_types( for actual, actual_type, actual_kind, callee_arg_type, callee_arg_kind in zip( actuals, actual_types, actual_kinds, callee_arg_types, callee_arg_kinds ): - 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, @@ -2615,7 +2661,7 @@ def check_arg( elif self.has_abstract_type_part(caller_type, callee_type): self.msg.concrete_only_call(callee_type, context) elif not is_subtype(caller_type, callee_type, options=self.chk.options): - code = self.msg.incompatible_argument( + error = self.msg.incompatible_argument( n, m, callee, @@ -2625,11 +2671,16 @@ def check_arg( context=context, outer_context=outer_context, ) - self.msg.incompatible_argument_note( - original_caller_type, callee_type, context, code=code - ) + if not caller_kind.is_star(): + # For *args and **kwargs this note would be incorrect - we're comparing + # iterable/mapping type with union of relevant arg types. + self.msg.incompatible_argument_note( + original_caller_type, callee_type, context, parent_error=error + ) if not self.msg.prefer_simple_messages(): - self.chk.check_possible_missing_await(caller_type, callee_type, context, code) + self.chk.check_possible_missing_await( + caller_type, callee_type, context, error.code + ) def check_overload_call( self, @@ -2656,6 +2707,7 @@ def check_overload_call( # for example, when we have a fallback alternative that accepts an unrestricted # typevar. See https://github.com/python/mypy/issues/4063 for related discussion. erased_targets: list[CallableType] | None = None + inferred_types: list[Type] | None = None unioned_result: tuple[Type, Type] | None = None # Determine whether we need to encourage union math. This should be generally safe, @@ -2683,13 +2735,14 @@ def check_overload_call( # Record if we succeeded. Next we need to see if maybe normal procedure # gives a narrower type. if unioned_return: - returns, inferred_types = zip(*unioned_return) + returns = [u[0] for u in unioned_return] + inferred_types = [u[1] for u in unioned_return] # Note that we use `combine_function_signatures` instead of just returning # a union of inferred callables because for example a call # Union[int -> int, str -> str](Union[int, str]) is invalid and # we don't want to introduce internal inconsistencies. unioned_result = ( - make_simplified_union(list(returns), context.line, context.column), + make_simplified_union(returns, context.line, context.column), self.combine_function_signatures(get_proper_types(inferred_types)), ) @@ -2704,7 +2757,7 @@ def check_overload_call( object_type, context, ) - # If any of checks succeed, stop early. + # If any of checks succeed, perform deprecation tests and stop early. if inferred_result is not None and unioned_result is not None: # Both unioned and direct checks succeeded, choose the more precise type. if ( @@ -2712,11 +2765,18 @@ def check_overload_call( and not isinstance(get_proper_type(inferred_result[0]), AnyType) and not none_type_var_overlap ): - return inferred_result - return unioned_result - elif unioned_result is not None: + unioned_result = None + else: + inferred_result = None + if unioned_result is not None: + if inferred_types is not None: + for inferred_type in inferred_types: + if isinstance(c := get_proper_type(inferred_type), CallableType): + self.chk.warn_deprecated(c.definition, context) return unioned_result - elif inferred_result is not None: + if inferred_result is not None: + if isinstance(c := get_proper_type(inferred_result[1]), CallableType): + self.chk.warn_deprecated(c.definition, context) return inferred_result # Step 4: Failure. At this point, we know there is no match. We fall back to trying @@ -2774,7 +2834,7 @@ 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, including + 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. @@ -2809,7 +2869,9 @@ def has_shape(typ: Type) -> bool: # 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. - star_matches.append(typ) + # 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 ): @@ -2852,7 +2914,7 @@ def infer_overload_return_type( for typ in plausible_targets: assert self.msg is self.chk.msg with self.msg.filter_errors() as w: - with self.chk.local_type_map() as m: + with self.chk.local_type_map as m: ret_type, infer_type = self.check_call( callee=typ, args=args, @@ -3265,12 +3327,18 @@ def check_union_call( 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, rvalue: Expression | None = None + ) -> Type: + """Analyse member expression or member lvalue. - def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type: - """Analyse member expression or member lvalue.""" + An rvalue can be provided optionally to infer better setter type when is_lvalue is True. + """ if e.kind is not None: # This is a reference to a module attribute. return self.analyze_ref_expr(e) @@ -3293,15 +3361,15 @@ 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, original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), module_symbol_table=module_symbol_table, is_self=is_self, + rvalue=rvalue, ) return member_type @@ -3317,10 +3385,9 @@ def analyze_external_member_access( member, base_type, context, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, original_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3350,11 +3417,19 @@ def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Ty if self.is_literal_context(): return LiteralType(value=value, fallback=typ) else: - return typ.copy_modified( - last_known_value=LiteralType( - value=value, fallback=typ, line=typ.line, column=typ.column - ) - ) + if value is True: + if self._literal_true is None: + self._literal_true = typ.copy_modified( + last_known_value=LiteralType(value=value, fallback=typ) + ) + return self._literal_true + if value is False: + if self._literal_false is None: + self._literal_false = typ.copy_modified( + last_known_value=LiteralType(value=value, fallback=typ) + ) + return self._literal_false + return typ.copy_modified(last_known_value=LiteralType(value=value, fallback=typ)) def concat_tuples(self, left: TupleType, right: TupleType) -> TupleType: """Concatenate two fixed length tuples.""" @@ -3393,7 +3468,7 @@ def visit_op_expr(self, e: OpExpr) -> Type: # It's actually a type expression X | Y. return self.accept(e.analyzed) if e.op == "and" or e.op == "or": - return self.check_boolean_op(e, e) + return self.check_boolean_op(e) if e.op == "*" and isinstance(e.left, ListExpr): # Expressions of form [...] * e get special type inference. return self.check_list_multiply(e) @@ -3627,7 +3702,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: elif operator == "is" or operator == "is not": right_type = self.accept(right) # validate the right operand sub_result = self.bool_type() - if self.dangerous_comparison(left_type, right_type): + if self.dangerous_comparison(left_type, right_type, identity_check=True): # Show the most specific literal types possible left_type = try_getting_literal(left_type) right_type = try_getting_literal(right_type) @@ -3669,6 +3744,7 @@ def dangerous_comparison( original_container: Type | None = None, seen_types: set[tuple[Type, Type]] | None = None, prefer_literal: bool = True, + identity_check: bool = False, ) -> bool: """Check for dangerous non-overlapping comparisons like 42 == 'no'. @@ -3696,10 +3772,12 @@ def dangerous_comparison( left, right = get_proper_types((left, right)) - # We suppress the error if there is a custom __eq__() method on either - # side. User defined (or even standard library) classes can define this + # We suppress the error for equality and container checks if there is a custom __eq__() + # method on either side. User defined (or even standard library) classes can define this # to return True for comparisons between non-overlapping types. - if custom_special_method(left, "__eq__") or custom_special_method(right, "__eq__"): + if ( + custom_special_method(left, "__eq__") or custom_special_method(right, "__eq__") + ) and not identity_check: return False if prefer_literal: @@ -3723,7 +3801,10 @@ def dangerous_comparison( # # TODO: find a way of disabling the check only for types resulted from the expansion. return False - if isinstance(left, NoneType) or isinstance(right, NoneType): + if self.chk.options.strict_equality_for_none: + if isinstance(left, NoneType) and isinstance(right, NoneType): + return False + elif isinstance(left, NoneType) or isinstance(right, NoneType): return False if isinstance(left, UnionType) and isinstance(right, UnionType): left = remove_optional(left) @@ -3772,6 +3853,18 @@ def dangerous_comparison( if isinstance(left.value, bool) and isinstance(right.value, bool): # Comparing different booleans is not dangerous. return False + if isinstance(left, LiteralType) and isinstance(right, Instance): + # bytes/bytearray comparisons are supported + if left.fallback.type.fullname == "builtins.bytes" and right.type.has_base( + "builtins.bytearray" + ): + return False + if isinstance(right, LiteralType) and isinstance(left, Instance): + # bytes/bytearray comparisons are supported + if right.fallback.type.fullname == "builtins.bytes" and left.type.has_base( + "builtins.bytearray" + ): + return False return not is_overlapping_types(left, right, ignore_promotions=False) def check_method_call_by_name( @@ -3782,13 +3875,16 @@ def check_method_call_by_name( arg_kinds: list[ArgKind], context: Context, original_type: Type | None = None, + self_type: Type | None = None, ) -> tuple[Type, Type]: """Type check a call to a named method on an object. Return tuple (result type, inferred method type). The 'original_type' - is used for error messages. + is used for error messages. The self_type is to bind self in methods + (see analyze_member_access for more details). """ original_type = original_type or base_type + self_type = self_type or base_type # Unions are special-cased to allow plugins to act on each element of the union. base_type = get_proper_type(base_type) if isinstance(base_type, UnionType): @@ -3800,11 +3896,11 @@ def check_method_call_by_name( method, base_type, context, - False, - False, - True, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=True, original_type=original_type, + self_type=self_type, chk=self.chk, in_literal_context=self.is_literal_context(), ) @@ -3881,11 +3977,8 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None: """Looks up the given operator and returns the corresponding type, if it exists.""" - # This check is an important performance optimization, - # even though it is mostly a subset of - # analyze_member_access. - # TODO: Find a way to remove this call without performance implications. - if not self.has_member(base_type, op_name): + # This check is an important performance optimization. + if not has_operator(base_type, op_name, self.named_type): return None with self.msg.filter_errors() as w: @@ -3897,7 +3990,6 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None: is_operator=True, original_type=base_type, context=context, - msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), ) @@ -3956,7 +4048,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) @@ -3977,19 +4069,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 @@ -3998,9 +4096,9 @@ 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) + result = self.check_method_call(name, obj, method, [arg], [ARG_POS], context) if local_errors.has_new_errors(): errors.append(local_errors.filtered_errors()) results.append(result) @@ -4030,14 +4128,8 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: errors.append(local_errors.filtered_errors()) results.append(result) else: - # In theory, we should never enter this case, but it seems - # we sometimes do, when dealing with Type[...]? E.g. see - # check-classes.testTypeTypeComparisonWorks. - # - # This is probably related to the TODO in lookup_operator(...) - # up above. - # - # TODO: Remove this extra case + # Although we should not need this case anymore, we keep it just in case, as + # otherwise we will get a crash if we introduce inconsistency in checkmember.py return result self.msg.add_errors(errors[0]) @@ -4159,20 +4251,18 @@ def check_op( context=context, ) - def check_boolean_op(self, e: OpExpr, context: Context) -> Type: + def check_boolean_op(self, e: OpExpr) -> Type: """Type check a boolean operation ('and' or 'or').""" # A boolean operation can evaluate to either of the operands. - # We use the current type context to guide the type inference of of + # We use the current type context to guide the type inference of # the left operand. We also use the left operand type to guide the type # inference of the right operand so that expressions such as # '[1] or []' are inferred correctly. ctx = self.type_context[-1] left_type = self.accept(e.left, ctx) - expanded_left_type = try_expanding_sum_type_to_union( - self.accept(e.left, ctx), "builtins.bool" - ) + expanded_left_type = try_expanding_sum_type_to_union(left_type, "builtins.bool") assert e.op in ("and", "or") # Checked by visit_op_expr @@ -4204,11 +4294,9 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: ): self.msg.unreachable_right_operand(e.op, e.right) - # If right_map is None then we know mypy considers the right branch - # to be unreachable and therefore any errors found in the right branch - # should be suppressed. - with self.msg.filter_errors(filter_errors=right_map is None): - right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type) + right_type = self.analyze_cond_branch( + right_map, e.right, self._combined_context(expanded_left_type) + ) if left_map is None and right_map is None: return UninhabitedType() @@ -4302,13 +4390,19 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type: return self.visit_index_with_type(left_type, e) def visit_index_with_type( - self, left_type: Type, e: IndexExpr, original_type: ProperType | None = None + self, + left_type: Type, + e: IndexExpr, + original_type: ProperType | None = None, + self_type: Type | None = None, ) -> Type: """Analyze type of an index expression for a given type of base expression. - The 'original_type' is used for error messages (currently used for union types). + The 'original_type' is used for error messages (currently used for union types). The + 'self_type' is to bind self in methods (see analyze_member_access for more details). """ index = e.index + self_type = self_type or left_type left_type = get_proper_type(left_type) # Visit the index, just to make sure we have a type for it available @@ -4357,22 +4451,28 @@ def visit_index_with_type( 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 self.chk.options.python_version >= (3, 9) and ( + elif ( left_type.type_object().type_vars or left_type.type_object().fullname == "builtins.type" ): return self.named_type("types.GenericAlias") - if isinstance(left_type, TypeVarType) and not self.has_member( - left_type.upper_bound, "__getitem__" - ): - return self.visit_index_with_type(left_type.upper_bound, e, original_type) + if isinstance(left_type, TypeVarType): + return self.visit_index_with_type( + left_type.values_or_bound(), e, original_type, left_type + ) elif isinstance(left_type, Instance) and left_type.type.fullname == "typing._SpecialForm": # Allow special forms to be indexed and used to create union types return self.named_type("typing._SpecialForm") else: result, method_type = self.check_method_call_by_name( - "__getitem__", left_type, [e.index], [ARG_POS], e, original_type=original_type + "__getitem__", + left_type, + [e.index], + [ARG_POS], + e, + original_type=original_type, + self_type=self_type, ) e.method_type = method_type return result @@ -4594,8 +4694,8 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: options = self.chk.options if ( options.warn_redundant_casts - and not isinstance(get_proper_type(target_type), AnyType) - and source_type == target_type + and not is_same_type(target_type, AnyType(TypeOfAny.special_form)) + and is_same_type(source_type, target_type) ): self.msg.redundant_cast(target_type, expr) if options.disallow_any_unimported and has_any_from_unimported_type(target_type): @@ -4605,6 +4705,10 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: ) return target_type + def visit_type_form_expr(self, expr: TypeFormExpr) -> Type: + typ = expr.type + return TypeType.make_normalized(typ, line=typ.line, column=typ.column, is_type_form=True) + def visit_assert_type_expr(self, expr: AssertTypeExpr) -> Type: source_type = self.accept( expr.expr, @@ -4912,7 +5016,7 @@ def apply_type_arguments_to_callable( tp.fallback, name="tuple", definition=tp.definition, - bound_args=tp.bound_args, + is_bound=tp.is_bound, ) self.msg.incompatible_type_application( min_arg_count, len(type_vars), len(args), ctx @@ -4959,31 +5063,59 @@ def fast_container_type( module-level constant definitions. Limitations: + - no active type context + - at least one item - no star expressions - - the joined type of all entries must be an Instance or Tuple type + - not after deferral + - either exactly one distinct type inside, + or the joined type of all entries is an Instance or Tuple type, """ ctx = self.type_context[-1] - if ctx: + if ctx or not e.items: + return None + if self.chk.current_node_deferred: + # Guarantees that all items will be Any, we'll reject it anyway. return None rt = self.resolved_type.get(e, None) if rt is not None: return rt if isinstance(rt, Instance) else None values: list[Type] = [] + # Preserve join order while avoiding O(n) lookups at every iteration + values_set: set[Type] = set() for item in e.items: if isinstance(item, StarExpr): # fallback to slow path self.resolved_type[e] = NoneType() return None - values.append(self.accept(item)) - vt = join.join_type_list(values) - if not allow_fast_container_literal(vt): + + typ = self.accept(item) + if typ not in values_set: + values.append(typ) + values_set.add(typ) + + vt = self._first_or_join_fast_item(values) + if vt is None: self.resolved_type[e] = NoneType() return None ct = self.chk.named_generic_type(container_fullname, [vt]) - self.resolved_type[e] = ct + if not self.in_lambda_expr: + # We cannot cache results in lambdas - their bodies can be accepted in + # error-suppressing watchers too early + self.resolved_type[e] = ct return ct + def _first_or_join_fast_item(self, items: list[Type]) -> Type | None: + if len(items) == 1 and not self.chk.current_node_deferred: + return items[0] + typ = join.join_type_list(items) + if not allow_fast_container_literal(typ): + # TODO: This is overly strict, many other types can be joined safely here. + # However, our join implementation isn't bug-free, and some joins may produce + # undesired `Any`s or even more surprising results. + return None + return typ + def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: str) -> Type: # fast path t = self.fast_container_type(e, fullname) @@ -5144,18 +5276,30 @@ def fast_dict_type(self, e: DictExpr) -> Type | None: module-level constant definitions. Limitations: + - no active type context + - at least one item - only supported star expressions are other dict instances - - the joined types of all keys and values must be Instance or Tuple types + - either exactly one distinct type (keys and values separately) inside, + or the joined type of all entries is an Instance or Tuple type """ ctx = self.type_context[-1] - if ctx: + if ctx or not e.items: + return None + + if self.chk.current_node_deferred: + # Guarantees that all items will be Any, we'll reject it anyway. return None + rt = self.resolved_type.get(e, None) if rt is not None: return rt if isinstance(rt, Instance) else None + keys: list[Type] = [] values: list[Type] = [] + # Preserve join order while avoiding O(n) lookups at every iteration + keys_set: set[Type] = set() + values_set: set[Type] = set() stargs: tuple[Type, Type] | None = None for key, value in e.items: if key is None: @@ -5170,18 +5314,33 @@ def fast_dict_type(self, e: DictExpr) -> Type | None: self.resolved_type[e] = NoneType() return None else: - keys.append(self.accept(key)) - values.append(self.accept(value)) - kt = join.join_type_list(keys) - vt = join.join_type_list(values) - if not (allow_fast_container_literal(kt) and allow_fast_container_literal(vt)): + key_t = self.accept(key) + if key_t not in keys_set: + keys.append(key_t) + keys_set.add(key_t) + value_t = self.accept(value) + if value_t not in values_set: + values.append(value_t) + values_set.add(value_t) + + kt = self._first_or_join_fast_item(keys) + if kt is None: self.resolved_type[e] = NoneType() return None + + vt = self._first_or_join_fast_item(values) + if vt is None: + self.resolved_type[e] = NoneType() + return None + if stargs and (stargs[0] != kt or stargs[1] != vt): self.resolved_type[e] = NoneType() return None dt = self.chk.named_generic_type("builtins.dict", [kt, vt]) - self.resolved_type[e] = dt + if not self.in_lambda_expr: + # We cannot cache results in lambdas - their bodies can be accepted in + # error-suppressing watchers too early + self.resolved_type[e] = dt return dt def check_typeddict_literal_in_context( @@ -5204,20 +5363,21 @@ def visit_dict_expr(self, e: DictExpr) -> Type: # an error, but returns the TypedDict type that matches the literal it found # that would cause a second error when that TypedDict type is returned upstream # to avoid the second error, we always return TypedDict type that was requested - typeddict_contexts = self.find_typeddict_context(self.type_context[-1], e) + typeddict_contexts, exhaustive = self.find_typeddict_context(self.type_context[-1], e) if typeddict_contexts: - if len(typeddict_contexts) == 1: + if len(typeddict_contexts) == 1 and exhaustive: return self.check_typeddict_literal_in_context(e, typeddict_contexts[0]) # Multiple items union, check if at least one of them matches cleanly. for typeddict_context in typeddict_contexts: - with self.msg.filter_errors() as err, self.chk.local_type_map() as tmap: + with self.msg.filter_errors() as err, self.chk.local_type_map as tmap: ret_type = self.check_typeddict_literal_in_context(e, typeddict_context) if err.has_new_errors(): continue self.chk.store_types(tmap) return ret_type # No item matched without an error, so we can't unambiguously choose the item. - self.msg.typeddict_context_ambiguous(typeddict_contexts, e) + if exhaustive: + self.msg.typeddict_context_ambiguous(typeddict_contexts, e) # fast path attempt dt = self.fast_dict_type(e) @@ -5279,36 +5439,46 @@ def visit_dict_expr(self, e: DictExpr) -> Type: def find_typeddict_context( self, context: Type | None, dict_expr: DictExpr - ) -> list[TypedDictType]: + ) -> tuple[list[TypedDictType], bool]: + """Extract `TypedDict` members of the enclosing context. + + Returns: + a 2-tuple, (found_candidates, is_exhaustive) + """ context = get_proper_type(context) if isinstance(context, TypedDictType): - return [context] + return [context], True elif isinstance(context, UnionType): items = [] + exhaustive = True for item in context.items: - item_contexts = self.find_typeddict_context(item, dict_expr) + item_contexts, item_exhaustive = self.find_typeddict_context(item, dict_expr) for item_context in item_contexts: if self.match_typeddict_call_with_dict( item_context, dict_expr.items, dict_expr ): items.append(item_context) - return items + exhaustive = exhaustive and item_exhaustive + return items, exhaustive # No TypedDict type in context. - return [] + return [], False def visit_lambda_expr(self, e: LambdaExpr) -> Type: """Type check lambda expression.""" + old_in_lambda = self.in_lambda_expr + self.in_lambda_expr = True self.chk.check_default_args(e, body_is_trivial=False) inferred_type, type_override = self.infer_lambda_type_using_context(e) if not inferred_type: self.chk.return_types.append(AnyType(TypeOfAny.special_form)) # Type check everything in the body except for the final return # statement (it can contain tuple unpacking before return). - with self.chk.binder.frame_context( - can_skip=True, fall_through=0 - ), self.chk.scope.push_function(e): + with ( + self.chk.binder.frame_context(can_skip=True, fall_through=0), + self.chk.scope.push_function(e), + ): # Lambdas can have more than one element in body, - # when we add "fictional" AssigmentStatement nodes, like in: + # when we add "fictional" AssignmentStatement nodes, like in: # `lambda (a, b): a` for stmt in e.body.body[:-1]: stmt.accept(self.chk) @@ -5317,6 +5487,7 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: ret_type = self.accept(e.expr(), allow_none_return=True) fallback = self.named_type("builtins.function") self.chk.return_types.pop() + self.in_lambda_expr = old_in_lambda return callable_type(e, fallback, ret_type) else: # Type context available. @@ -5329,6 +5500,7 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: self.accept(e.expr(), allow_none_return=True) ret_type = self.chk.lookup_type(e.expr()) self.chk.return_types.pop() + self.in_lambda_expr = old_in_lambda return replace_callable_return_type(inferred_type, ret_type) def infer_lambda_type_using_context( @@ -5439,7 +5611,7 @@ def visit_super_expr(self, e: SuperExpr) -> Type: if type_info in mro: index = mro.index(type_info) else: - method = self.chk.scope.top_function() + method = self.chk.scope.current_function() # Mypy explicitly allows supertype upper bounds (and no upper bound at all) # for annotating self-types. However, if such an annotation is used for # checking super() we will still get an error. So to be consistent, we also @@ -5485,7 +5657,6 @@ def visit_super_expr(self, e: SuperExpr) -> Type: original_type=instance_type, override_info=base, context=e, - msg=self.msg, chk=self.chk, in_literal_context=self.is_literal_context(), ) @@ -5514,7 +5685,7 @@ def _super_arg_types(self, e: SuperExpr) -> Type | tuple[Type, Type]: type_type: ProperType = TypeType(current_type) # Use the type of the self argument, in case it was annotated - method = self.chk.scope.top_function() + method = self.chk.scope.current_function() assert method is not None if method.arguments: instance_type: Type = method.arguments[0].variable.type or current_type @@ -5580,11 +5751,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( @@ -5699,6 +5874,14 @@ def check_for_comp(self, e: GeneratorExpr | DictionaryComprehension) -> None: _, sequence_type = self.chk.analyze_async_iterable_item_type(sequence) else: _, sequence_type = self.chk.analyze_iterable_item_type(sequence) + if ( + isinstance(get_proper_type(sequence_type), UninhabitedType) + and isinstance(index, NameExpr) + and index.name == "_" + ): + # To preserve backward compatibility, avoid inferring Never for "_" + sequence_type = AnyType(TypeOfAny.special_form) + self.chk.analyze_index_variables(index, sequence_type, True, e) for condition in conditions: self.accept(condition) @@ -5742,7 +5925,7 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F else_map, e.else_expr, context=ctx, allow_none_return=allow_none_return ) - if not mypy.checker.is_valid_inferred_type(if_type): + if not mypy.checker.is_valid_inferred_type(if_type, self.chk.options): # Analyze the right branch disregarding the left branch. else_type = full_context_else_type # we want to keep the narrowest value of else_type for union'ing the branches @@ -5779,6 +5962,12 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F context=if_type_fallback, allow_none_return=allow_none_return, ) + + # 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 @@ -5796,16 +5985,33 @@ def analyze_cond_branch( node: Expression, context: Type | None, allow_none_return: bool = False, + suppress_unreachable_errors: bool = True, ) -> Type: with self.chk.binder.frame_context(can_skip=True, fall_through=0): if map is None: # We still need to type check node, in case we want to - # process it for isinstance checks later - self.accept(node, type_context=context, allow_none_return=allow_none_return) + # process it for isinstance checks later. Since the branch was + # determined to be unreachable, any errors should be suppressed. + with self.msg.filter_errors(filter_errors=suppress_unreachable_errors): + self.accept(node, type_context=context, allow_none_return=allow_none_return) return UninhabitedType() self.chk.push_type_map(map) return self.accept(node, type_context=context, allow_none_return=allow_none_return) + def _combined_context(self, ty: Type | None) -> Type | None: + ctx_items = [] + if ty is not None: + if has_any_type(ty): + # HACK: Any should be contagious, `dict[str, Any] or ` should still + # infer Any in x. + return ty + ctx_items.append(ty) + if self.type_context and self.type_context[-1] is not None: + ctx_items.append(self.type_context[-1]) + if ctx_items: + return make_simplified_union(ctx_items) + return None + # # Helpers # @@ -5835,6 +6041,7 @@ def accept( old_is_callee = self.is_callee self.is_callee = is_callee try: + p_type_context = get_proper_type(type_context) if allow_none_return and isinstance(node, CallExpr): typ = self.visit_call_expr(node, allow_none_return=True) elif allow_none_return and isinstance(node, YieldFromExpr): @@ -5843,8 +6050,59 @@ def accept( typ = self.visit_conditional_expr(node, allow_none_return=True) elif allow_none_return and isinstance(node, AwaitExpr): typ = self.visit_await_expr(node, allow_none_return=True) + + elif ( + isinstance(p_type_context, TypeType) + and p_type_context.is_type_form + and (node_as_type := self.try_parse_as_type_expression(node)) is not None + ): + typ = TypeType.make_normalized( + node_as_type, + line=node_as_type.line, + column=node_as_type.column, + is_type_form=True, + ) # r-value type, when interpreted as a type expression + elif ( + isinstance(p_type_context, UnionType) + and any( + isinstance(p_item := get_proper_type(item), TypeType) and p_item.is_type_form + for item in p_type_context.items + ) + and (node_as_type := self.try_parse_as_type_expression(node)) is not None + ): + typ1 = TypeType.make_normalized( + node_as_type, + line=node_as_type.line, + column=node_as_type.column, + is_type_form=True, + ) + if is_subtype(typ1, p_type_context): + typ = typ1 # r-value type, when interpreted as a type expression + else: + typ2 = node.accept(self) + typ = typ2 # r-value type, when interpreted as a value expression + # Deeply nested generic calls can deteriorate performance dramatically. + # Although in most cases caching makes little difference, in worst case + # it avoids exponential complexity. + # We cannot use cache inside lambdas, because they skip immediate type + # context, and use enclosing one, see infer_lambda_type_using_context(). + # TODO: consider using cache for more expression kinds. + elif ( + isinstance(node, (CallExpr, ListExpr, TupleExpr, DictExpr, OpExpr)) + and not (self.in_lambda_expr or self.chk.current_node_deferred) + and not self.chk.options.disable_expression_cache + ): + if (node, type_context) in self.expr_cache: + binder_version, typ, messages, type_map = self.expr_cache[(node, type_context)] + if binder_version == self.chk.binder.version: + self.chk.store_types(type_map) + self.msg.add_errors(messages) + else: + typ = self.accept_maybe_cache(node, type_context=type_context) + else: + typ = self.accept_maybe_cache(node, type_context=type_context) else: - typ = node.accept(self) + typ = node.accept(self) # r-value type, when interpreted as a value expression except Exception as err: report_internal_error( err, self.chk.errors.file, node.line, self.chk.errors, self.chk.options @@ -5873,6 +6131,18 @@ def accept( self.in_expression = False return result + def accept_maybe_cache(self, node: Expression, type_context: Type | None = None) -> Type: + binder_version = self.chk.binder.version + with self.msg.filter_errors(filter_errors=True, save_filtered_errors=True) as msg: + with self.chk.local_type_map as type_map: + typ = node.accept(self) + messages = msg.filtered_errors() + if binder_version == self.chk.binder.version and not self.chk.current_node_deferred: + self.expr_cache[(node, type_context)] = (binder_version, typ, messages, type_map) + self.chk.store_types(type_map) + self.msg.add_errors(messages) + return typ + def named_type(self, name: str) -> Instance: """Return an instance type with type given by the name and no type arguments. Alias for TypeChecker.named_type. @@ -5911,45 +6181,6 @@ def is_valid_keyword_var_arg(self, typ: Type) -> bool: or isinstance(typ, ParamSpecType) ) - def has_member(self, typ: Type, member: str) -> bool: - """Does type have member with the given name?""" - # TODO: refactor this to use checkmember.analyze_member_access, otherwise - # these two should be carefully kept in sync. - # This is much faster than analyze_member_access, though, and so using - # it first as a filter is important for performance. - typ = get_proper_type(typ) - - if isinstance(typ, TypeVarType): - typ = get_proper_type(typ.upper_bound) - if isinstance(typ, TupleType): - typ = tuple_fallback(typ) - if isinstance(typ, LiteralType): - typ = typ.fallback - if isinstance(typ, Instance): - return typ.type.has_readable_member(member) - if isinstance(typ, FunctionLike) and typ.is_type_obj(): - return typ.fallback.type.has_readable_member(member) - elif isinstance(typ, AnyType): - return True - elif isinstance(typ, UnionType): - result = all(self.has_member(x, member) for x in typ.relevant_items()) - return result - elif isinstance(typ, TypeType): - # Type[Union[X, ...]] is always normalized to Union[Type[X], ...], - # so we don't need to care about unions here. - item = typ.item - if isinstance(item, TypeVarType): - item = get_proper_type(item.upper_bound) - if isinstance(item, TupleType): - item = tuple_fallback(item) - if isinstance(item, Instance) and item.type.metaclass_type is not None: - return self.has_member(item.type.metaclass_type, member) - if isinstance(item, AnyType): - return True - return False - else: - return False - def not_ready_callback(self, name: str, context: Context) -> None: """Called when we can't infer the type of a variable because it's not ready yet. @@ -6041,6 +6272,7 @@ def visit_yield_from_expr(self, e: YieldFromExpr, allow_none_return: bool = Fals generic_generator_type = self.chk.named_generic_type( "typing.Generator", [any_type, any_type, any_type] ) + generic_generator_type.set_line(e) iter_type, _ = self.check_method_call_by_name( "__iter__", subexpr_type, [], [], context=generic_generator_type ) @@ -6086,7 +6318,7 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> Type: ): if not is_subtype(p_default, e.upper_bound): self.chk.fail("TypeVar default must be a subtype of the bound type", e) - if e.values and not any(p_default == value for value in e.values): + if e.values and not any(is_same_type(p_default, value) for value in e.values): self.chk.fail("TypeVar default must be one of the constraint types", e) return AnyType(TypeOfAny.special_form) @@ -6173,7 +6405,13 @@ def narrow_type_from_binder( known_type, restriction, prohibit_none_typevar_overlap=True ): return None - return narrow_declared_type(known_type, restriction) + narrowed = narrow_declared_type(known_type, restriction) + if isinstance(get_proper_type(narrowed), UninhabitedType): + # If we hit this case, it means that we can't reliably mark the code as + # unreachable, but the resulting type can't be expressed in type system. + # Falling back to restriction is more intuitive in most cases. + return restriction + return narrowed return known_type def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool: @@ -6196,6 +6434,77 @@ def has_abstract_type(self, caller_type: ProperType, callee_type: ProperType) -> and not self.chk.allow_abstract_call ) + def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> Type | None: + """Try to parse a value Expression as a type expression. + If success then return the type that it spells. + If fails then return None. + + A value expression that is parsable as a type expression may be used + where a TypeForm is expected to represent the spelled type. + + Unlike SemanticAnalyzer.try_parse_as_type_expression() + (used in the earlier SemanticAnalyzer pass), this function can only + recognize type expressions which contain no string annotations.""" + if not isinstance(maybe_type_expr, MaybeTypeExpression): + return None + + # Check whether has already been parsed as a type expression + # by SemanticAnalyzer.try_parse_as_type_expression(), + # perhaps containing a string annotation + if ( + isinstance(maybe_type_expr, (StrExpr, IndexExpr, OpExpr)) + and maybe_type_expr.as_type != NotParsed.VALUE + ): + return maybe_type_expr.as_type + + # If is potentially a type expression containing a string annotation, + # don't try to parse it because there isn't enough information + # available to the TypeChecker pass to resolve string annotations + if has_str_expression(maybe_type_expr): + self.chk.fail( + "TypeForm containing a string annotation cannot be recognized here. " + "Surround with TypeForm(...) to recognize.", + maybe_type_expr, + code=codes.MAYBE_UNRECOGNIZED_STR_TYPEFORM, + ) + return None + + # Collect symbols targeted by NameExprs and MemberExprs, + # to be looked up by TypeAnalyser when binding the + # UnboundTypes corresponding to those expressions. + (name_exprs, member_exprs) = all_name_and_member_expressions(maybe_type_expr) + sym_for_name = {e.name: SymbolTableNode(UNBOUND_IMPORTED, e.node) for e in name_exprs} | { + e_name: SymbolTableNode(UNBOUND_IMPORTED, e.node) + for e in member_exprs + if (e_name := get_member_expr_fullname(e)) is not None + } + + chk_sem = mypy.checker.TypeCheckerAsSemanticAnalyzer(self.chk, sym_for_name) + tpan = TypeAnalyser( + chk_sem, + # NOTE: Will never need to lookup type vars in this scope because + # SemanticAnalyzer.try_parse_as_type_expression() will have + # already recognized any type var referenced in a NameExpr. + # String annotations (which may also reference type vars) + # can't be resolved in the TypeChecker pass anyway. + TypeVarLikeScope(), # empty scope + self.plugin, + self.chk.options, + self.chk.tree, + self.chk.is_typeshed_stub, + ) + + try: + typ1 = expr_to_unanalyzed_type( + maybe_type_expr, self.chk.options, self.chk.is_typeshed_stub + ) + typ2 = typ1.accept(tpan) + if chk_sem.did_fail: + return None + return typ2 + except TypeTranslationError: + return None + def has_any_type(t: Type, ignore_in_type_obj: bool = False) -> bool: """Whether t contains an Any type""" @@ -6221,7 +6530,7 @@ def visit_type_var(self, t: TypeVarType) -> bool: def visit_param_spec(self, t: ParamSpecType) -> bool: default = [t.default] if t.has_default() else [] - return self.query_types([t.upper_bound, *default]) + return self.query_types([t.upper_bound, *default, t.prefix]) def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: default = [t.default] if t.has_default() else [] @@ -6306,23 +6615,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: @@ -6591,3 +6884,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 8f99f96e2dd50..719b48b14e07c 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence, cast +from collections.abc import Sequence +from typing import Callable, TypeVar, cast -from mypy import meet, message_registry, subtypes +from mypy import message_registry, state +from mypy.checker_shared import TypeCheckerSharedApi from mypy.erasetype import erase_typevars from mypy.expandtype import ( expand_self_type, @@ -12,43 +14,44 @@ freshen_all_functions_type_vars, ) from mypy.maptype import map_instance_to_supertype +from mypy.meet import is_overlapping_types from mypy.messages import MessageBuilder from mypy.nodes import ( ARG_POS, ARG_STAR, ARG_STAR2, + EXCLUDED_ENUM_ATTRIBUTES, SYMBOL_FUNCBASE_TYPES, Context, Decorator, + Expression, FuncBase, FuncDef, IndexExpr, MypyFile, NameExpr, OverloadedFuncDef, - SymbolNode, SymbolTable, TempNode, TypeAlias, TypeInfo, - TypeVarExpr, + TypeVarLikeExpr, Var, is_final_node, ) from mypy.plugin import AttributeContext +from mypy.subtypes import is_subtype from mypy.typeops import ( bind_self, - class_callable, erase_to_bound, + freeze_all_type_vars, function_type, - get_type_vars, + get_all_type_vars, make_simplified_union, supported_self_type, tuple_fallback, - type_object_type_from_function, ) from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, DeletedType, @@ -68,15 +71,10 @@ TypeVarLikeType, TypeVarTupleType, TypeVarType, + UninhabitedType, UnionType, get_proper_type, ) -from mypy.typetraverser import TypeTraverserVisitor - -if TYPE_CHECKING: # import for forward declaration only - import mypy.checker - -from mypy import state class MemberContext: @@ -87,17 +85,20 @@ class MemberContext: def __init__( self, + *, is_lvalue: bool, is_super: bool, is_operator: bool, original_type: Type, context: Context, - msg: MessageBuilder, - chk: mypy.checker.TypeChecker, - self_type: Type | None, + chk: TypeCheckerSharedApi, + self_type: Type | None = None, module_symbol_table: SymbolTable | None = None, no_deferral: bool = False, is_self: bool = False, + rvalue: Expression | None = None, + suppress_errors: bool = False, + preserve_type_var_ids: bool = False, ) -> None: self.is_lvalue = is_lvalue self.is_super = is_super @@ -105,11 +106,19 @@ def __init__( self.original_type = original_type self.self_type = self_type or original_type self.context = context # Error context - self.msg = msg self.chk = chk + self.msg = chk.msg self.module_symbol_table = module_symbol_table self.no_deferral = no_deferral self.is_self = is_self + if rvalue is not None: + assert is_lvalue + self.rvalue = rvalue + self.suppress_errors = suppress_errors + # This attribute is only used to preserve old protocol member access logic. + # It is needed to avoid infinite recursion in cases involving self-referential + # generic methods, see find_member() for details. Do not use for other purposes! + self.preserve_type_var_ids = preserve_type_var_ids def named_type(self, name: str) -> Instance: return self.chk.named_type(name) @@ -117,28 +126,31 @@ def named_type(self, name: str) -> Instance: def not_ready_callback(self, name: str, context: Context) -> None: self.chk.handle_cannot_determine_type(name, context) + def fail(self, msg: str) -> None: + if not self.suppress_errors: + self.msg.fail(msg, self.context) + def copy_modified( self, *, - messages: MessageBuilder | None = None, self_type: Type | None = None, is_lvalue: bool | None = None, 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, + chk=self.chk, + self_type=self.self_type, + module_symbol_table=self.module_symbol_table, + no_deferral=self.no_deferral, + rvalue=self.rvalue, + suppress_errors=self.suppress_errors, + preserve_type_var_ids=self.preserve_type_var_ids, ) - if messages is not None: - mx.msg = messages if self_type is not None: mx.self_type = self_type if is_lvalue is not None: @@ -152,19 +164,20 @@ 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, + chk: TypeCheckerSharedApi, override_info: TypeInfo | None = None, in_literal_context: bool = False, self_type: Type | None = None, module_symbol_table: SymbolTable | None = None, no_deferral: bool = False, is_self: bool = False, + rvalue: Expression | None = None, + suppress_errors: bool = False, ) -> Type: """Return the type of attribute 'name' of 'typ'. @@ -183,24 +196,33 @@ def analyze_member_access( of 'original_type'. 'original_type' is always preserved as the 'typ' type used in the initial, non-recursive call. The 'self_type' is a component of 'original_type' to which generic self should be bound (a narrower type that has a fallback to instance). - Currently this is used only for union types. + Currently, this is used only for union types. - 'module_symbol_table' is passed to this function if 'typ' is actually a module + 'module_symbol_table' is passed to this function if 'typ' is actually a module, and we want to keep track of the available attributes of the module (since they are not available via the type object directly) + + 'rvalue' can be provided optionally to infer better setter type when is_lvalue is True, + most notably this helps for descriptors with overloaded __set__() method. + + 'suppress_errors' will skip any logic that is only needed to generate error messages. + Note that this more of a performance optimization, one should not rely on this to not + show any messages, as some may be show e.g. by callbacks called here, + use msg.filter_errors(), if needed. """ 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, chk=chk, self_type=self_type, module_symbol_table=module_symbol_table, no_deferral=no_deferral, is_self=is_self, + rvalue=rvalue, + suppress_errors=suppress_errors, ) result = _analyze_member_access(name, typ, mx, override_info) possible_literal = get_proper_type(result) @@ -217,8 +239,6 @@ def analyze_member_access( def _analyze_member_access( name: str, typ: Type, mx: MemberContext, override_info: TypeInfo | None = None ) -> Type: - # TODO: This and following functions share some logic with subtypes.find_member; - # consider refactoring. typ = get_proper_type(typ) if isinstance(typ, Instance): return analyze_instance_member_access(name, typ, mx, override_info) @@ -248,8 +268,13 @@ def _analyze_member_access( ) return _analyze_member_access(name, typ.upper_bound, mx, override_info) elif isinstance(typ, DeletedType): - mx.msg.deleted_as_rvalue(typ, mx.context) + if not mx.suppress_errors: + mx.msg.deleted_as_rvalue(typ, mx.context) return AnyType(TypeOfAny.from_error) + elif isinstance(typ, UninhabitedType): + attr_type = UninhabitedType() + attr_type.ambiguous = typ.ambiguous + return attr_type return report_missing_attribute(mx.original_type, typ, name, mx) @@ -264,7 +289,9 @@ def may_be_awaitable_attribute( aw_type = mx.chk.get_precise_awaitable_type(typ, local_errors) if aw_type is None: return False - _ = _analyze_member_access(name, aw_type, mx, override_info) + _ = _analyze_member_access( + name, aw_type, mx.copy_modified(self_type=aw_type), override_info + ) return not local_errors.has_new_errors() @@ -275,6 +302,8 @@ def report_missing_attribute( mx: MemberContext, override_info: TypeInfo | None = None, ) -> Type: + if mx.suppress_errors: + return AnyType(TypeOfAny.from_error) error_code = mx.msg.has_no_attr(original_type, typ, name, mx.context, mx.module_symbol_table) if not mx.msg.prefer_simple_messages(): if may_be_awaitable_attribute(name, typ, mx, override_info): @@ -289,37 +318,43 @@ def report_missing_attribute( def analyze_instance_member_access( name: str, typ: Instance, mx: MemberContext, override_info: TypeInfo | None ) -> Type: - if name == "__init__" and not mx.is_super: - # Accessing __init__ in statically typed code would compromise - # type safety unless used via super(). - mx.msg.fail(message_registry.CANNOT_ACCESS_INIT, mx.context) - return AnyType(TypeOfAny.from_error) - - # The base object has an instance type. - info = typ.type if override_info: info = override_info + method = info.get_method(name) + + if name == "__init__" and not mx.is_super and not info.is_final: + if not method or not method.is_final: + # Accessing __init__ in statically typed code would compromise + # type safety unless used via super() or the method/class is final. + mx.fail(message_registry.CANNOT_ACCESS_INIT) + return AnyType(TypeOfAny.from_error) + + # The base object has an instance type. + if ( state.find_occurrences and info.name == state.find_occurrences[0] and name == state.find_occurrences[1] + and not mx.suppress_errors ): mx.msg.note("Occurrence of '{}.{}'".format(*state.find_occurrences), mx.context) # Look up the member. First look up the method dictionary. - method = info.get_method(name) if method and not isinstance(method, Decorator): - if mx.is_super: + if mx.is_super and not mx.suppress_errors: validate_super_call(method, mx) 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) - if mx.is_lvalue: + getter = method.items[0] + assert isinstance(getter, Decorator) + if mx.is_lvalue and getter.var.is_settable_property: + mx.chk.warn_deprecated(method.setter, mx.context) + return analyze_var(name, getter.var, typ, mx) + + if mx.is_lvalue and not mx.suppress_errors: mx.msg.cant_assign_to_method(mx.context) if not isinstance(method, OverloadedFuncDef): signature = function_type(method, mx.named_type("builtins.function")) @@ -332,17 +367,16 @@ def analyze_instance_member_access( return AnyType(TypeOfAny.special_form) assert isinstance(method.type, Overloaded) signature = method.type - signature = freshen_all_functions_type_vars(signature) + if not mx.preserve_type_var_ids: + signature = freshen_all_functions_type_vars(signature) if not method.is_static: - # TODO: use proper treatment of special methods on unions instead - # of this hack here and below (i.e. mx.self_type). - dispatched_type = meet.meet_types(mx.original_type, typ) - signature = check_self_arg( - signature, dispatched_type, method.is_class, mx.context, name, mx.msg - ) - signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class) - # TODO: should we skip these steps for static methods as well? - # Since generic static methods should not be allowed. + if isinstance(method, (FuncDef, OverloadedFuncDef)) and method.is_trivial_self: + signature = bind_self_fast(signature, mx.self_type) + else: + signature = check_self_arg( + signature, mx.self_type, method.is_class, mx.context, name, mx.msg + ) + signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class) typ = map_instance_to_supertype(typ, method.info) member_type = expand_type_by_instance(signature, typ) freeze_all_type_vars(member_type) @@ -356,11 +390,13 @@ def validate_super_call(node: FuncBase, mx: MemberContext) -> None: unsafe_super = False if isinstance(node, FuncDef) and node.is_trivial_body: unsafe_super = True - impl = node elif isinstance(node, OverloadedFuncDef): if node.impl: impl = node.impl if isinstance(node.impl, FuncDef) else node.impl.func unsafe_super = impl.is_trivial_body + elif not node.is_property and node.items: + assert isinstance(node.items[0], Decorator) + unsafe_super = node.items[0].func.is_trivial_body if unsafe_super: mx.msg.unsafe_super(node.name, node.info.name, mx.context) @@ -374,6 +410,8 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member ret_type = tuple_fallback(ret_type) if isinstance(ret_type, TypedDictType): ret_type = ret_type.fallback + if isinstance(ret_type, LiteralType): + ret_type = ret_type.fallback if isinstance(ret_type, Instance): if not mx.is_operator: # When Python sees an operator (eg `3 == 4`), it automatically translates that @@ -491,32 +529,35 @@ def analyze_member_var_access( original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. - v = lookup_member_var_or_accessor(info, name, mx.is_lvalue) + node = info.get(name) + v = node.node if node else None + + mx.chk.warn_deprecated(v, mx.context) vv = v + is_trivial_self = False if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var - if mx.is_super: + is_trivial_self = vv.func.is_trivial_self and not vv.decorators + if mx.is_super and not mx.suppress_errors: validate_super_call(vv.func, mx) + if isinstance(v, FuncDef): + assert False, "Did not expect a function" + if isinstance(v, MypyFile): + # Special case: accessing module on instances is allowed, but will not + # be recorded by semantic analyzer. + mx.chk.module_refs.add(v.fullname) - if isinstance(vv, TypeInfo): + if isinstance(vv, (TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)): # If the associated variable is a TypeInfo synthesize a Var node for # the purposes of type checking. This enables us to type check things - # like accessing class attributes on an inner class. - v = Var(name, type=type_object_type(vv, mx.named_type)) - v.info = info - - if isinstance(vv, TypeAlias): - # Similar to the above TypeInfo case, we allow using - # qualified type aliases in runtime context if it refers to an - # instance type. For example: + # like accessing class attributes on an inner class. Similar we allow + # using qualified type aliases in runtime context. For example: # class C: # A = List[int] # x = C.A() <- this is OK - typ = mx.chk.expr_checker.alias_type_in_runtime_context( - vv, ctx=mx.context, alias_definition=mx.is_lvalue - ) + typ = mx.chk.expr_checker.analyze_static_reference(vv, mx.context, mx.is_lvalue) v = Var(name, type=typ) v.info = info @@ -528,12 +569,7 @@ def analyze_member_var_access( if mx.is_lvalue and not mx.chk.get_final_context(): check_final_member(name, info, mx.msg, mx.context) - return analyze_var(name, v, itype, info, mx, implicit=implicit) - elif isinstance(v, FuncDef): - assert False, "Did not expect a function" - elif isinstance(v, MypyFile): - mx.chk.module_refs.add(v.fullname) - return mx.chk.expr_checker.module_type(v) + return analyze_var(name, v, itype, mx, implicit=implicit, is_trivial_self=is_trivial_self) elif ( not v and name not in ["__getattr__", "__setattr__", "__getattribute__"] @@ -552,12 +588,7 @@ def analyze_member_var_access( # that the attribute exists if method and method.info.fullname != "builtins.object": bound_method = analyze_decorator_or_funcbase_access( - defn=method, - itype=itype, - info=info, - self_type=mx.self_type, - name=method_name, - mx=mx, + defn=method, itype=itype, name=method_name, mx=mx ) typ = map_instance_to_supertype(itype, method.info) getattr_type = get_proper_type(expand_type_by_instance(bound_method, typ)) @@ -572,7 +603,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 @@ -582,9 +617,7 @@ def analyze_member_var_access( bound_type = analyze_decorator_or_funcbase_access( defn=setattr_meth, itype=itype, - info=info, - self_type=mx.self_type, - name=name, + name="__setattr__", mx=mx.copy_modified(is_lvalue=False), ) typ = map_instance_to_supertype(itype, setattr_meth.info) @@ -601,7 +634,7 @@ def analyze_member_var_access( if not itype.extra_attrs.mod_name: return itype.extra_attrs.attrs[name] - if mx.is_super: + if mx.is_super and not mx.suppress_errors: mx.msg.undefined_in_superclass(name, mx.context) return AnyType(TypeOfAny.from_error) else: @@ -636,7 +669,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: (the type of ``f`` in ``a.f`` when ``f`` is a descriptor). mx: The current member access context. Return: - The return type of the appropriate ``__get__`` overload for the descriptor. + The return type of the appropriate ``__get__/__set__`` overload for the descriptor. """ instance_type = get_proper_type(mx.self_type) orig_descriptor_type = descriptor_type @@ -650,26 +683,35 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: elif not isinstance(descriptor_type, Instance): return orig_descriptor_type - if not descriptor_type.type.has_readable_member("__get__"): + if not mx.is_lvalue and not descriptor_type.type.has_readable_member("__get__"): + return orig_descriptor_type + + # We do this check first to accommodate for descriptors with only __set__ method. + # 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, + # (which allow you to override the descriptor with any value), but preserves + # the type of accessing the attribute (even after the override). + if mx.is_lvalue and descriptor_type.type.has_readable_member("__set__"): + return analyze_descriptor_assign(descriptor_type, mx) + + if mx.is_lvalue and not descriptor_type.type.has_readable_member("__get__"): + # This turned out to be not a descriptor after all. return orig_descriptor_type dunder_get = descriptor_type.type.get_method("__get__") if dunder_get is None: - mx.msg.fail( + mx.fail( message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( descriptor_type.str_with_options(mx.msg.options) - ), - mx.context, + ) ) return AnyType(TypeOfAny.from_error) bound_method = analyze_decorator_or_funcbase_access( defn=dunder_get, itype=descriptor_type, - info=descriptor_type.type, - self_type=descriptor_type, name="__get__", - mx=mx, + mx=mx.copy_modified(self_type=descriptor_type), ) typ = map_instance_to_supertype(descriptor_type, dunder_get.info) @@ -709,23 +751,94 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: callable_name=callable_name, ) + # Search for possible deprecations: + mx.chk.warn_deprecated(dunder_get, mx.context) + 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 return inferred_dunder_get_type if not isinstance(inferred_dunder_get_type, CallableType): - mx.msg.fail( + mx.fail( message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( descriptor_type.str_with_options(mx.msg.options) - ), - mx.context, + ) ) return AnyType(TypeOfAny.from_error) return inferred_dunder_get_type.ret_type +def analyze_descriptor_assign(descriptor_type: Instance, mx: MemberContext) -> Type: + instance_type = get_proper_type(mx.self_type) + dunder_set = descriptor_type.type.get_method("__set__") + if dunder_set is None: + mx.fail( + message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format( + descriptor_type.str_with_options(mx.msg.options) + ).value + ) + return AnyType(TypeOfAny.from_error) + + bound_method = analyze_decorator_or_funcbase_access( + defn=dunder_set, + itype=descriptor_type, + name="__set__", + mx=mx.copy_modified(is_lvalue=False, self_type=descriptor_type), + ) + typ = map_instance_to_supertype(descriptor_type, dunder_set.info) + dunder_set_type = expand_type_by_instance(bound_method, typ) + + callable_name = mx.chk.expr_checker.method_fullname(descriptor_type, "__set__") + rvalue = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context) + dunder_set_type = mx.chk.expr_checker.transform_callee_type( + callable_name, + dunder_set_type, + [TempNode(instance_type, context=mx.context), rvalue], + [ARG_POS, ARG_POS], + mx.context, + object_type=descriptor_type, + ) + + # For non-overloaded setters, the result should be type-checked like a regular assignment. + # Hence, we first only try to infer the type by using the rvalue as type context. + type_context = rvalue + with mx.msg.filter_errors(): + _, inferred_dunder_set_type = mx.chk.expr_checker.check_call( + dunder_set_type, + [TempNode(instance_type, context=mx.context), type_context], + [ARG_POS, ARG_POS], + mx.context, + object_type=descriptor_type, + callable_name=callable_name, + ) + + # And now we in fact type check the call, to show errors related to wrong arguments + # count, etc., replacing the type context for non-overloaded setters only. + inferred_dunder_set_type = get_proper_type(inferred_dunder_set_type) + if isinstance(inferred_dunder_set_type, CallableType): + type_context = TempNode(AnyType(TypeOfAny.special_form), context=mx.context) + mx.chk.expr_checker.check_call( + dunder_set_type, + [TempNode(instance_type, context=mx.context), type_context], + [ARG_POS, ARG_POS], + mx.context, + object_type=descriptor_type, + callable_name=callable_name, + ) + + # Search for possible deprecations: + mx.chk.warn_deprecated(dunder_set, mx.context) + + # 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 + ): + return AnyType(TypeOfAny.from_error) + return inferred_dunder_set_type.arg_types[1] + + def is_instance_var(var: Var) -> bool: """Return if var is an instance variable according to PEP 526.""" return ( @@ -742,10 +855,10 @@ def analyze_var( name: str, var: Var, itype: Instance, - info: TypeInfo, mx: MemberContext, *, implicit: bool = False, + is_trivial_self: bool = False, ) -> Type: """Analyze access to an attribute via a Var node. @@ -753,71 +866,58 @@ def analyze_var( itype is the instance type in which attribute should be looked up original_type is the type of E in the expression E.var if implicit is True, the original Var was created as an assignment to self + if is_trivial_self is True, we can use fast path for bind_self(). """ # Found a member variable. original_itype = itype itype = map_instance_to_supertype(itype, var.info) - typ = var.type + if var.is_settable_property and mx.is_lvalue: + typ: Type | None = var.setter_type + if typ is None and var.is_ready: + # Existing synthetic properties may not set setter type. Fall back to getter. + typ = var.type + else: + typ = var.type if typ: if isinstance(typ, PartialType): return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context) - if mx.is_lvalue and var.is_property and not var.is_settable_property: - # TODO allow setting attributes in subclass (although it is probably an error) - mx.msg.read_only_property(name, itype.type, mx.context) - if mx.is_lvalue and var.is_classvar: - mx.msg.cant_assign_to_classvar(name, mx.context) - t = freshen_all_functions_type_vars(typ) - t = expand_self_type_if_needed(t, mx, var, original_itype) - t = expand_type_by_instance(t, itype) - freeze_all_type_vars(t) - result = t - typ = get_proper_type(typ) - + if mx.is_lvalue and not mx.suppress_errors: + if var.is_property and not var.is_settable_property: + mx.msg.read_only_property(name, itype.type, mx.context) + if var.is_classvar: + mx.msg.cant_assign_to_classvar(name, mx.context) + # This is the most common case for variables, so start with this. + result = expand_without_binding(typ, var, itype, original_itype, mx) + + # A non-None value indicates that we should actually bind self for this variable. call_type: ProperType | None = None if var.is_initialized_in_class and (not is_instance_var(var) or mx.is_operator): + typ = get_proper_type(typ) if isinstance(typ, FunctionLike) and not typ.is_type_obj(): call_type = typ elif var.is_property: - call_type = get_proper_type(_analyze_member_access("__call__", typ, mx)) + deco_mx = mx.copy_modified(original_type=typ, self_type=typ, is_lvalue=False) + call_type = get_proper_type(_analyze_member_access("__call__", typ, deco_mx)) else: call_type = typ + # Bound variables with callable types are treated like methods + # (these are usually method aliases like __rmul__ = __mul__). if isinstance(call_type, FunctionLike) and not call_type.is_type_obj(): - if mx.is_lvalue: - if var.is_property: - if not var.is_settable_property: - mx.msg.read_only_property(name, itype.type, mx.context) + if mx.is_lvalue and not var.is_property and not mx.suppress_errors: + mx.msg.cant_assign_to_method(mx.context) + + # Bind the self type for each callable component (when needed). + if call_type and not var.is_staticmethod: + bound_items = [] + for ct in call_type.items if isinstance(call_type, UnionType) else [call_type]: + p_ct = get_proper_type(ct) + if isinstance(p_ct, FunctionLike) and (not p_ct.bound() or var.is_property): + item = expand_and_bind_callable(p_ct, var, itype, name, mx, is_trivial_self) else: - mx.msg.cant_assign_to_method(mx.context) - - if not var.is_staticmethod: - # Class-level function objects and classmethods become bound methods: - # the former to the instance, the latter to the class. - functype: FunctionLike = call_type - # Use meet to narrow original_type to the dispatched type. - # For example, assume - # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) - # * B.f: Callable[[B1], None] where B1 <: B (maybe B1 == B) - # * x: Union[A1, B1] - # In `x.f`, when checking `x` against A1 we assume x is compatible with A - # and similarly for B1 when checking against B - dispatched_type = meet.meet_types(mx.original_type, itype) - signature = freshen_all_functions_type_vars(functype) - bound = get_proper_type(expand_self_type(var, signature, mx.original_type)) - assert isinstance(bound, FunctionLike) - signature = bound - signature = check_self_arg( - signature, dispatched_type, var.is_classmethod, mx.context, name, mx.msg - ) - signature = bind_self(signature, mx.self_type, var.is_classmethod) - expanded_signature = expand_type_by_instance(signature, itype) - freeze_all_type_vars(expanded_signature) - if var.is_property: - # A property cannot have an overloaded type => the cast is fine. - assert isinstance(expanded_signature, CallableType) - result = expanded_signature.ret_type - else: - result = expanded_signature + item = expand_without_binding(ct, var, itype, original_itype, mx) + bound_items.append(item) + result = UnionType.make_union(bound_items) else: if not var.is_ready and not mx.no_deferral: mx.not_ready_callback(var.name, mx.context) @@ -825,15 +925,87 @@ def analyze_var( result = AnyType(TypeOfAny.special_form) fullname = f"{var.info.fullname}.{name}" hook = mx.chk.plugin.get_attribute_hook(fullname) - if result and not mx.is_lvalue and not implicit: + + if var.info.is_enum and not mx.is_lvalue: + if name in var.info.enum_members and name not in {"name", "value"}: + enum_literal = LiteralType(name, fallback=itype) + result = itype.copy_modified(last_known_value=enum_literal) + elif ( + isinstance(p_result := get_proper_type(result), Instance) + and p_result.type.fullname == "enum.nonmember" + and p_result.args + ): + # Unwrap nonmember similar to class-level access + result = p_result.args[0] + if result and not (implicit or var.info.is_protocol and is_instance_var(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 +def expand_without_binding( + typ: Type, var: Var, itype: Instance, original_itype: Instance, mx: MemberContext +) -> Type: + if not mx.preserve_type_var_ids: + typ = freshen_all_functions_type_vars(typ) + typ = expand_self_type_if_needed(typ, mx, var, original_itype) + expanded = expand_type_by_instance(typ, itype) + freeze_all_type_vars(expanded) + return expanded + + +def expand_and_bind_callable( + functype: FunctionLike, + var: Var, + itype: Instance, + name: str, + mx: MemberContext, + is_trivial_self: bool, +) -> Type: + if not mx.preserve_type_var_ids: + functype = freshen_all_functions_type_vars(functype) + typ = get_proper_type(expand_self_type(var, functype, mx.self_type)) + assert isinstance(typ, FunctionLike) + if is_trivial_self: + typ = bind_self_fast(typ, mx.self_type) + else: + typ = check_self_arg(typ, mx.self_type, var.is_classmethod, mx.context, name, mx.msg) + typ = bind_self(typ, mx.self_type, var.is_classmethod) + expanded = expand_type_by_instance(typ, itype) + freeze_all_type_vars(expanded) + if not var.is_property: + return expanded + if isinstance(expanded, Overloaded): + # Legacy way to store settable properties is with overloads. Also in case it is + # an actual overloaded property, selecting first item that passed check_self_arg() + # is a good approximation, long-term we should use check_call() inference below. + if not expanded.items: + # A broken overload, error should be already reported. + return AnyType(TypeOfAny.from_error) + expanded = expanded.items[0] + assert isinstance(expanded, CallableType), expanded + if var.is_settable_property and mx.is_lvalue and var.setter_type is not None: + if expanded.variables: + type_ctx = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context) + _, inferred_expanded = mx.chk.expr_checker.check_call( + expanded, [type_ctx], [ARG_POS], mx.context + ) + expanded = get_proper_type(inferred_expanded) + assert isinstance(expanded, CallableType) + if not expanded.arg_types: + # This can happen when accessing invalid property from its own body, + # error will be reported elsewhere. + return AnyType(TypeOfAny.from_error) + return expanded.arg_types[0] + else: + return expanded.ret_type + + def expand_self_type_if_needed( t: Type, mx: MemberContext, var: Var, itype: Instance, is_class: bool = False ) -> Type: @@ -878,27 +1050,6 @@ def expand_self_type_if_needed( return t -def freeze_all_type_vars(member_type: Type) -> None: - member_type.accept(FreezeTypeVarsVisitor()) - - -class FreezeTypeVarsVisitor(TypeTraverserVisitor): - def visit_callable_type(self, t: CallableType) -> None: - for v in t.variables: - v.id.meta_level = 0 - super().visit_callable_type(t) - - -def lookup_member_var_or_accessor(info: TypeInfo, name: str, is_lvalue: bool) -> SymbolNode | None: - """Find the attribute/accessor node that refers to a member of a type.""" - # TODO handle lvalues - node = info.get(name) - if node: - return node.node - else: - return None - - def check_self_arg( functype: FunctionLike, dispatched_arg_type: Type, @@ -912,13 +1063,9 @@ def check_self_arg( For example if the method is defined as: class A: def f(self: S) -> T: ... - then for 'x.f' we check that meet(type(x), A) <: S. If the method is overloaded, we - select only overloads items that satisfy this requirement. If there are no matching + then for 'x.f' we check that type(x) <: S. If the method is overloaded, we select + only overloads items that satisfy this requirement. If there are no matching overloads, an error is generated. - - Note: dispatched_arg_type uses a meet to select a relevant item in case if the - original type of 'x' is a union. This is done because several special methods - treat union types in ad-hoc manner, so we can't use MemberContext.self_type yet. """ items = functype.items if not items: @@ -926,6 +1073,7 @@ def f(self: S) -> T: ... new_items = [] if is_classmethod: dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) + p_dispatched_arg_type = get_proper_type(dispatched_arg_type) for item in items: if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR): @@ -934,17 +1082,42 @@ def f(self: S) -> T: ... # This is pretty bad, so just return the original signature if # there is at least one such error. return functype - else: - selfarg = get_proper_type(item.arg_types[0]) + selfarg = get_proper_type(item.arg_types[0]) + if isinstance(selfarg, Instance) and isinstance(p_dispatched_arg_type, Instance): + if selfarg.type is p_dispatched_arg_type.type and selfarg.args: + if not is_overlapping_types(p_dispatched_arg_type, selfarg): + # This special casing is needed since `actual <: erased(template)` + # logic below doesn't always work, and a more correct approach may + # be tricky. + continue + new_items.append(item) + + if new_items: + items = new_items + new_items = [] + + for item in items: + selfarg = get_proper_type(item.arg_types[0]) + # This matches similar special-casing in bind_self(), see more details there. + self_callable = name == "__call__" and isinstance(selfarg, CallableType) + if self_callable or is_subtype( + dispatched_arg_type, # This level of erasure matches the one in checker.check_func_def(), # better keep these two checks consistent. - if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))): - new_items.append(item) - elif isinstance(selfarg, ParamSpecType): - # TODO: This is not always right. What's the most reasonable thing to do here? - new_items.append(item) - elif isinstance(selfarg, TypeVarTupleType): - raise NotImplementedError + erase_typevars(erase_to_bound(selfarg)), + # This is to work around the fact that erased ParamSpec and TypeVarTuple + # callables are not always compatible with non-erased ones both ways. + always_covariant=any( + not isinstance(tv, TypeVarType) for tv in get_all_type_vars(selfarg) + ), + ignore_pos_arg_names=True, + ): + new_items.append(item) + elif isinstance(selfarg, ParamSpecType): + # TODO: This is not always right. What's the most reasonable thing to do here? + new_items.append(item) + elif isinstance(selfarg, TypeVarTupleType): + raise NotImplementedError if not new_items: # Choose first item for the message (it may be not very helpful for overloads). msg.incompatible_self_argument( @@ -1004,24 +1177,24 @@ 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: + if mx.is_lvalue and not mx.suppress_errors: if is_method: mx.msg.cant_assign_to_method(mx.context) if isinstance(node.node, TypeInfo): - mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context) + mx.fail(message_registry.CANNOT_ASSIGN_TO_TYPE) # Refuse class attribute access if slot defined if info.slots and name in info.slots: - mx.msg.fail(message_registry.CLASS_VAR_CONFLICTS_SLOTS.format(name), mx.context) + mx.fail(message_registry.CLASS_VAR_CONFLICTS_SLOTS.format(name)) # If a final attribute was declared on `self` in `__init__`, then it # can't be accessed on the class object. if node.implicit and isinstance(node.node, Var) and node.node.is_final: - mx.msg.fail( - message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR.format(node.node.name), mx.context - ) + mx.fail(message_registry.CANNOT_ACCESS_FINAL_INSTANCE_ATTR.format(node.node.name)) # An assignment to final attribute on class object is also always an error, # independently of types. @@ -1062,43 +1235,72 @@ def analyze_class_attribute_access( if isinstance(node.node, Var): assert isuper is not None + object_type = get_proper_type(mx.self_type) # Check if original variable type has type variables. For example: # class C(Generic[T]): # x: T # C.x # Error, ambiguous access # C[int].x # Also an error, since C[int] is same as C at runtime # Exception is Self type wrapped in ClassVar, that is safe. + prohibit_self = not node.node.is_classvar def_vars = set(node.node.info.defn.type_vars) - if not node.node.is_classvar and node.node.info.self_type: + if prohibit_self and node.node.info.self_type: def_vars.add(node.node.info.self_type) - typ_vars = set(get_type_vars(t)) - if def_vars & typ_vars: - # Exception: access on Type[...], including first argument of class methods is OK. - if not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit: - if node.node.is_classvar: - message = message_registry.GENERIC_CLASS_VAR_ACCESS - else: - message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS - mx.msg.fail(message, mx.context) + # Exception: access on Type[...], including first argument of class methods is OK. + prohibit_generic = not isinstance(object_type, TypeType) or node.implicit + if prohibit_generic and def_vars & set(get_all_type_vars(t)): + if node.node.is_classvar: + message = message_registry.GENERIC_CLASS_VAR_ACCESS + else: + message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS + mx.fail(message) t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True) + t = expand_type_by_instance(t, isuper) # Erase non-mapped variables, but keep mapped ones, even if there is an error. # In the above example this means that we infer following types: # C.x -> Any # C[int].x -> int - t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars}) - - is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( - isinstance(node.node, FuncBase) and node.node.is_class - ) - is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or ( - isinstance(node.node, FuncBase) and node.node.is_static + if prohibit_generic: + erase_vars = set(itype.type.defn.type_vars) + if prohibit_self and itype.type.self_type: + erase_vars.add(itype.type.self_type) + t = erase_typevars(t, {tv.id for tv in erase_vars}) + + is_classmethod = ( + (is_decorated and cast(Decorator, node.node).func.is_class) + or (isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class) + or isinstance(node.node, Var) + and node.node.is_classmethod ) t = get_proper_type(t) - if isinstance(t, FunctionLike) and is_classmethod: + is_trivial_self = False + if isinstance(node.node, Decorator): + # Use fast path if there are trivial decorators like @classmethod or @property + is_trivial_self = node.node.func.is_trivial_self and not node.node.decorators + elif isinstance(node.node, (FuncDef, OverloadedFuncDef)): + is_trivial_self = node.node.is_trivial_self + if ( + isinstance(t, FunctionLike) + and is_classmethod + and not is_trivial_self + and not t.bound() + ): t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg) - result = add_class_tvars( - t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars + t = add_class_tvars( + t, + isuper, + is_classmethod, + mx, + original_vars=original_vars, + is_trivial_self=is_trivial_self, ) + if is_decorated: + t = expand_self_type_if_needed( + t, mx, cast(Decorator, node.node).var, itype, is_class=is_classmethod + ) + + result = t + # __set__ is not called on class objects. if not mx.is_lvalue: result = analyze_descriptor_access(result, mx) @@ -1107,23 +1309,9 @@ def analyze_class_attribute_access( mx.not_ready_callback(name, mx.context) return AnyType(TypeOfAny.special_form) - if isinstance(node.node, TypeVarExpr): - mx.msg.fail( - message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(info.name, name), mx.context - ) - return AnyType(TypeOfAny.from_error) - - if isinstance(node.node, TypeInfo): - return type_object_type(node.node, mx.named_type) - - if isinstance(node.node, MypyFile): - # Reference to a module object. - return mx.named_type("types.ModuleType") - - if isinstance(node.node, TypeAlias): - return mx.chk.expr_checker.alias_type_in_runtime_context( - node.node, ctx=mx.context, alias_definition=mx.is_lvalue - ) + if isinstance(node.node, (TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)): + # TODO: should we apply class plugin here (similar to instance access)? + return mx.chk.expr_checker.analyze_static_reference(node.node, mx.context, mx.is_lvalue) if is_decorated: assert isinstance(node.node, Decorator) @@ -1133,13 +1321,13 @@ def analyze_class_attribute_access( mx.not_ready_callback(name, mx.context) return AnyType(TypeOfAny.from_error) else: - assert isinstance(node.node, FuncBase) + assert isinstance(node.node, SYMBOL_FUNCBASE_TYPES) typ = function_type(node.node, mx.named_type("builtins.function")) # Note: if we are accessing class method on class object, the cls argument is bound. # Annotated and/or explicit class methods go through other code paths above, for # unannotated implicit class methods we do this here. if node.node.is_class: - typ = bind_self(typ, is_classmethod=True) + typ = bind_self_fast(typ) return apply_class_attr_hook(mx, hook, typ) @@ -1148,7 +1336,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 +1347,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("_", "") != "": @@ -1189,7 +1379,7 @@ def analyze_typeddict_access( typ, mx.context.index, setitem=True ) assigned_readonly_keys = typ.readonly_keys & key_names - if assigned_readonly_keys: + if assigned_readonly_keys and not mx.suppress_errors: mx.msg.readonly_keys_mutated(assigned_readonly_keys, context=mx.context) else: # It can also be `a.__setitem__(...)` direct call. @@ -1221,9 +1411,9 @@ def add_class_tvars( t: ProperType, isuper: Instance | None, is_classmethod: bool, - is_staticmethod: bool, - original_type: Type, + mx: MemberContext, original_vars: Sequence[TypeVarLikeType] | None = None, + is_trivial_self: bool = False, ) -> Type: """Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: @@ -1240,10 +1430,8 @@ class B(A[str]): pass isuper: Current instance mapped to the superclass where method was defined, this is usually done by map_instance_to_supertype() is_classmethod: True if this method is decorated with @classmethod - is_staticmethod: True if this method is decorated with @staticmethod - original_type: The value of the type B in the expression B.foo() or the corresponding - component in case of a union (this is used to bind the self-types) original_vars: Type variables of the class callable on which the method was accessed + is_trivial_self: if True, we can use fast path for bind_self(). Returns: Expanded method type with added type variables (when needed). """ @@ -1263,11 +1451,14 @@ class B(A[str]): pass # (i.e. appear in the return type of the class object on which the method was accessed). if isinstance(t, CallableType): tvars = original_vars if original_vars is not None else [] - t = freshen_all_functions_type_vars(t) - if is_classmethod: - t = bind_self(t, original_type, is_classmethod=True) - if is_classmethod or is_staticmethod: - assert isuper is not None + if not mx.preserve_type_var_ids: + t = freshen_all_functions_type_vars(t) + if is_classmethod and not t.is_bound: + if is_trivial_self: + t = bind_self_fast(t, mx.self_type) + else: + t = bind_self(t, mx.self_type, is_classmethod=True) + if isuper is not None: t = expand_type_by_instance(t, isuper) freeze_all_type_vars(t) return t.copy_modified(variables=list(tvars) + list(t.variables)) @@ -1276,14 +1467,7 @@ class B(A[str]): pass [ cast( CallableType, - add_class_tvars( - item, - isuper, - is_classmethod, - is_staticmethod, - original_type, - original_vars=original_vars, - ), + add_class_tvars(item, isuper, is_classmethod, mx, original_vars=original_vars), ) for item in t.items ] @@ -1293,84 +1477,8 @@ class B(A[str]): pass return t -def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: - """Return the type of a type object. - - For a generic type G with type variables T and S the type is generally of form - - Callable[..., G[T, S]] - - where ... are argument types for the __init__/__new__ method (without the self - argument). Also, the fallback type will be 'type' instead of 'function'. - """ - - # We take the type from whichever of __init__ and __new__ is first - # in the MRO, preferring __init__ if there is a tie. - init_method = info.get("__init__") - new_method = info.get("__new__") - if not init_method or not is_valid_constructor(init_method.node): - # Must be an invalid class definition. - return AnyType(TypeOfAny.from_error) - # There *should* always be a __new__ method except the test stubs - # lack it, so just copy init_method in that situation - new_method = new_method or init_method - if not is_valid_constructor(new_method.node): - # Must be an invalid class definition. - return AnyType(TypeOfAny.from_error) - - # The two is_valid_constructor() checks ensure this. - assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) - assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) - - init_index = info.mro.index(init_method.node.info) - new_index = info.mro.index(new_method.node.info) - - fallback = info.metaclass_type or named_type("builtins.type") - if init_index < new_index: - method: FuncBase | Decorator = init_method.node - is_new = False - elif init_index > new_index: - method = new_method.node - is_new = True - else: - if init_method.node.info.fullname == "builtins.object": - # Both are defined by object. But if we've got a bogus - # base class, we can't know for sure, so check for that. - if info.fallback_to_any: - # Construct a universal callable as the prototype. - any_type = AnyType(TypeOfAny.special_form) - sig = CallableType( - arg_types=[any_type, any_type], - arg_kinds=[ARG_STAR, ARG_STAR2], - arg_names=["_args", "_kwds"], - ret_type=any_type, - fallback=named_type("builtins.function"), - ) - return class_callable(sig, info, fallback, None, is_new=False) - - # Otherwise prefer __init__ in a tie. It isn't clear that this - # is the right thing, but __new__ caused problems with - # typeshed (#5647). - method = init_method.node - is_new = False - # Construct callable type based on signature of __init__. Adjust - # return type and insert type arguments. - if isinstance(method, FuncBase): - t = function_type(method, fallback) - else: - assert isinstance(method.type, ProperType) - assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this - t = method.type - return type_object_type_from_function(t, info, method.info, fallback, is_new) - - def analyze_decorator_or_funcbase_access( - defn: Decorator | FuncBase, - itype: Instance, - info: TypeInfo, - self_type: Type | None, - name: str, - mx: MemberContext, + defn: Decorator | FuncBase, itype: Instance, name: str, mx: MemberContext ) -> Type: """Analyzes the type behind method access. @@ -1378,20 +1486,91 @@ def analyze_decorator_or_funcbase_access( See: https://github.com/python/mypy/issues/10409 """ if isinstance(defn, Decorator): - return analyze_var(name, defn.var, itype, info, mx) - return bind_self( - function_type(defn, mx.chk.named_type("builtins.function")), original_type=self_type + return analyze_var(name, defn.var, itype, mx) + typ = function_type(defn, mx.chk.named_type("builtins.function")) + if isinstance(defn, (FuncDef, OverloadedFuncDef)) and defn.is_trivial_self: + return bind_self_fast(typ, mx.self_type) + typ = check_self_arg(typ, mx.self_type, defn.is_class, mx.context, name, mx.msg) + return bind_self(typ, original_type=mx.self_type, is_classmethod=defn.is_class) + + +F = TypeVar("F", bound=FunctionLike) + + +def bind_self_fast(method: F, original_type: Type | None = None) -> F: + """Return a copy of `method`, with the type of its first parameter (usually + self or cls) bound to original_type. + + This is a faster version of mypy.typeops.bind_self() that can be used for methods + with trivial self/cls annotations. + """ + if isinstance(method, Overloaded): + items = [bind_self_fast(c, original_type) for c in method.items] + return cast(F, Overloaded(items)) + assert isinstance(method, CallableType) + if not method.arg_types: + # Invalid method, return something. + return method + if method.arg_kinds[0] in (ARG_STAR, ARG_STAR2): + # See typeops.py for details. + return method + return method.copy_modified( + arg_types=method.arg_types[1:], + arg_kinds=method.arg_kinds[1:], + arg_names=method.arg_names[1:], + is_bound=True, ) -def is_valid_constructor(n: SymbolNode | None) -> bool: - """Does this node represents a valid constructor method? +def has_operator(typ: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool: + """Does type have operator with the given name? - This includes normal functions, overloaded functions, and decorators - that return a callable type. + Note: this follows the rules for operator access, in particular: + * __getattr__ is not considered + * for class objects we only look in metaclass + * instance level attributes (i.e. extra_attrs) are not considered """ - if isinstance(n, FuncBase): + # This is much faster than analyze_member_access, and so using + # it first as a filter is important for performance. This is mostly relevant + # in situations where we can't expect that method is likely present, + # e.g. for __OP__ vs __rOP__. + typ = get_proper_type(typ) + + if isinstance(typ, TypeVarLikeType): + typ = typ.values_or_bound() + if isinstance(typ, AnyType): + return True + if isinstance(typ, UnionType): + return all(has_operator(x, op_method, named_type) for x in typ.relevant_items()) + if isinstance(typ, FunctionLike) and typ.is_type_obj(): + return typ.fallback.type.has_readable_member(op_method) + if isinstance(typ, TypeType): + # Type[Union[X, ...]] is always normalized to Union[Type[X], ...], + # so we don't need to care about unions here, but we need to care about + # Type[T], where upper bound of T is a union. + item = typ.item + if isinstance(item, TypeVarType): + item = item.values_or_bound() + if isinstance(item, UnionType): + return all(meta_has_operator(x, op_method, named_type) for x in item.relevant_items()) + return meta_has_operator(item, op_method, named_type) + return instance_fallback(typ, named_type).type.has_readable_member(op_method) + + +def instance_fallback(typ: ProperType, named_type: Callable[[str], Instance]) -> Instance: + if isinstance(typ, Instance): + return typ + if isinstance(typ, TupleType): + return tuple_fallback(typ) + if isinstance(typ, (LiteralType, TypedDictType)): + return typ.fallback + return named_type("builtins.object") + + +def meta_has_operator(item: Type, op_method: str, named_type: Callable[[str], Instance]) -> bool: + item = get_proper_type(item) + if isinstance(item, AnyType): return True - if isinstance(n, Decorator): - return isinstance(get_proper_type(n.type), FunctionLike) - return False + item = instance_fallback(item, named_type) + meta = item.type.metaclass_type or named_type("builtins.type") + return meta.type.has_readable_member(op_method) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index fa23dfb5f453d..cafc69490e09d 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -5,8 +5,8 @@ from collections import defaultdict from typing import Final, NamedTuple -import mypy.checker from mypy import message_registry +from mypy.checker_shared import TypeCheckerSharedApi, TypeRange from mypy.checkmember import analyze_member_access from mypy.expandtype import expand_type_by_instance from mypy.join import join_types @@ -14,7 +14,7 @@ from mypy.maptype import map_instance_to_supertype from mypy.meet import narrow_declared_type from mypy.messages import MessageBuilder -from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, TypeInfo, Var +from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, Var from mypy.options import Options from mypy.patterns import ( AsPattern, @@ -37,6 +37,7 @@ ) from mypy.types import ( AnyType, + FunctionLike, Instance, LiteralType, NoneType, @@ -45,15 +46,18 @@ Type, TypedDictType, TypeOfAny, + TypeType, TypeVarTupleType, + TypeVarType, UninhabitedType, UnionType, UnpackType, + callable_with_ellipsis, find_unpack_in_list, get_proper_type, split_with_prefix_and_suffix, ) -from mypy.typevars import fill_typevars +from mypy.typevars import fill_typevars, fill_typevars_with_any from mypy.visitor import PatternVisitor self_match_type_names: Final = [ @@ -90,7 +94,7 @@ class PatternChecker(PatternVisitor[PatternType]): """ # Some services are provided by a TypeChecker instance. - chk: mypy.checker.TypeChecker + chk: TypeCheckerSharedApi # This is shared with TypeChecker, but stored also here for convenience. msg: MessageBuilder # Currently unused @@ -111,7 +115,7 @@ class PatternChecker(PatternVisitor[PatternType]): options: Options def __init__( - self, chk: mypy.checker.TypeChecker, msg: MessageBuilder, plugin: Plugin, options: Options + self, chk: TypeCheckerSharedApi, msg: MessageBuilder, plugin: Plugin, options: Options ) -> None: self.chk = chk self.msg = msg @@ -158,7 +162,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 @@ -190,7 +195,7 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType: for capture_list in capture_types.values(): typ = UninhabitedType() for _, other in capture_list: - typ = join_types(typ, other) + typ = make_simplified_union([typ, other]) captures[capture_list[0][0]] = typ @@ -342,13 +347,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: @@ -447,6 +450,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: @@ -528,36 +541,31 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: # Check class type # type_info = o.class_ref.node - if type_info is None: - return PatternType(AnyType(TypeOfAny.from_error), AnyType(TypeOfAny.from_error), {}) + typ = self.chk.expr_checker.accept(o.class_ref) + p_typ = get_proper_type(typ) if isinstance(type_info, TypeAlias) and not type_info.no_args: self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o) return self.early_non_match() - if isinstance(type_info, TypeInfo): - any_type = AnyType(TypeOfAny.implementation_artifact) - args: list[Type] = [] - for tv in type_info.defn.type_vars: - if isinstance(tv, TypeVarTupleType): - args.append( - UnpackType(self.chk.named_generic_type("builtins.tuple", [any_type])) - ) - else: - args.append(any_type) - typ: Type = Instance(type_info, args) - elif isinstance(type_info, TypeAlias): - typ = type_info.target + elif isinstance(p_typ, FunctionLike) and p_typ.is_type_obj(): + typ = fill_typevars_with_any(p_typ.type_object()) elif ( isinstance(type_info, Var) and type_info.type is not None - and isinstance(get_proper_type(type_info.type), AnyType) + and type_info.fullname == "typing.Callable" ): - typ = type_info.type - else: - if isinstance(type_info, Var) and type_info.type is not None: - name = type_info.type.str_with_options(self.options) - else: - name = type_info.name - self.msg.fail(message_registry.CLASS_PATTERN_TYPE_REQUIRED.format(name), o) + # Create a `Callable[..., Any]` + fallback = self.chk.named_type("builtins.function") + any_type = AnyType(TypeOfAny.unannotated) + typ = callable_with_ellipsis(any_type, ret_type=any_type, fallback=fallback) + elif isinstance(p_typ, TypeType) and isinstance(p_typ.item, NoneType): + typ = p_typ.item + elif not isinstance(p_typ, AnyType): + self.msg.fail( + message_registry.CLASS_PATTERN_TYPE_REQUIRED.format( + typ.str_with_options(self.options) + ), + o, + ) return self.early_non_match() new_type, rest_type = self.chk.conditional_types_with_intersection( @@ -594,10 +602,9 @@ 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, original_type=typ, chk=self.chk, ) @@ -660,10 +667,9 @@ 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, original_type=new_type, chk=self.chk, ) @@ -672,12 +678,15 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: has_local_errors = local_errors.has_new_errors() if has_local_errors or key_type is None: key_type = AnyType(TypeOfAny.from_error) - self.msg.fail( - message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format( - typ.str_with_options(self.options), keyword - ), - pattern, - ) + if not (type_info and type_info.fullname == "builtins.object"): + self.msg.fail( + message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format( + typ.str_with_options(self.options), keyword + ), + pattern, + ) + elif keyword is not None: + new_type = self.chk.add_any_attribute_to_type(new_type, keyword) inner_type, inner_rest_type, inner_captures = self.accept(pattern, key_type) if is_uninhabited(inner_type): @@ -693,7 +702,13 @@ 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, TupleType): + typ = typ.partial_fallback + if isinstance(typ, AnyType): + return False + 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): @@ -701,6 +716,8 @@ def should_self_match(self, typ: Type) -> bool: return False def can_match_sequence(self, typ: ProperType) -> bool: + if isinstance(typ, AnyType): + return True if isinstance(typ, UnionType): return any(self.can_match_sequence(get_proper_type(item)) for item in typ.items) for other in self.non_sequence_match_types: @@ -751,6 +768,8 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: or class T(Sequence[Tuple[T, T]]), there is no way any of those can map to Sequence[str]. """ proper_type = get_proper_type(outer_type) + if isinstance(proper_type, AnyType): + return outer_type if isinstance(proper_type, UnionType): types = [ self.construct_sequence_child(item, inner_type) @@ -760,7 +779,6 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: return make_simplified_union(types) sequence = self.chk.named_generic_type("typing.Sequence", [inner_type]) if is_subtype(outer_type, self.chk.named_type("typing.Sequence")): - proper_type = get_proper_type(outer_type) if isinstance(proper_type, TupleType): proper_type = tuple_fallback(proper_type) assert isinstance(proper_type, Instance) @@ -790,13 +808,13 @@ def get_var(expr: Expression) -> Var: Warning: this in only true for expressions captured by a match statement. Don't call it from anywhere else """ - assert isinstance(expr, NameExpr) + assert isinstance(expr, NameExpr), expr node = expr.node - assert isinstance(node, Var) + assert isinstance(node, Var), node return node -def get_type_range(typ: Type) -> mypy.checker.TypeRange: +def get_type_range(typ: Type) -> TypeRange: typ = get_proper_type(typ) if ( isinstance(typ, Instance) @@ -804,7 +822,7 @@ def get_type_range(typ: Type) -> mypy.checker.TypeRange: and isinstance(typ.last_known_value.value, bool) ): typ = typ.last_known_value - return mypy.checker.TypeRange(typ, is_upper_bound=False) + return TypeRange(typ, is_upper_bound=False) def is_uninhabited(typ: Type) -> bool: diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index c63210a96c443..45075bd375521 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -13,11 +13,16 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Callable, Dict, Final, Match, Pattern, Tuple, Union, cast +from re import Match, Pattern +from typing import Callable, Final, Union, cast from typing_extensions import TypeAlias as _TypeAlias import mypy.errorcodes as codes +from mypy import message_registry +from mypy.checker_shared import TypeCheckerSharedApi from mypy.errors import Errors +from mypy.maptype import map_instance_to_supertype +from mypy.messages import MessageBuilder from mypy.nodes import ( ARG_NAMED, ARG_POS, @@ -40,6 +45,9 @@ TempNode, TupleExpr, ) +from mypy.parse import parse +from mypy.subtypes import is_subtype +from mypy.typeops import custom_special_method from mypy.types import ( AnyType, Instance, @@ -56,21 +64,9 @@ get_proper_types, ) -if TYPE_CHECKING: - # break import cycle only needed for mypy - import mypy.checker - import mypy.checkexpr - -from mypy import message_registry -from mypy.maptype import map_instance_to_supertype -from mypy.messages import MessageBuilder -from mypy.parse import parse -from mypy.subtypes import is_subtype -from mypy.typeops import custom_special_method - FormatStringExpr: _TypeAlias = Union[StrExpr, BytesExpr] -Checkers: _TypeAlias = Tuple[Callable[[Expression], None], Callable[[Type], bool]] -MatchMap: _TypeAlias = Dict[Tuple[int, int], Match[str]] # span -> match +Checkers: _TypeAlias = tuple[Callable[[Expression], None], Callable[[Type], bool]] +MatchMap: _TypeAlias = dict[tuple[int, int], Match[str]] # span -> match def compile_format_re() -> Pattern[str]: @@ -298,21 +294,13 @@ class StringFormatterChecker: """ # Some services are provided by a TypeChecker instance. - chk: mypy.checker.TypeChecker + chk: TypeCheckerSharedApi # This is shared with TypeChecker, but stored also here for convenience. msg: MessageBuilder - # Some services are provided by a ExpressionChecker instance. - exprchk: mypy.checkexpr.ExpressionChecker - def __init__( - self, - exprchk: mypy.checkexpr.ExpressionChecker, - chk: mypy.checker.TypeChecker, - msg: MessageBuilder, - ) -> None: + def __init__(self, chk: TypeCheckerSharedApi, msg: MessageBuilder) -> None: """Construct an expression type checker.""" self.chk = chk - self.exprchk = exprchk self.msg = msg def check_str_format_call(self, call: CallExpr, format_value: str) -> None: @@ -393,8 +381,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 +462,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, ) @@ -616,7 +605,7 @@ def apply_field_accessors( # TODO: fix column to point to actual start of the format specifier _within_ string. temp_ast.line = ctx.line temp_ast.column = ctx.column - self.exprchk.accept(temp_ast) + self.chk.expr_checker.accept(temp_ast) return temp_ast def validate_and_transform_accessors( @@ -654,8 +643,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, ) @@ -682,7 +672,7 @@ def check_str_interpolation(self, expr: FormatStringExpr, replacements: Expressi """Check the types of the 'replacements' in a string interpolation expression: str % replacements. """ - self.exprchk.accept(expr) + self.chk.expr_checker.accept(expr) specifiers = parse_conversion_specifiers(expr.value) has_mapping_keys = self.analyze_conversion_specifiers(specifiers, expr) if has_mapping_keys is None: diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a6bf021000c1a..2bfd2a1e2eefe 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -8,36 +8,30 @@ import sys from io import StringIO -from mypy.errorcodes import error_codes - if sys.version_info >= (3, 11): import tomllib else: import tomli as tomllib -from typing import ( - Any, - Callable, - Dict, - Final, - Iterable, - List, - Mapping, - MutableMapping, - Sequence, - TextIO, - Tuple, - Union, -) -from typing_extensions import TypeAlias as _TypeAlias +from collections.abc import Mapping, MutableMapping, Sequence +from typing import Any, Callable, Final, TextIO, Union +from typing_extensions import Never, TypeAlias from mypy import defaults from mypy.options import PER_MODULE_OPTIONS, Options -_CONFIG_VALUE_TYPES: _TypeAlias = Union[ - str, bool, int, float, Dict[str, str], List[str], Tuple[int, int] +_CONFIG_VALUE_TYPES: TypeAlias = Union[ + str, bool, int, float, dict[str, str], list[str], tuple[int, int] ] -_INI_PARSER_CALLABLE: _TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES] +_INI_PARSER_CALLABLE: TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES] + + +class VersionTypeError(argparse.ArgumentTypeError): + """Provide a fallback value if the Python version is unsupported.""" + + def __init__(self, *args: Any, fallback: tuple[int, int]) -> None: + self.fallback = fallback + super().__init__(*args) def parse_version(v: str | float) -> tuple[int, int]: @@ -56,7 +50,7 @@ def parse_version(v: str | float) -> tuple[int, int]: if isinstance(v, float): msg += ". You may need to put quotes around your Python version" - raise argparse.ArgumentTypeError(msg) + raise VersionTypeError(msg, fallback=defaults.PYTHON3_VERSION_MIN) else: raise argparse.ArgumentTypeError( f"Python major version '{major}' out of range (must be 3)" @@ -64,21 +58,31 @@ def parse_version(v: str | float) -> tuple[int, int]: return major, minor -def try_split(v: str | Sequence[str], split_regex: str = "[,]") -> list[str]: - """Split and trim a str or list of str into a list of str""" - if isinstance(v, str): - return [p.strip() for p in re.split(split_regex, v)] - - return [p.strip() for p in v] - +def try_split(v: str | Sequence[str] | object, split_regex: str = ",") -> list[str]: + """Split and trim a str or sequence (eg: list) of str into a list of str. + If an element of the input is not str, a type error will be raised.""" -def validate_codes(codes: list[str]) -> list[str]: - invalid_codes = set(codes) - set(error_codes.keys()) - if invalid_codes: + def complain(x: object, additional_info: str = "") -> Never: raise argparse.ArgumentTypeError( - f"Invalid error code(s): {', '.join(sorted(invalid_codes))}" + f"Expected a list or a stringified version thereof, but got: '{x}', of type {type(x).__name__}.{additional_info}" ) - return codes + + if isinstance(v, str): + items = [p.strip() for p in re.split(split_regex, v)] + if items and items[-1] == "": + items.pop(-1) + return items + elif isinstance(v, Sequence): + return [ + ( + p.strip() + if isinstance(p, str) + else complain(p, additional_info=" (As an element of the list.)") + ) + for p in v + ] + else: + complain(v) def validate_package_allow_list(allow_list: list[str]) -> list[str]: @@ -138,7 +142,7 @@ def split_and_match_files(paths: str) -> list[str]: Returns a list of file paths """ - return split_and_match_files_list(paths.split(",")) + return split_and_match_files_list(split_commas(paths)) def check_follow_imports(choice: str) -> str: @@ -194,8 +198,8 @@ def split_commas(value: str) -> list[str]: [p.strip() for p in split_commas(s)] ), "enable_incomplete_feature": lambda s: [p.strip() for p in split_commas(s)], - "disable_error_code": lambda s: validate_codes([p.strip() for p in split_commas(s)]), - "enable_error_code": lambda s: validate_codes([p.strip() for p in split_commas(s)]), + "disable_error_code": lambda s: [p.strip() for p in split_commas(s)], + "enable_error_code": lambda s: [p.strip() for p in split_commas(s)], "package_root": lambda s: [p.strip() for p in split_commas(s)], "cache_dir": expand_path, "python_executable": expand_path, @@ -219,8 +223,8 @@ def split_commas(value: str) -> list[str]: "always_false": try_split, "untyped_calls_exclude": lambda s: validate_package_allow_list(try_split(s)), "enable_incomplete_feature": try_split, - "disable_error_code": lambda s: validate_codes(try_split(s)), - "enable_error_code": lambda s: validate_codes(try_split(s)), + "disable_error_code": lambda s: try_split(s), + "enable_error_code": lambda s: try_split(s), "package_root": try_split, "exclude": str_or_array_as_list, "packages": try_split, @@ -229,6 +233,72 @@ def split_commas(value: str) -> list[str]: ) +def _parse_individual_file( + config_file: str, stderr: TextIO | None = None +) -> tuple[MutableMapping[str, Any], dict[str, _INI_PARSER_CALLABLE], str] | None: + + if not os.path.exists(config_file): + return None + + parser: MutableMapping[str, Any] + try: + if is_toml(config_file): + with open(config_file, "rb") as f: + toml_data = tomllib.load(f) + # Filter down to just mypy relevant toml keys + toml_data = toml_data.get("tool", {}) + if "mypy" not in toml_data: + return None + toml_data = {"mypy": toml_data["mypy"]} + parser = destructure_overrides(toml_data) + config_types = toml_config_types + else: + parser = configparser.RawConfigParser() + parser.read(config_file) + config_types = ini_config_types + + except (tomllib.TOMLDecodeError, configparser.Error, ConfigTOMLValueError) as err: + print(f"{config_file}: {err}", file=stderr) + return None + + if os.path.basename(config_file) in defaults.SHARED_CONFIG_NAMES and "mypy" not in parser: + return None + + return parser, config_types, config_file + + +def _find_config_file( + stderr: TextIO | None = None, +) -> tuple[MutableMapping[str, Any], dict[str, _INI_PARSER_CALLABLE], str] | None: + + current_dir = os.path.abspath(os.getcwd()) + + while True: + for name in defaults.CONFIG_NAMES + defaults.SHARED_CONFIG_NAMES: + config_file = os.path.relpath(os.path.join(current_dir, name)) + ret = _parse_individual_file(config_file, stderr) + if ret is None: + continue + return ret + + if any( + os.path.exists(os.path.join(current_dir, cvs_root)) for cvs_root in (".git", ".hg") + ): + break + parent_dir = os.path.dirname(current_dir) + if parent_dir == current_dir: + break + current_dir = parent_dir + + for config_file in defaults.USER_CONFIG_FILES: + ret = _parse_individual_file(config_file, stderr) + if ret is None: + continue + return ret + + return None + + def parse_config_file( options: Options, set_strict_flags: Callable[[], None], @@ -245,47 +315,20 @@ def parse_config_file( stdout = stdout or sys.stdout stderr = stderr or sys.stderr - if filename is not None: - config_files: tuple[str, ...] = (filename,) - else: - config_files_iter: Iterable[str] = map(os.path.expanduser, defaults.CONFIG_FILES) - config_files = tuple(config_files_iter) - - config_parser = configparser.RawConfigParser() - - for config_file in config_files: - if not os.path.exists(config_file): - continue - try: - if is_toml(config_file): - with open(config_file, "rb") as f: - toml_data = tomllib.load(f) - # Filter down to just mypy relevant toml keys - toml_data = toml_data.get("tool", {}) - if "mypy" not in toml_data: - continue - toml_data = {"mypy": toml_data["mypy"]} - parser: MutableMapping[str, Any] = destructure_overrides(toml_data) - config_types = toml_config_types - else: - config_parser.read(config_file) - parser = config_parser - config_types = ini_config_types - except (tomllib.TOMLDecodeError, configparser.Error, ConfigTOMLValueError) as err: - print(f"{config_file}: {err}", file=stderr) - else: - if config_file in defaults.SHARED_CONFIG_FILES and "mypy" not in parser: - continue - file_read = config_file - options.config_file = file_read - break - else: + ret = ( + _parse_individual_file(filename, stderr) + if filename is not None + else _find_config_file(stderr) + ) + if ret is None: return + parser, config_types, file_read = ret - os.environ["MYPY_CONFIG_FILE_DIR"] = os.path.dirname(os.path.abspath(config_file)) + options.config_file = file_read + os.environ["MYPY_CONFIG_FILE_DIR"] = os.path.dirname(os.path.abspath(file_read)) if "mypy" not in parser: - if filename or file_read not in defaults.SHARED_CONFIG_FILES: + if filename or os.path.basename(file_read) not in defaults.SHARED_CONFIG_NAMES: print(f"{file_read}: No [mypy] section in config file", file=stderr) else: section = parser["mypy"] @@ -472,12 +515,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("_", "-") @@ -524,6 +562,9 @@ def parse_section( continue try: v = ct(section.get(key)) + except VersionTypeError as err_version: + print(f"{prefix}{key}: {err_version}", file=stderr) + v = err_version.fallback except argparse.ArgumentTypeError as err: print(f"{prefix}{key}: {err}", file=stderr) continue @@ -615,9 +656,8 @@ def parse_mypy_comments( Returns a dictionary of options to be applied and a list of error messages generated. """ - errors: list[tuple[int, str]] = [] - sections = {} + sections: dict[str, object] = {"enable_error_code": [], "disable_error_code": []} for lineno, line in args: # In order to easily match the behavior for bools, we abuse configparser. @@ -625,6 +665,10 @@ def parse_mypy_comments( # method is to create a config parser. parser = configparser.RawConfigParser() options, parse_errors = mypy_comments_to_config_map(line, template) + if "python_version" in options: + errors.append((lineno, "python_version not supported in inline configuration")) + del options["python_version"] + parser["dummy"] = options errors.extend((lineno, x) for x in parse_errors) @@ -650,9 +694,24 @@ def set_strict_flags() -> None: '(see "mypy -h" for the list of flags enabled in strict mode)', ) ) - + # Because this is currently special-cased + # (the new_sections for an inline config *always* includes 'disable_error_code' and + # 'enable_error_code' fields, usually empty, which overwrite the old ones), + # we have to manipulate them specially. + # This could use a refactor, but so could the whole subsystem. + if ( + "enable_error_code" in new_sections + and isinstance(neec := new_sections["enable_error_code"], list) + and isinstance(eec := sections.get("enable_error_code", []), list) + ): + new_sections["enable_error_code"] = sorted(set(neec + eec)) + if ( + "disable_error_code" in new_sections + and isinstance(ndec := new_sections["disable_error_code"], list) + and isinstance(dec := sections.get("disable_error_code", []), list) + ): + new_sections["disable_error_code"] = sorted(set(ndec + dec)) sections.update(new_sections) - return sections, errors diff --git a/mypy/constraints.py b/mypy/constraints.py index 49a2aea8fa051..96c0c7ccaf35e 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Iterable, List, Sequence +from collections.abc import Iterable, Sequence +from typing import TYPE_CHECKING, Final, cast +from typing_extensions import TypeGuard import mypy.subtypes import mypy.typeops @@ -19,6 +21,7 @@ ArgKind, TypeInfo, ) +from mypy.type_visitor import ALL_STRATEGY, BoolTypeQuery from mypy.types import ( TUPLE_LIKE_INSTANCE_NAMES, AnyType, @@ -39,7 +42,6 @@ TypeAliasType, TypedDictType, TypeOfAny, - TypeQuery, TypeType, TypeVarId, TypeVarLikeType, @@ -51,6 +53,7 @@ UnionType, UnpackType, find_unpack_in_list, + flatten_nested_tuples, get_proper_type, has_recursive_types, has_type_vars, @@ -125,12 +128,12 @@ def infer_constraints_for_callable( param_spec_arg_kinds = [] incomplete_star_mapping = False - for i, actuals in enumerate(formal_to_actual): + for i, actuals in enumerate(formal_to_actual): # TODO: isn't this `enumerate(arg_types)`? for actual in actuals: - if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): + if actual is None and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2): # type: ignore[unreachable] # We can't use arguments to infer ParamSpec constraint, if only some # are present in the current inference pass. - incomplete_star_mapping = True + incomplete_star_mapping = True # type: ignore[unreachable] break for i, actuals in enumerate(formal_to_actual): @@ -339,6 +342,16 @@ def _infer_constraints( if isinstance(actual, AnyType) and actual.type_of_any == TypeOfAny.suggestion_engine: return [] + # type[A | B] is always represented as type[A] | type[B] internally. + # This makes our constraint solver choke on type[T] <: type[A] | type[B], + # solving T as generic meet(A, B) which is often `object`. Force unwrap such unions + # if both sides are type[...] or unions thereof. See `testTypeVarType` test + type_type_unwrapped = False + if _is_type_type(template) and _is_type_type(actual): + type_type_unwrapped = True + template = _unwrap_type_type(template) + actual = _unwrap_type_type(actual) + # If the template is simply a type variable, emit a Constraint directly. # We need to handle this case before handling Unions for two reasons: # 1. "T <: Union[U1, U2]" is not equivalent to "T <: U1 or T <: U2", @@ -372,6 +385,11 @@ def _infer_constraints( if direction == SUPERTYPE_OF and isinstance(actual, UnionType): res = [] for a_item in actual.items: + # `orig_template` has to be preserved intact in case it's recursive. + # If we unwrapped ``type[...]`` previously, wrap the item back again, + # as ``type[...]`` can't be removed from `orig_template`. + if type_type_unwrapped: + a_item = TypeType.make_normalized(a_item) res.extend(infer_constraints(orig_template, a_item, direction)) return res @@ -398,7 +416,7 @@ def _infer_constraints( infer_constraints_if_possible(t_item, actual, direction) for t_item in template.items ], - eager=False, + eager=isinstance(actual, AnyType), ) if result: return result @@ -410,6 +428,26 @@ def _infer_constraints( return template.accept(ConstraintBuilderVisitor(actual, direction, skip_neg_op)) +def _is_type_type(tp: ProperType) -> TypeGuard[TypeType | UnionType]: + """Is ``tp`` a ``type[...]`` or a union thereof? + + ``Type[A | B]`` is internally represented as ``type[A] | type[B]``, and this + troubles the solver sometimes. + """ + return ( + isinstance(tp, TypeType) + or isinstance(tp, UnionType) + and all(isinstance(get_proper_type(o), TypeType) for o in tp.items) + ) + + +def _unwrap_type_type(tp: TypeType | UnionType) -> ProperType: + """Extract the inner type from ``type[...]`` expression or a union thereof.""" + if isinstance(tp, TypeType): + return tp.item + return UnionType.make_union([cast(TypeType, get_proper_type(o)).item for o in tp.items]) + + def infer_constraints_if_possible( template: Type, actual: Type, direction: int ) -> list[Constraint] | None: @@ -474,7 +512,7 @@ def handle_recursive_union(template: UnionType, actual: Type, direction: int) -> ) or infer_constraints(UnionType.make_union(type_var_items), actual, direction) -def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list[Constraint]: +def any_constraints(options: list[list[Constraint] | None], *, eager: bool) -> list[Constraint]: """Deduce what we can from a collection of constraint lists. It's a given that at least one of the lists must be satisfied. A @@ -508,12 +546,8 @@ def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list for option in valid_options: if option in trivial_options: continue - if option is not None: - merged_option: list[Constraint] | None = [merge_with_any(c) for c in option] - else: - merged_option = None - merged_options.append(merged_option) - return any_constraints(list(merged_options), eager) + merged_options.append([merge_with_any(c) for c in option]) + return any_constraints(list(merged_options), eager=eager) # If normal logic didn't work, try excluding trivially unsatisfiable constraint (due to # upper bounds) from each option, and comparing them again. @@ -521,6 +555,14 @@ def any_constraints(options: list[list[Constraint] | None], eager: bool) -> list if filtered_options != options: return any_constraints(filtered_options, eager=eager) + # Try harder: if that didn't work, try to strip typevars that aren't meta vars. + # Note this is what we would always do, but unfortunately some callers may not + # set the meta var status correctly (for historical reasons), so we use this as + # a fallback only. + filtered_options = [exclude_non_meta_vars(o) for o in options] + if filtered_options != options: + return any_constraints(filtered_options, eager=eager) + # Otherwise, there are either no valid options or multiple, inconsistent valid # options. Give up and deduce nothing. return [] @@ -535,6 +577,7 @@ def filter_satisfiable(option: list[Constraint] | None) -> list[Constraint] | No """ if not option: return option + satisfiable = [] for c in option: if isinstance(c.origin_type_var, TypeVarType) and c.origin_type_var.values: @@ -549,6 +592,15 @@ def filter_satisfiable(option: list[Constraint] | None) -> list[Constraint] | No return satisfiable +def exclude_non_meta_vars(option: list[Constraint] | None) -> list[Constraint] | None: + # If we had an empty list, keep it intact + if not option: + return option + # However, if none of the options actually references meta vars, better remove + # this constraint entirely. + return [c for c in option if c.type_var.is_meta_var()] or None + + def is_same_constraints(x: list[Constraint], y: list[Constraint]) -> bool: for c1 in x: if not any(is_same_constraint(c1, c2) for c2 in y): @@ -618,15 +670,15 @@ def is_complete_type(typ: Type) -> bool: return typ.accept(CompleteTypeVisitor()) -class CompleteTypeVisitor(TypeQuery[bool]): +class CompleteTypeVisitor(BoolTypeQuery): def __init__(self) -> None: - super().__init__(all) + super().__init__(ALL_STRATEGY) def visit_uninhabited_type(self, t: UninhabitedType) -> bool: return False -class ConstraintBuilderVisitor(TypeVisitor[List[Constraint]]): +class ConstraintBuilderVisitor(TypeVisitor[list[Constraint]]): """Visitor class for inferring type constraints.""" # The type that is compared against a template @@ -719,40 +771,40 @@ def visit_instance(self, template: Instance) -> list[Constraint]: "__call__", template, actual, is_operator=True ) assert call is not None - if mypy.subtypes.is_subtype(actual, erase_typevars(call)): - subres = infer_constraints(call, actual, self.direction) - res.extend(subres) + if ( + self.direction == SUPERTYPE_OF + and mypy.subtypes.is_subtype(actual, erase_typevars(call)) + or self.direction == SUBTYPE_OF + and mypy.subtypes.is_subtype(erase_typevars(call), actual) + ): + res.extend(infer_constraints(call, actual, self.direction)) template.type.inferring.pop() if isinstance(actual, CallableType) and actual.fallback is not None: - if actual.is_type_obj() and template.type.is_protocol: + if ( + actual.is_type_obj() + and template.type.is_protocol + and self.direction == SUPERTYPE_OF + ): ret_type = get_proper_type(actual.ret_type) if isinstance(ret_type, TupleType): ret_type = mypy.typeops.tuple_fallback(ret_type) if isinstance(ret_type, Instance): - if self.direction == SUBTYPE_OF: - subtype = template - else: - subtype = ret_type res.extend( self.infer_constraints_from_protocol_members( - ret_type, template, subtype, template, class_obj=True + ret_type, template, ret_type, template, class_obj=True ) ) actual = actual.fallback if isinstance(actual, TypeType) and template.type.is_protocol: - if isinstance(actual.item, Instance): - if self.direction == SUBTYPE_OF: - subtype = template - else: - subtype = actual.item - res.extend( - self.infer_constraints_from_protocol_members( - actual.item, template, subtype, template, class_obj=True - ) - ) if self.direction == SUPERTYPE_OF: - # Infer constraints for Type[T] via metaclass of T when it makes sense. a_item = actual.item + if isinstance(a_item, Instance): + res.extend( + self.infer_constraints_from_protocol_members( + a_item, template, a_item, template, class_obj=True + ) + ) + # Infer constraints for Type[T] via metaclass of T when it makes sense. if isinstance(a_item, TypeVarType): a_item = get_proper_type(a_item.upper_bound) if isinstance(a_item, Instance) and a_item.type.metaclass_type: @@ -1006,6 +1058,17 @@ def infer_constraints_from_protocol_members( return [] # See #11020 # The above is safe since at this point we know that 'instance' is a subtype # of (erased) 'template', therefore it defines all protocol members + if class_obj: + # For class objects we must only infer constraints if possible, otherwise it + # can lead to confusion between class and instance, for example StrEnum is + # Iterable[str] for an instance, but Iterable[StrEnum] for a class object. + if not mypy.subtypes.is_subtype( + inst, erase_typevars(temp), ignore_pos_arg_names=True + ): + continue + # This exception matches the one in typeops.py, see PR #14121 for context. + if member == "__call__" and instance.type.is_metaclass(precise=True): + continue res.extend(infer_constraints(temp, inst, self.direction)) if mypy.subtypes.IS_SETTABLE in mypy.subtypes.get_member_flags(member, protocol): # Settable members are invariant, add opposite constraints @@ -1018,29 +1081,21 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # using e.g. callback protocols. # TODO: check that callables match? Ideally we should not infer constraints # callables that can never be subtypes of one another in given direction. - template = template.with_unpacked_kwargs() + template = template.with_unpacked_kwargs().with_normalized_var_args() extra_tvars = False if isinstance(self.actual, CallableType): res: list[Constraint] = [] - cactual = self.actual.with_unpacked_kwargs() + cactual = self.actual.with_unpacked_kwargs().with_normalized_var_args() param_spec = template.param_spec() template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type 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)) @@ -1055,7 +1110,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: # like U -> U, should be Callable[..., Any], but if U is a self-type, we can # allow it to leak, to be later bound to self. A bunch of existing code # depends on this old behaviour. - and not any(tv.id.is_self() for tv in cactual.variables) + and not ( + any(tv.id.is_self() for tv in cactual.variables) + and template.is_ellipsis_args + ) ): # If the actual callable is generic, infer constraints in the opposite # direction, and indicate to the solver there are extra type variables @@ -1280,6 +1338,11 @@ def visit_tuple_type(self, template: TupleType) -> list[Constraint]: res.extend( infer_constraints(template_items[i], actual_items[i], self.direction) ) + res.extend( + infer_constraints( + template.partial_fallback, actual.partial_fallback, self.direction + ) + ) return res elif isinstance(actual, AnyType): return self.infer_against_any(template.items, actual) @@ -1311,7 +1374,9 @@ def visit_type_alias_type(self, template: TypeAliasType) -> list[Constraint]: def infer_against_any(self, types: Iterable[Type], any_type: AnyType) -> list[Constraint]: res: list[Constraint] = [] - for t in types: + # Some items may be things like `*Tuple[*Ts, T]` for example from callable types with + # suffix after *arg, so flatten them. + for t in flatten_nested_tuples(types): if isinstance(t, UnpackType): if isinstance(t.type, TypeVarTupleType): res.append(Constraint(t.type, self.direction, any_type)) diff --git a/mypy/copytype.py b/mypy/copytype.py index ecb1a89759b67..a890431a1772b 100644 --- a/mypy/copytype.py +++ b/mypy/copytype.py @@ -122,7 +122,7 @@ def visit_overloaded(self, t: Overloaded) -> ProperType: def visit_type_type(self, t: TypeType) -> ProperType: # Use cast since the type annotations in TypeType are imprecise. - return self.copy_common(t, TypeType(cast(Any, t.item))) + return self.copy_common(t, TypeType(cast(Any, t.item), is_type_form=t.is_type_form)) def visit_type_alias_type(self, t: TypeAliasType) -> ProperType: assert False, "only ProperTypes supported" diff --git a/mypy/defaults.py b/mypy/defaults.py index 2bbae23d7e2d8..58a74a478b16d 100644 --- a/mypy/defaults.py +++ b/mypy/defaults.py @@ -6,23 +6,21 @@ # Earliest fully supported Python 3.x version. Used as the default Python # version in tests. Mypy wheels should be built starting with this version, # and CI tests should be run on this version (and later versions). -PYTHON3_VERSION: Final = (3, 8) +PYTHON3_VERSION: Final = (3, 9) # Earliest Python 3.x version supported via --python-version 3.x. To run # mypy, at least version PYTHON3_VERSION is needed. -PYTHON3_VERSION_MIN: Final = (3, 8) # Keep in sync with typeshed's python support +PYTHON3_VERSION_MIN: Final = (3, 9) # Keep in sync with typeshed's python support CACHE_DIR: Final = ".mypy_cache" -CONFIG_FILE: Final = ["mypy.ini", ".mypy.ini"] -PYPROJECT_CONFIG_FILES: Final = ["pyproject.toml"] -SHARED_CONFIG_FILES: Final = ["setup.cfg"] -USER_CONFIG_FILES: Final = ["~/.config/mypy/config", "~/.mypy.ini"] + +CONFIG_NAMES: Final = ["mypy.ini", ".mypy.ini"] +SHARED_CONFIG_NAMES: Final = ["pyproject.toml", "setup.cfg"] + +USER_CONFIG_FILES: list[str] = ["~/.config/mypy/config", "~/.mypy.ini"] if os.environ.get("XDG_CONFIG_HOME"): USER_CONFIG_FILES.insert(0, os.path.join(os.environ["XDG_CONFIG_HOME"], "mypy/config")) - -CONFIG_FILES: Final = ( - CONFIG_FILE + PYPROJECT_CONFIG_FILES + SHARED_CONFIG_FILES + USER_CONFIG_FILES -) +USER_CONFIG_FILES = [os.path.expanduser(f) for f in USER_CONFIG_FILES] # This must include all reporters defined in mypy.report. This is defined here # to make reporter names available without importing mypy.report -- this speeds diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 9f0751e936090..3db47f80d01b7 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -14,11 +14,13 @@ import sys import time import traceback -from typing import Any, Callable, Mapping, NoReturn +from collections.abc import Mapping +from typing import Any, Callable, NoReturn from mypy.dmypy_os import alive, kill from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive, send from mypy.ipc import IPCClient, IPCException +from mypy.main import RECURSION_LIMIT from mypy.util import check_python_version, get_terminal_width, should_force_color from mypy.version import __version__ @@ -27,8 +29,8 @@ class AugmentedHelpFormatter(argparse.RawDescriptionHelpFormatter): - def __init__(self, prog: str) -> None: - super().__init__(prog=prog, max_help_position=30) + def __init__(self, prog: str, **kwargs: Any) -> None: + super().__init__(prog=prog, max_help_position=30, **kwargs) parser = argparse.ArgumentParser( @@ -267,6 +269,10 @@ class BadStatus(Exception): def main(argv: list[str]) -> None: """The code is top-down.""" check_python_version("dmypy") + + # set recursion limit consistent with mypy/main.py + sys.setrecursionlimit(RECURSION_LIMIT) + args = parser.parse_args(argv) if not args.action: parser.print_usage() @@ -436,7 +442,7 @@ def do_status(args: argparse.Namespace) -> None: if args.verbose or "error" in response: show_stats(response) if "error" in response: - fail(f"Daemon is stuck; consider {sys.argv[0]} kill") + fail(f"Daemon may be busy processing; if this persists, consider {sys.argv[0]} kill") print("Daemon is up and running") @@ -447,7 +453,7 @@ def do_stop(args: argparse.Namespace) -> None: response = request(args.status_file, "stop", timeout=5) if "error" in response: show_stats(response) - fail(f"Daemon is stuck; consider {sys.argv[0]} kill") + fail(f"Daemon may be busy processing; if this persists, consider {sys.argv[0]} kill") else: print("Daemon stopped") @@ -553,6 +559,10 @@ def check_output( Call sys.exit() unless the status code is zero. """ + if os.name == "nt": + # Enable ANSI color codes for Windows cmd using this strange workaround + # ( see https://github.com/python/cpython/issues/74261 ) + os.system("") if "error" in response: fail(response["error"]) try: diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index f8a0f91f87d96..33e9e07477ca9 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -16,8 +16,9 @@ import sys import time import traceback +from collections.abc import Sequence, Set as AbstractSet from contextlib import redirect_stderr, redirect_stdout -from typing import AbstractSet, Any, Callable, Final, List, Sequence, Tuple +from typing import Any, Callable, Final from typing_extensions import TypeAlias as _TypeAlias import mypy.build @@ -161,9 +162,9 @@ def ignore_suppressed_imports(module: str) -> bool: return module.startswith("encodings.") -ModulePathPair: _TypeAlias = Tuple[str, str] -ModulePathPairs: _TypeAlias = List[ModulePathPair] -ChangesAndRemovals: _TypeAlias = Tuple[ModulePathPairs, ModulePathPairs] +ModulePathPair: _TypeAlias = tuple[str, str] +ModulePathPairs: _TypeAlias = list[ModulePathPair] +ChangesAndRemovals: _TypeAlias = tuple[ModulePathPairs, ModulePathPairs] class Server: @@ -619,12 +620,17 @@ def fine_grained_increment_follow_imports( t1 = time.time() manager.log(f"fine-grained increment: find_changed: {t1 - t0:.3f}s") + # Track all modules encountered so far. New entries for all dependencies + # are added below by other module finding methods below. All dependencies + # in graph but not in `seen` are considered deleted at the end of this method. seen = {source.module for source in sources} # Find changed modules reachable from roots (or in roots) already in graph. 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) @@ -712,7 +718,7 @@ def refresh_file(module: str, path: str) -> list[str]: find_changes_time=t1 - t0, fg_update_time=t2 - t1, refresh_suppressed_time=t3 - t2, - find_added_supressed_time=t4 - t3, + find_added_suppressed_time=t4 - t3, cleanup_time=t5 - t4, ) @@ -733,7 +739,9 @@ def find_reachable_changed_modules( Args: roots: modules where to start search from graph: module graph to use for the search - seen: modules we've seen before that won't be visited (mutated here!!) + seen: modules we've seen before that won't be visited (mutated here!!). + Needed to accumulate all modules encountered during update and remove + everything that no longer exists. changed_paths: which paths have changed (stop search here and return any found) Return (encountered reachable changed modules, @@ -753,7 +761,8 @@ def find_reachable_changed_modules( changed.append((nxt.module, nxt.path)) elif nxt.module in graph: state = graph[nxt.module] - for dep in state.dependencies: + ancestors = state.ancestors or [] + for dep in state.dependencies + ancestors: if dep not in seen: seen.add(dep) worklist.append(BuildSource(graph[dep].path, graph[dep].id, followed=True)) @@ -772,7 +781,9 @@ def find_added_suppressed( """Find suppressed modules that have been added (and not included in seen). Args: - seen: reachable modules we've seen before (mutated here!!) + seen: reachable modules we've seen before (mutated here!!). + Needed to accumulate all modules encountered during update and remove + everything that no longer exists. Return suppressed, added modules. """ @@ -888,6 +899,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 +930,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..eeb918b7877e5 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -7,8 +7,9 @@ import io import json +from collections.abc import Iterable, Iterator from types import TracebackType -from typing import Any, Final, Iterable, Iterator, TextIO +from typing import Any, Final, TextIO from mypy.ipc import IPCBase @@ -104,8 +105,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 222e7f2a6d7ab..500d8fd5ae087 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Callable, Container, cast +from collections.abc import Container +from typing import Callable, cast from mypy.nodes import ARG_STAR, ARG_STAR2 from mypy.types import ( @@ -133,7 +134,9 @@ def visit_union_type(self, t: UnionType) -> ProperType: return make_simplified_union(erased_items) def visit_type_type(self, t: TypeType) -> ProperType: - return TypeType.make_normalized(t.item.accept(self), line=t.line) + return TypeType.make_normalized( + t.item.accept(self), line=t.line, is_type_form=t.is_type_form + ) def visit_type_alias_type(self, t: TypeAliasType) -> ProperType: raise RuntimeError("Type aliases should be expanded before accepting this visitor") @@ -144,29 +147,34 @@ def erase_typevars(t: Type, ids_to_erase: Container[TypeVarId] | None = None) -> or just the ones in the provided collection. """ + if ids_to_erase is None: + return t.accept(TypeVarEraser(None, AnyType(TypeOfAny.special_form))) + def erase_id(id: TypeVarId) -> bool: - if ids_to_erase is None: - return True return id in ids_to_erase return t.accept(TypeVarEraser(erase_id, AnyType(TypeOfAny.special_form))) +def erase_meta_id(id: TypeVarId) -> bool: + return id.is_meta_var() + + def replace_meta_vars(t: Type, target_type: Type) -> Type: """Replace unification variables in a type with the target type.""" - return t.accept(TypeVarEraser(lambda id: id.is_meta_var(), target_type)) + return t.accept(TypeVarEraser(erase_meta_id, target_type)) class TypeVarEraser(TypeTranslator): """Implementation of type erasure""" - def __init__(self, erase_id: Callable[[TypeVarId], bool], replacement: Type) -> None: + def __init__(self, erase_id: Callable[[TypeVarId], bool] | None, replacement: Type) -> None: super().__init__() self.erase_id = erase_id self.replacement = replacement def visit_type_var(self, t: TypeVarType) -> Type: - if self.erase_id(t.id): + if self.erase_id is None or self.erase_id(t.id): return self.replacement return t @@ -202,13 +210,22 @@ def visit_tuple_type(self, t: TupleType) -> Type: return unpacked return result + def visit_callable_type(self, t: CallableType) -> Type: + result = super().visit_callable_type(t) + assert isinstance(result, ProperType) and isinstance(result, CallableType) + # Usually this is done in semanal_typeargs.py, but erasure can create + # a non-normal callable from normal one. + result.normalize_trivial_unpack() + return result + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: - if self.erase_id(t.id): + if self.erase_id is None or self.erase_id(t.id): return t.tuple_fallback.copy_modified(args=[self.replacement]) return t def visit_param_spec(self, t: ParamSpecType) -> Type: - if self.erase_id(t.id): + # TODO: we should probably preserve prefix here. + if self.erase_id is None or self.erase_id(t.id): return self.replacement return t diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index a170b5d4d65aa..785b6166b18bf 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -37,6 +37,10 @@ def __init__( def __str__(self) -> str: return f"" + def __repr__(self) -> str: + """This doesn't fulfill the goals of repr but it's better than the default view.""" + return f"" + def __eq__(self, other: object) -> bool: if not isinstance(other, ErrorCode): return False @@ -48,32 +52,28 @@ def __hash__(self) -> int: ATTR_DEFINED: Final = ErrorCode("attr-defined", "Check that attribute exists", "General") NAME_DEFINED: Final = ErrorCode("name-defined", "Check that name is defined", "General") -CALL_ARG: Final[ErrorCode] = ErrorCode( +CALL_ARG: Final = ErrorCode( "call-arg", "Check number, names and kinds of arguments in calls", "General" ) ARG_TYPE: Final = ErrorCode("arg-type", "Check argument types in calls", "General") CALL_OVERLOAD: Final = ErrorCode( "call-overload", "Check that an overload variant matches arguments", "General" ) -VALID_TYPE: Final[ErrorCode] = ErrorCode( - "valid-type", "Check that type (annotation) is valid", "General" -) +VALID_TYPE: Final = ErrorCode("valid-type", "Check that type (annotation) is valid", "General") VAR_ANNOTATED: Final = ErrorCode( "var-annotated", "Require variable annotation if type can't be inferred", "General" ) OVERRIDE: Final = ErrorCode( "override", "Check that method override is compatible with base class", "General" ) -RETURN: Final[ErrorCode] = ErrorCode( - "return", "Check that function always returns a value", "General" -) -RETURN_VALUE: Final[ErrorCode] = ErrorCode( +RETURN: Final = ErrorCode("return", "Check that function always returns a value", "General") +RETURN_VALUE: Final = ErrorCode( "return-value", "Check that return value is compatible with signature", "General" ) -ASSIGNMENT: Final[ErrorCode] = ErrorCode( +ASSIGNMENT: Final = ErrorCode( "assignment", "Check that assigned value is compatible with target", "General" ) -METHOD_ASSIGN: Final[ErrorCode] = ErrorCode( +METHOD_ASSIGN: Final = ErrorCode( "method-assign", "Check that assignment target is not a method", "General", @@ -139,9 +139,7 @@ def __hash__(self) -> int: UNUSED_COROUTINE: Final = ErrorCode( "unused-coroutine", "Ensure that all coroutines are used", "General" ) -# TODO: why do we need the explicit type here? Without it mypyc CI builds fail with -# mypy/message_registry.py:37: error: Cannot determine type of "EMPTY_BODY" [has-type] -EMPTY_BODY: Final[ErrorCode] = ErrorCode( +EMPTY_BODY: Final = ErrorCode( "empty-body", "A dedicated error code to opt out return errors for empty/trivial bodies", "General", @@ -156,7 +154,7 @@ def __hash__(self) -> int: "await-not-async", 'Warn about "await" outside coroutine ("async def")', "General" ) # These error codes aren't enabled by default. -NO_UNTYPED_DEF: Final[ErrorCode] = ErrorCode( +NO_UNTYPED_DEF: Final = ErrorCode( "no-untyped-def", "Check that every function has an annotation", "General" ) NO_UNTYPED_CALL: Final = ErrorCode( @@ -182,13 +180,13 @@ def __hash__(self) -> int: UNREACHABLE: Final = ErrorCode( "unreachable", "Warn about unreachable statements or expressions", "General" ) -ANNOTATION_UNCHECKED = ErrorCode( +ANNOTATION_UNCHECKED: Final = ErrorCode( "annotation-unchecked", "Notify about type annotations in unchecked functions", "General" ) -TYPEDDICT_READONLY_MUTATED = ErrorCode( +TYPEDDICT_READONLY_MUTATED: Final = ErrorCode( "typeddict-readonly-mutated", "TypedDict's ReadOnly key is mutated", "General" ) -POSSIBLY_UNDEFINED: Final[ErrorCode] = ErrorCode( +POSSIBLY_UNDEFINED: Final = ErrorCode( "possibly-undefined", "Warn about variables that are defined only in some execution paths", "General", @@ -197,18 +195,18 @@ def __hash__(self) -> int: REDUNDANT_EXPR: Final = ErrorCode( "redundant-expr", "Warn about redundant expressions", "General", default_enabled=False ) -TRUTHY_BOOL: Final[ErrorCode] = ErrorCode( +TRUTHY_BOOL: Final = ErrorCode( "truthy-bool", "Warn about expressions that could always evaluate to true in boolean contexts", "General", default_enabled=False, ) -TRUTHY_FUNCTION: Final[ErrorCode] = ErrorCode( +TRUTHY_FUNCTION: Final = ErrorCode( "truthy-function", "Warn about function that always evaluate to true in boolean contexts", "General", ) -TRUTHY_ITERABLE: Final[ErrorCode] = ErrorCode( +TRUTHY_ITERABLE: Final = ErrorCode( "truthy-iterable", "Warn about Iterable expressions that could always evaluate to true in boolean contexts", "General", @@ -234,13 +232,13 @@ def __hash__(self) -> int: "General", default_enabled=False, ) -REDUNDANT_SELF_TYPE = ErrorCode( +REDUNDANT_SELF_TYPE: Final = ErrorCode( "redundant-self", "Warn about redundant Self type annotations on method first argument", "General", default_enabled=False, ) -USED_BEFORE_DEF: Final[ErrorCode] = ErrorCode( +USED_BEFORE_DEF: Final = ErrorCode( "used-before-def", "Warn about variables that are used before they are defined", "General" ) UNUSED_IGNORE: Final = ErrorCode( @@ -258,15 +256,27 @@ def __hash__(self) -> int: "General", default_enabled=False, ) -MUTABLE_OVERRIDE: Final[ErrorCode] = ErrorCode( +MUTABLE_OVERRIDE: Final = ErrorCode( "mutable-override", "Reject covariant overrides for mutable attributes", "General", default_enabled=False, ) +EXHAUSTIVE_MATCH: Final = ErrorCode( + "exhaustive-match", + "Reject match statements that are not exhaustive", + "General", + default_enabled=False, +) +METACLASS: Final = ErrorCode("metaclass", "Ensure that metaclass is valid", "General") +MAYBE_UNRECOGNIZED_STR_TYPEFORM: Final = ErrorCode( + "maybe-unrecognized-str-typeform", + "Error when a string is used where a TypeForm is expected but a string annotation cannot be recognized", + "General", +) # Syntax errors are often blocking. -SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General") +SYNTAX: Final = ErrorCode("syntax", "Report syntax errors", "General") # This is an internal marker code for a whole-file ignore. It is not intended to # be user-visible. @@ -274,35 +284,49 @@ def __hash__(self) -> int: del error_codes[FILE.code] # This is a catch-all for remaining uncategorized errors. -MISC: Final[ErrorCode] = ErrorCode("misc", "Miscellaneous other checks", "General") +MISC: Final = ErrorCode("misc", "Miscellaneous other checks", "General") -OVERLOAD_CANNOT_MATCH: Final[ErrorCode] = ErrorCode( +OVERLOAD_CANNOT_MATCH: Final = 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: Final = ErrorCode( "overload-overlap", "Warn if multiple @overload variants overlap in unsafe ways", "General", sub_code_of=MISC, ) -PROPERTY_DECORATOR = ErrorCode( +PROPERTY_DECORATOR: Final = ErrorCode( "prop-decorator", "Decorators on top of @property are not supported", "General", sub_code_of=MISC, ) -NARROWED_TYPE_NOT_SUBTYPE: Final[ErrorCode] = ErrorCode( +UNTYPED_DECORATOR: Final = ErrorCode( + "untyped-decorator", "Error if an untyped decorator makes a typed function untyped", "General" +) + +NARROWED_TYPE_NOT_SUBTYPE: Final = ErrorCode( "narrowed-type-not-subtype", "Warn if a TypeIs function's narrowed type is not a subtype of the original type", "General", ) +EXPLICIT_ANY: Final = ErrorCode( + "explicit-any", "Warn about explicit Any type annotations", "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..ce5c6cc8215fb 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -4,15 +4,18 @@ import sys import traceback from collections import defaultdict -from typing import Callable, Final, Iterable, NoReturn, Optional, TextIO, Tuple, TypeVar -from typing_extensions import Literal, TypeAlias as _TypeAlias +from collections.abc import Iterable, Iterator +from itertools import chain +from typing import Callable, Final, NoReturn, Optional, TextIO, TypeVar +from typing_extensions import Literal, Self, TypeAlias as _TypeAlias from mypy import errorcodes as codes from mypy.error_formatter import ErrorFormatter from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode, mypy_error_codes -from mypy.message_registry import ErrorMessage +from mypy.nodes import Context from mypy.options import Options from mypy.scope import Scope +from mypy.types import Type from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file from mypy.version import __version__ as mypy_version @@ -20,7 +23,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. @@ -38,8 +41,6 @@ codes.OVERRIDE, } -allowed_duplicates: Final = ["@overload", "Got:", "Expected:"] - BASE_RTD_URL: Final = "https://mypy.rtfd.io/en/stable/_refs.html#code" # Keep track of the original error code when the error code of a message is changed. @@ -93,9 +94,6 @@ class ErrorInfo: # Only report this particular messages once per program. only_once = False - # Do not remove duplicate copies of this message (ignored if only_once is True). - allow_dups = False - # Actual origin of the error message as tuple (path, line number, end line number) # If end line number is unknown, use line number. origin: tuple[str, Iterable[int]] @@ -107,6 +105,10 @@ class ErrorInfo: # by mypy daemon) hidden = False + # For notes, specifies (optionally) the error this note is attached to. This is used to + # simplify error code matching and de-duplication logic for complex multi-line notes. + parent_error: ErrorInfo | None = None + def __init__( self, import_ctx: list[tuple[str, int]], @@ -124,10 +126,10 @@ def __init__( code: ErrorCode | None, blocker: bool, only_once: bool, - allow_dups: bool, origin: tuple[str, Iterable[int]] | None = None, target: str | None = None, priority: int = 0, + parent_error: ErrorInfo | None = None, ) -> None: self.import_ctx = import_ctx self.file = file @@ -143,17 +145,17 @@ def __init__( self.code = code self.blocker = blocker self.only_once = only_once - self.allow_dups = allow_dups self.origin = origin or (file, [line]) self.target = target self.priority = priority + if parent_error is not None: + assert severity == "note", "Only notes can specify parent errors" + self.parent_error = parent_error # Type used internally to represent errors: -# (path, line, column, end_line, end_column, severity, message, allow_dups, code) -ErrorTuple: _TypeAlias = Tuple[ - Optional[str], int, int, int, int, str, str, bool, Optional[ErrorCode] -] +# (path, line, column, end_line, end_column, severity, message, code) +ErrorTuple: _TypeAlias = tuple[Optional[str], int, int, int, int, str, str, Optional[ErrorCode]] class ErrorWatcher: @@ -165,19 +167,27 @@ class ErrorWatcher: out by one of the ErrorWatcher instances. """ + # public attribute for the special treatment of `reveal_type` by + # `MessageBuilder.reveal_type`: + filter_revealed_type: bool + def __init__( self, errors: Errors, *, filter_errors: bool | Callable[[str, ErrorInfo], bool] = False, save_filtered_errors: bool = False, + filter_deprecated: bool = False, + filter_revealed_type: bool = False, ) -> None: self.errors = errors self._has_new_errors = False self._filter = filter_errors + self._filter_deprecated = filter_deprecated + self.filter_revealed_type = filter_revealed_type self._filtered: list[ErrorInfo] | None = [] if save_filtered_errors else None - def __enter__(self) -> ErrorWatcher: + def __enter__(self) -> Self: self.errors._watchers.append(self) return self @@ -194,6 +204,11 @@ 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: + # Deprecated is not a type error, so it is handled on opt-in basis here. + if not self._filter_deprecated: + return False + self._has_new_errors = True if isinstance(self._filter, bool): should_filter = self._filter @@ -214,6 +229,101 @@ def filtered_errors(self) -> list[ErrorInfo]: return self._filtered +class IterationDependentErrors: + """An `IterationDependentErrors` instance serves to collect the `unreachable`, + `redundant-expr`, and `redundant-casts` errors, as well as the revealed types, + handled by the individual `IterationErrorWatcher` instances sequentially applied to + the same code section.""" + + # One set of `unreachable`, `redundant-expr`, and `redundant-casts` errors per + # iteration step. Meaning of the tuple items: ErrorCode, message, line, column, + # end_line, end_column. + uselessness_errors: list[set[tuple[ErrorCode, str, int, int, int, int]]] + + # One set of unreachable line numbers per iteration step. Not only the lines where + # the error report occurs but really all unreachable lines. + unreachable_lines: list[set[int]] + + # One list of revealed types for each `reveal_type` statement. Each created list + # can grow during the iteration. Meaning of the tuple items: line, column, + # end_line, end_column: + revealed_types: dict[tuple[int, int, int | None, int | None], list[Type]] + + def __init__(self) -> None: + self.uselessness_errors = [] + self.unreachable_lines = [] + self.revealed_types = defaultdict(list) + + def yield_uselessness_error_infos(self) -> Iterator[tuple[str, Context, ErrorCode]]: + """Report only those `unreachable`, `redundant-expr`, and `redundant-casts` + errors that could not be ruled out in any iteration step.""" + + persistent_uselessness_errors = set() + for candidate in set(chain(*self.uselessness_errors)): + if all( + (candidate in errors) or (candidate[2] in lines) + for errors, lines in zip(self.uselessness_errors, self.unreachable_lines) + ): + persistent_uselessness_errors.add(candidate) + for error_info in persistent_uselessness_errors: + context = Context(line=error_info[2], column=error_info[3]) + context.end_line = error_info[4] + context.end_column = error_info[5] + yield error_info[1], context, error_info[0] + + def yield_revealed_type_infos(self) -> Iterator[tuple[list[Type], Context]]: + """Yield all types revealed in at least one iteration step.""" + + for note_info, types in self.revealed_types.items(): + context = Context(line=note_info[0], column=note_info[1]) + context.end_line = note_info[2] + context.end_column = note_info[3] + yield types, context + + +class IterationErrorWatcher(ErrorWatcher): + """Error watcher that filters and separately collects `unreachable` errors, + `redundant-expr` and `redundant-casts` errors, and revealed types when analysing + code sections iteratively to help avoid making too-hasty reports.""" + + iteration_dependent_errors: IterationDependentErrors + + def __init__( + self, + errors: Errors, + iteration_dependent_errors: IterationDependentErrors, + *, + filter_errors: bool | Callable[[str, ErrorInfo], bool] = False, + save_filtered_errors: bool = False, + filter_deprecated: bool = False, + ) -> None: + super().__init__( + errors, + filter_errors=filter_errors, + save_filtered_errors=save_filtered_errors, + filter_deprecated=filter_deprecated, + ) + self.iteration_dependent_errors = iteration_dependent_errors + iteration_dependent_errors.uselessness_errors.append(set()) + iteration_dependent_errors.unreachable_lines.append(set()) + + def on_error(self, file: str, info: ErrorInfo) -> bool: + """Filter out the "iteration-dependent" errors and notes and store their + information to handle them after iteration is completed.""" + + iter_errors = self.iteration_dependent_errors + + if info.code in (codes.UNREACHABLE, codes.REDUNDANT_EXPR, codes.REDUNDANT_CAST): + iter_errors.uselessness_errors[-1].add( + (info.code, info.message, info.line, info.column, info.end_line, info.end_column) + ) + if info.code == codes.UNREACHABLE: + iter_errors.unreachable_lines[-1].update(range(info.line, info.end_line + 1)) + return True + + return super().on_error(file, info) + + class Errors: """Container for compile errors. @@ -265,7 +375,7 @@ class Errors: show_column_numbers: bool = False # Set to True to show end line and end column in error messages. - # Ths implies `show_column_numbers`. + # This implies `show_column_numbers`. show_error_end: bool = False # Set to True to show absolute file paths in error messages. @@ -281,7 +391,7 @@ class Errors: # in some cases to avoid reporting huge numbers of errors. seen_import_error = False - _watchers: list[ErrorWatcher] = [] + _watchers: list[ErrorWatcher] def __init__( self, @@ -312,6 +422,7 @@ def initialize(self) -> None: self.scope = None self.target_module = None self.seen_import_error = False + self._watchers = [] def reset(self) -> None: self.initialize() @@ -386,12 +497,12 @@ def report( severity: str = "error", file: str | None = None, only_once: bool = False, - allow_dups: bool = False, origin_span: Iterable[int] | None = None, offset: int = 0, end_line: int | None = None, end_column: int | None = None, - ) -> None: + parent_error: ErrorInfo | None = None, + ) -> ErrorInfo: """Report message at the given line using the current error context. Args: @@ -403,10 +514,10 @@ def report( severity: 'error' or 'note' file: if non-None, override current file as context only_once: if True, only report this exact message once per build - allow_dups: if True, allow duplicate copies of this message (ignored if only_once) origin_span: if non-None, override current context as origin (type: ignores have effect here) end_line: if non-None, override current context as end + parent_error: an error this note is attached to (for notes only). """ if self.scope: type = self.scope.current_type_name() @@ -436,6 +547,7 @@ def report( if end_line is None: end_line = line + code = code or (parent_error.code if parent_error else None) code = code or (codes.MISC if not blocker else None) info = ErrorInfo( @@ -453,11 +565,12 @@ def report( code=code, blocker=blocker, only_once=only_once, - allow_dups=allow_dups, origin=(self.file, origin_span), target=self.current_target(), + parent_error=parent_error, ) self.add_error_info(info) + return info def _add_error_info(self, file: str, info: ErrorInfo) -> None: assert file not in self.flushed_files @@ -473,18 +586,19 @@ def _add_error_info(self, file: str, info: ErrorInfo) -> None: if info.code in (IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND): self.seen_import_error = True + def get_watchers(self) -> Iterator[ErrorWatcher]: + """Yield the `ErrorWatcher` stack from top to bottom.""" + i = len(self._watchers) + while i > 0: + i -= 1 + yield self._watchers[i] + def _filter_error(self, file: str, info: ErrorInfo) -> bool: """ process ErrorWatcher stack from top to bottom, stopping early if error needs to be filtered out """ - i = len(self._watchers) - while i > 0: - i -= 1 - w = self._watchers[i] - if w.on_error(file, info): - return True - return False + return any(w.on_error(file, info) for w in self.get_watchers()) def add_error_info(self, info: ErrorInfo) -> None: file, lines = info.origin @@ -500,10 +614,13 @@ def add_error_info(self, info: ErrorInfo) -> None: # line == end_line for most nodes, so we only loop once. for scope_line in lines: if self.is_ignored_error(scope_line, info, self.ignored_lines[file]): + err_code = info.code or codes.MISC + if not self.is_error_code_enabled(err_code): + # Error code is disabled - don't mark the current + # "type: ignore" comment as used. + return # Annotation requests us to ignore all errors on this line. - self.used_ignored_lines[file][scope_line].append( - (info.code or codes.MISC).code - ) + self.used_ignored_lines[file][scope_line].append(err_code.code) return if file in self.ignored_files: return @@ -553,7 +670,6 @@ def add_error_info(self, info: ErrorInfo) -> None: code=None, blocker=False, only_once=False, - allow_dups=False, ) self._add_error_info(file, note) if ( @@ -582,7 +698,6 @@ def add_error_info(self, info: ErrorInfo) -> None: code=info.code, blocker=False, only_once=True, - allow_dups=False, priority=20, ) self._add_error_info(file, info) @@ -622,7 +737,6 @@ def report_hidden_errors(self, info: ErrorInfo) -> None: code=None, blocker=False, only_once=True, - allow_dups=False, origin=info.origin, target=info.target, ) @@ -692,8 +806,8 @@ def generate_unused_ignore_errors(self, file: str) -> None: continue if codes.UNUSED_IGNORE.code in ignored_codes: continue - used_ignored_codes = used_ignored_lines[line] - unused_ignored_codes = set(ignored_codes) - set(used_ignored_codes) + used_ignored_codes = set(used_ignored_lines[line]) + unused_ignored_codes = [c for c in ignored_codes if c not in used_ignored_codes] # `ignore` is used if not ignored_codes and used_ignored_codes: continue @@ -703,7 +817,7 @@ def generate_unused_ignore_errors(self, file: str) -> None: # Display detail only when `ignore[...]` specifies more than one error code unused_codes_message = "" if len(ignored_codes) > 1 and unused_ignored_codes: - unused_codes_message = f"[{', '.join(sorted(unused_ignored_codes))}]" + unused_codes_message = f"[{', '.join(unused_ignored_codes)}]" message = f'Unused "type: ignore{unused_codes_message}" comment' for unused in unused_ignored_codes: narrower = set(used_ignored_codes) & codes.sub_code_map[unused] @@ -725,7 +839,8 @@ def generate_unused_ignore_errors(self, file: str) -> None: code=codes.UNUSED_IGNORE, blocker=False, only_once=False, - allow_dups=False, + origin=(self.file, [line]), + target=self.target_module, ) self._add_error_info(file, info) @@ -777,7 +892,8 @@ def generate_ignore_without_code_errors( code=codes.IGNORE_WITHOUT_CODE, blocker=False, only_once=False, - allow_dups=False, + origin=(self.file, [line]), + target=self.target_module, ) self._add_error_info(file, info) @@ -817,7 +933,8 @@ def prefer_simple_messages(self) -> bool: if self.file in self.ignored_files: # Errors ignored, so no point generating fancy messages return True - for _watcher in self._watchers: + if self._watchers: + _watcher = self._watchers[-1] if _watcher._filter is True and _watcher._filtered is None: # Errors are filtered return True @@ -834,7 +951,7 @@ def raise_error(self, use_stdout: bool = True) -> NoReturn: self.new_messages(), use_stdout=use_stdout, module_with_blocker=self.blocker_module() ) - def format_messages( + def format_messages_default( self, error_tuples: list[ErrorTuple], source_lines: list[str] | None ) -> list[str]: """Return a string list that represents the error messages. @@ -844,17 +961,7 @@ def format_messages( severity 'error'). """ a: list[str] = [] - for ( - file, - line, - column, - end_line, - end_column, - severity, - message, - allow_dups, - code, - ) in error_tuples: + for file, line, column, end_line, end_column, severity, message, code in error_tuples: s = "" if file is not None: if self.options.show_column_numbers and line >= 0 and column >= 0: @@ -896,31 +1003,54 @@ def format_messages( marker = "^" if end_line == line and end_column > column: marker = f'^{"~" * (end_column - column - 1)}' + elif end_line != line: + # just highlight the first line instead + marker = f'^{"~" * (len(source_line_expanded) - column - 1)}' a.append(" " * (DEFAULT_SOURCE_OFFSET + column) + marker) return a - def file_messages(self, path: str, formatter: ErrorFormatter | None = None) -> list[str]: - """Return a string list of new error messages from a given file. - - Use a form suitable for displaying to the user. - """ + def file_messages(self, path: str) -> list[ErrorTuple]: + """Return an error tuple list of new error messages from a given file.""" if path not in self.error_info_map: return [] error_info = self.error_info_map[path] error_info = [info for info in error_info if not info.hidden] - error_tuples = self.render_messages(self.sort_messages(error_info)) - error_tuples = self.remove_duplicates(error_tuples) + error_info = self.remove_duplicates(self.sort_messages(error_info)) + return self.render_messages(error_info) + def format_messages( + self, path: str, error_tuples: list[ErrorTuple], formatter: ErrorFormatter | None = None + ) -> list[str]: + """Return a string list of new error messages from a given file. + + Use a form suitable for displaying to the user. + """ + self.flushed_files.add(path) if formatter is not None: errors = create_errors(error_tuples) return [formatter.report_error(err) for err in errors] - self.flushed_files.add(path) source_lines = None if self.options.pretty and self.read_source: - source_lines = self.read_source(path) - return self.format_messages(error_tuples, source_lines) + # 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_default(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. @@ -932,7 +1062,8 @@ def new_messages(self) -> list[str]: msgs = [] for path in self.error_info_map.keys(): if path not in self.flushed_files: - msgs.extend(self.file_messages(path)) + error_tuples = self.file_messages(path) + msgs.extend(self.format_messages(path, error_tuples)) return msgs def targets(self) -> set[str]: @@ -946,7 +1077,7 @@ def targets(self) -> set[str]: def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: """Translate the messages into a sequence of tuples. - Each tuple is of form (path, line, col, severity, message, allow_dups, code). + Each tuple is of form (path, line, col, severity, message, code). The rendered sequence includes information about error contexts. The path item may be None. If the line item is negative, the line number is not defined for the tuple. @@ -975,9 +1106,7 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: # Remove prefix to ignore from path (if present) to # simplify path. path = remove_path_prefix(path, self.ignore_prefix) - result.append( - (None, -1, -1, -1, -1, "note", fmt.format(path, line), e.allow_dups, None) - ) + result.append((None, -1, -1, -1, -1, "note", fmt.format(path, line), None)) i -= 1 file = self.simplify_path(e.file) @@ -988,22 +1117,10 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: elif e.function_or_member != prev_function_or_member or e.type != prev_type: if e.function_or_member is None: if e.type is None: - result.append( - (file, -1, -1, -1, -1, "note", "At top level:", e.allow_dups, None) - ) + result.append((file, -1, -1, -1, -1, "note", "At top level:", None)) else: result.append( - ( - file, - -1, - -1, - -1, - -1, - "note", - f'In class "{e.type}":', - e.allow_dups, - None, - ) + (file, -1, -1, -1, -1, "note", f'In class "{e.type}":', None) ) else: if e.type is None: @@ -1016,7 +1133,6 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: -1, "note", f'In function "{e.function_or_member}":', - e.allow_dups, None, ) ) @@ -1032,48 +1148,18 @@ def render_messages(self, errors: list[ErrorInfo]) -> list[ErrorTuple]: 'In member "{}" of class "{}":'.format( e.function_or_member, e.type ), - e.allow_dups, None, ) ) elif e.type != prev_type: if e.type is None: - result.append( - (file, -1, -1, -1, -1, "note", "At top level:", e.allow_dups, None) - ) + result.append((file, -1, -1, -1, -1, "note", "At top level:", None)) else: - result.append( - (file, -1, -1, -1, -1, "note", f'In class "{e.type}":', e.allow_dups, None) - ) + result.append((file, -1, -1, -1, -1, "note", f'In class "{e.type}":', None)) - if isinstance(e.message, ErrorMessage): - result.append( - ( - file, - e.line, - e.column, - e.end_line, - e.end_column, - e.severity, - e.message.value, - e.allow_dups, - e.code, - ) - ) - else: - result.append( - ( - file, - e.line, - e.column, - e.end_line, - e.end_column, - e.severity, - e.message, - e.allow_dups, - e.code, - ) - ) + result.append( + (file, e.line, e.column, e.end_line, e.end_column, e.severity, e.message, e.code) + ) prev_import_context = e.import_ctx prev_function_or_member = e.function_or_member @@ -1134,40 +1220,24 @@ def sort_within_context(self, errors: list[ErrorInfo]) -> list[ErrorInfo]: result.extend(a) return result - def remove_duplicates(self, errors: list[ErrorTuple]) -> list[ErrorTuple]: - """Remove duplicates from a sorted error list.""" - res: list[ErrorTuple] = [] - i = 0 - while i < len(errors): - dup = False - # Use slightly special formatting for member conflicts reporting. - conflicts_notes = False - j = i - 1 - # Find duplicates, unless duplicates are allowed. - if not errors[i][7]: - while j >= 0 and errors[j][0] == errors[i][0]: - if errors[j][6].strip() == "Got:": - conflicts_notes = True - j -= 1 - j = i - 1 - while j >= 0 and errors[j][0] == errors[i][0] and errors[j][1] == errors[i][1]: - if ( - errors[j][5] == errors[i][5] - and - # Allow duplicate notes in overload conflicts reporting. - not ( - (errors[i][5] == "note" and errors[i][6].strip() in allowed_duplicates) - or (errors[i][6].strip().startswith("def ") and conflicts_notes) - ) - and errors[j][6] == errors[i][6] - ): # ignore column - dup = True - break - j -= 1 - if not dup: - res.append(errors[i]) - i += 1 - return res + def remove_duplicates(self, errors: list[ErrorInfo]) -> list[ErrorInfo]: + filtered_errors = [] + seen_by_line: defaultdict[int, set[tuple[str, str]]] = defaultdict(set) + removed = set() + for err in errors: + if err.parent_error is not None: + # Notes with specified parent are removed together with error below. + filtered_errors.append(err) + elif (err.severity, err.message) not in seen_by_line[err.line]: + filtered_errors.append(err) + seen_by_line[err.line].add((err.severity, err.message)) + else: + removed.add(err) + return [ + err + for err in filtered_errors + if err.parent_error is None or err.parent_error not in removed + ] class CompileError(Exception): @@ -1308,7 +1378,7 @@ def __init__( # (file_path, line, column) -_ErrorLocation = Tuple[str, int, int] +_ErrorLocation = tuple[str, int, int] def create_errors(error_tuples: list[ErrorTuple]) -> list[MypyError]: @@ -1316,7 +1386,7 @@ def create_errors(error_tuples: list[ErrorTuple]) -> list[MypyError]: latest_error_at_location: dict[_ErrorLocation, MypyError] = {} for error_tuple in error_tuples: - file_path, line, column, _, _, severity, message, _, errorcode = error_tuple + file_path, line, column, _, _, severity, message, errorcode = error_tuple if file_path is None: continue diff --git a/mypy/evalexpr.py b/mypy/evalexpr.py index e39c5840d47a8..218d50e37ec36 100644 --- a/mypy/evalexpr.py +++ b/mypy/evalexpr.py @@ -75,6 +75,9 @@ def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> object: def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> object: return o.expr.accept(self) + def visit_type_form_expr(self, o: mypy.nodes.TypeFormExpr) -> object: + return UNKNOWN + def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> object: return o.expr.accept(self) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index b2040ec074c32..891ea4d89a806 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload +from collections.abc import Iterable, Mapping +from typing import Final, TypeVar, cast, overload from mypy.nodes import ARG_STAR, FakeInfo, Var from mypy.state import state @@ -121,7 +122,7 @@ def freshen_function_type_vars(callee: F) -> F: """Substitute fresh type variables for generic function type variables.""" if isinstance(callee, CallableType): if not callee.is_generic(): - return cast(F, callee) + return callee tvs = [] tvmap: dict[TypeVarId, Type] = {} for v in callee.variables: @@ -181,7 +182,7 @@ class ExpandTypeVisitor(TrivialSyntheticTypeTranslator): def __init__(self, variables: Mapping[TypeVarId, Type]) -> None: super().__init__() self.variables = variables - self.recursive_tvar_guard: dict[TypeVarId, Type | None] = {} + self.recursive_tvar_guard: dict[TypeVarId, Type | None] | None = None def visit_unbound_type(self, t: UnboundType) -> Type: return t @@ -208,7 +209,10 @@ def visit_erased_type(self, t: ErasedType) -> Type: return t def visit_instance(self, t: Instance) -> Type: - args = self.expand_types_with_unpack(list(t.args)) + if len(t.args) == 0: + return t + + args = self.expand_type_tuple_with_unpack(t.args) if isinstance(t.type, FakeInfo): # The type checker expands function definitions and bodies @@ -225,6 +229,8 @@ def visit_instance(self, t: Instance) -> Type: if isinstance(arg, UnpackType): unpacked = get_proper_type(arg.type) if isinstance(unpacked, Instance): + # TODO: this and similar asserts below may be unsafe because get_proper_type() + # may be called during semantic analysis before all invalid types are removed. assert unpacked.type.fullname == "builtins.tuple" args = list(unpacked.args) return t.copy_modified(args=args) @@ -240,12 +246,14 @@ def visit_type_var(self, t: TypeVarType) -> Type: # If I try to remove this special-casing ~40 tests fail on reveal_type(). return repl.copy_modified(last_known_value=None) if isinstance(repl, TypeVarType) and repl.has_default(): + if self.recursive_tvar_guard is None: + self.recursive_tvar_guard = {} if (tvar_id := repl.id) in self.recursive_tvar_guard: return self.recursive_tvar_guard[tvar_id] or repl self.recursive_tvar_guard[tvar_id] = None - repl = repl.accept(self) - if isinstance(repl, TypeVarType): - repl.default = repl.default.accept(self) + repl.default = repl.default.accept(self) + expanded = repl.accept(self) # Note: `expanded is repl` may be true. + repl = repl if isinstance(expanded, TypeVarType) else expanded self.recursive_tvar_guard[tvar_id] = repl return repl @@ -332,10 +340,7 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l var_arg_type = get_proper_type(var_arg.type) new_unpack: Type - if isinstance(var_arg_type, Instance): - # we have something like Unpack[Tuple[Any, ...]] - new_unpack = UnpackType(var_arg.type.accept(self)) - elif isinstance(var_arg_type, TupleType): + if isinstance(var_arg_type, TupleType): # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] expanded_tuple = var_arg_type.accept(self) assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) @@ -347,6 +352,11 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l fallback = var_arg_type.tuple_fallback expanded_items = self.expand_unpack(var_arg) new_unpack = UnpackType(TupleType(expanded_items, fallback)) + # Since get_proper_type() may be called in semanal.py before callable + # normalization happens, we need to also handle non-normal cases here. + elif isinstance(var_arg_type, Instance): + # we have something like Unpack[Tuple[Any, ...]] + new_unpack = UnpackType(var_arg.type.accept(self)) else: # We have invalid type in Unpack. This can happen when expanding aliases # to Callable[[*Invalid], Ret] @@ -426,7 +436,7 @@ def visit_overloaded(self, t: Overloaded) -> Type: items.append(new_item) return Overloaded(items) - def expand_types_with_unpack(self, typs: Sequence[Type]) -> list[Type]: + def expand_type_list_with_unpack(self, typs: list[Type]) -> list[Type]: """Expands a list of types that has an unpack.""" items: list[Type] = [] for item in typs: @@ -436,8 +446,19 @@ def expand_types_with_unpack(self, typs: Sequence[Type]) -> list[Type]: items.append(item.accept(self)) return items + def expand_type_tuple_with_unpack(self, typs: tuple[Type, ...]) -> list[Type]: + """Expands a tuple of types that has an unpack.""" + # Micro-optimization: Specialized variant of expand_type_list_with_unpack + items: list[Type] = [] + for item in typs: + if isinstance(item, UnpackType) and isinstance(item.type, TypeVarTupleType): + items.extend(self.expand_unpack(item)) + else: + items.append(item.accept(self)) + return items + def visit_tuple_type(self, t: TupleType) -> Type: - items = self.expand_types_with_unpack(t.items) + items = self.expand_type_list_with_unpack(t.items) if len(items) == 1: # Normalize Tuple[*Tuple[X, ...]] -> Tuple[X, ...] item = items[0] @@ -500,12 +521,14 @@ def visit_type_type(self, t: TypeType) -> Type: # union of instances or Any). Sadly we can't report errors # here yet. item = t.item.accept(self) - return TypeType.make_normalized(item) + return TypeType.make_normalized(item, is_type_form=t.is_type_form) def visit_type_alias_type(self, t: TypeAliasType) -> Type: # Target of the type alias cannot contain type variables (not bound by the type # alias itself), so we just expand the arguments. - args = self.expand_types_with_unpack(t.args) + if len(t.args) == 0: + return t + args = self.expand_type_list_with_unpack(t.args) # TODO: normalize if target is Tuple, and args are [*tuple[X, ...]]? return t.copy_modified(args=args) diff --git a/mypy/exportjson.py b/mypy/exportjson.py new file mode 100644 index 0000000000000..dfc1cf5abbc6b --- /dev/null +++ b/mypy/exportjson.py @@ -0,0 +1,578 @@ +"""Tool to convert mypy cache file to a JSON format (print to stdout). + +Usage: + python -m mypy.exportjson .mypy_cache/.../my_module.data.ff + +The idea is to make caches introspectable once we've switched to a binary +cache format and removed support for the older JSON cache format. + +This is primarily to support existing use cases that need to inspect +cache files, and to support debugging mypy caching issues. This means that +this doesn't necessarily need to be kept 1:1 up to date with changes in the +binary cache format (to simplify maintenance -- we don't want this to slow +down mypy development). +""" + +import argparse +import json +import sys +from typing import Any, Union +from typing_extensions import TypeAlias as _TypeAlias + +from librt.internal import ReadBuffer + +from mypy.nodes import ( + FUNCBASE_FLAGS, + FUNCDEF_FLAGS, + VAR_FLAGS, + ClassDef, + DataclassTransformSpec, + Decorator, + FuncDef, + MypyFile, + OverloadedFuncDef, + OverloadPart, + ParamSpecExpr, + SymbolNode, + SymbolTable, + SymbolTableNode, + TypeAlias, + TypeInfo, + TypeVarExpr, + TypeVarTupleExpr, + Var, + get_flags, + node_kinds, +) +from mypy.types import ( + NOT_READY, + AnyType, + CallableType, + ExtraAttrs, + Instance, + LiteralType, + NoneType, + Overloaded, + Parameters, + ParamSpecType, + TupleType, + Type, + TypeAliasType, + TypedDictType, + TypeType, + TypeVarTupleType, + TypeVarType, + UnboundType, + UninhabitedType, + UnionType, + UnpackType, + get_proper_type, +) + +Json: _TypeAlias = Union[dict[str, Any], str] + + +class Config: + def __init__(self, *, implicit_names: bool = True) -> None: + self.implicit_names = implicit_names + + +def convert_binary_cache_to_json(data: bytes, *, implicit_names: bool = True) -> Json: + tree = MypyFile.read(ReadBuffer(data)) + return convert_mypy_file_to_json(tree, Config(implicit_names=implicit_names)) + + +def convert_mypy_file_to_json(self: MypyFile, cfg: Config) -> Json: + return { + ".class": "MypyFile", + "_fullname": self._fullname, + "names": convert_symbol_table(self.names, cfg), + "is_stub": self.is_stub, + "path": self.path, + "is_partial_stub_package": self.is_partial_stub_package, + "future_import_flags": sorted(self.future_import_flags), + } + + +def convert_symbol_table(self: SymbolTable, cfg: Config) -> Json: + data: dict[str, Any] = {".class": "SymbolTable"} + for key, value in self.items(): + # Skip __builtins__: it's a reference to the builtins + # module that gets added to every module by + # SemanticAnalyzerPass2.visit_file(), but it shouldn't be + # accessed by users of the module. + if key == "__builtins__" or value.no_serialize: + continue + if not cfg.implicit_names and key in { + "__spec__", + "__package__", + "__file__", + "__doc__", + "__annotations__", + "__name__", + }: + continue + data[key] = convert_symbol_table_node(value, cfg) + return data + + +def convert_symbol_table_node(self: SymbolTableNode, cfg: Config) -> Json: + data: dict[str, Any] = {".class": "SymbolTableNode", "kind": node_kinds[self.kind]} + if self.module_hidden: + data["module_hidden"] = True + if not self.module_public: + data["module_public"] = False + if self.implicit: + data["implicit"] = True + if self.plugin_generated: + data["plugin_generated"] = True + if self.cross_ref: + data["cross_ref"] = self.cross_ref + elif self.node is not None: + data["node"] = convert_symbol_node(self.node, cfg) + return data + + +def convert_symbol_node(self: SymbolNode, cfg: Config) -> Json: + if isinstance(self, FuncDef): + return convert_func_def(self) + elif isinstance(self, OverloadedFuncDef): + return convert_overloaded_func_def(self) + elif isinstance(self, Decorator): + return convert_decorator(self) + elif isinstance(self, Var): + return convert_var(self) + elif isinstance(self, TypeInfo): + return convert_type_info(self, cfg) + elif isinstance(self, TypeAlias): + return convert_type_alias(self) + elif isinstance(self, TypeVarExpr): + return convert_type_var_expr(self) + elif isinstance(self, ParamSpecExpr): + return convert_param_spec_expr(self) + elif isinstance(self, TypeVarTupleExpr): + return convert_type_var_tuple_expr(self) + return {"ERROR": f"{type(self)!r} unrecognized"} + + +def convert_func_def(self: FuncDef) -> Json: + return { + ".class": "FuncDef", + "name": self._name, + "fullname": self._fullname, + "arg_names": self.arg_names, + "arg_kinds": [int(x.value) for x in self.arg_kinds], + "type": None if self.type is None else convert_type(self.type), + "flags": get_flags(self, FUNCDEF_FLAGS), + "abstract_status": self.abstract_status, + # TODO: Do we need expanded, original_def? + "dataclass_transform_spec": ( + None + if self.dataclass_transform_spec is None + else convert_dataclass_transform_spec(self.dataclass_transform_spec) + ), + "deprecated": self.deprecated, + "original_first_arg": self.original_first_arg, + } + + +def convert_dataclass_transform_spec(self: DataclassTransformSpec) -> Json: + return { + "eq_default": self.eq_default, + "order_default": self.order_default, + "kw_only_default": self.kw_only_default, + "frozen_default": self.frozen_default, + "field_specifiers": list(self.field_specifiers), + } + + +def convert_overloaded_func_def(self: OverloadedFuncDef) -> Json: + return { + ".class": "OverloadedFuncDef", + "items": [convert_overload_part(i) for i in self.items], + "type": None if self.type is None else convert_type(self.type), + "fullname": self._fullname, + "impl": None if self.impl is None else convert_overload_part(self.impl), + "flags": get_flags(self, FUNCBASE_FLAGS), + "deprecated": self.deprecated, + "setter_index": self.setter_index, + } + + +def convert_overload_part(self: OverloadPart) -> Json: + if isinstance(self, FuncDef): + return convert_func_def(self) + else: + return convert_decorator(self) + + +def convert_decorator(self: Decorator) -> Json: + return { + ".class": "Decorator", + "func": convert_func_def(self.func), + "var": convert_var(self.var), + "is_overload": self.is_overload, + } + + +def convert_var(self: Var) -> Json: + data: dict[str, Any] = { + ".class": "Var", + "name": self._name, + "fullname": self._fullname, + "type": None if self.type is None else convert_type(self.type), + "setter_type": None if self.setter_type is None else convert_type(self.setter_type), + "flags": get_flags(self, VAR_FLAGS), + } + if self.final_value is not None: + data["final_value"] = self.final_value + return data + + +def convert_type_info(self: TypeInfo, cfg: Config) -> Json: + data = { + ".class": "TypeInfo", + "module_name": self.module_name, + "fullname": self.fullname, + "names": convert_symbol_table(self.names, cfg), + "defn": convert_class_def(self.defn), + "abstract_attributes": self.abstract_attributes, + "type_vars": self.type_vars, + "has_param_spec_type": self.has_param_spec_type, + "bases": [convert_type(b) for b in self.bases], + "mro": self._mro_refs, + "_promote": [convert_type(p) for p in self._promote], + "alt_promote": None if self.alt_promote is None else convert_type(self.alt_promote), + "declared_metaclass": ( + None if self.declared_metaclass is None else convert_type(self.declared_metaclass) + ), + "metaclass_type": ( + None if self.metaclass_type is None else convert_type(self.metaclass_type) + ), + "tuple_type": None if self.tuple_type is None else convert_type(self.tuple_type), + "typeddict_type": ( + None if self.typeddict_type is None else convert_typeddict_type(self.typeddict_type) + ), + "flags": get_flags(self, TypeInfo.FLAGS), + "metadata": self.metadata, + "slots": sorted(self.slots) if self.slots is not None else None, + "deletable_attributes": self.deletable_attributes, + "self_type": convert_type(self.self_type) if self.self_type is not None else None, + "dataclass_transform_spec": ( + convert_dataclass_transform_spec(self.dataclass_transform_spec) + if self.dataclass_transform_spec is not None + else None + ), + "deprecated": self.deprecated, + } + return data + + +def convert_class_def(self: ClassDef) -> Json: + return { + ".class": "ClassDef", + "name": self.name, + "fullname": self.fullname, + "type_vars": [convert_type(v) for v in self.type_vars], + } + + +def convert_type_alias(self: TypeAlias) -> Json: + data: Json = { + ".class": "TypeAlias", + "fullname": self._fullname, + "module": self.module, + "target": convert_type(self.target), + "alias_tvars": [convert_type(v) for v in self.alias_tvars], + "no_args": self.no_args, + "normalized": self.normalized, + "python_3_12_type_alias": self.python_3_12_type_alias, + } + return data + + +def convert_type_var_expr(self: TypeVarExpr) -> Json: + return { + ".class": "TypeVarExpr", + "name": self._name, + "fullname": self._fullname, + "values": [convert_type(t) for t in self.values], + "upper_bound": convert_type(self.upper_bound), + "default": convert_type(self.default), + "variance": self.variance, + } + + +def convert_param_spec_expr(self: ParamSpecExpr) -> Json: + return { + ".class": "ParamSpecExpr", + "name": self._name, + "fullname": self._fullname, + "upper_bound": convert_type(self.upper_bound), + "default": convert_type(self.default), + "variance": self.variance, + } + + +def convert_type_var_tuple_expr(self: TypeVarTupleExpr) -> Json: + return { + ".class": "TypeVarTupleExpr", + "name": self._name, + "fullname": self._fullname, + "upper_bound": convert_type(self.upper_bound), + "tuple_fallback": convert_type(self.tuple_fallback), + "default": convert_type(self.default), + "variance": self.variance, + } + + +def convert_type(typ: Type) -> Json: + if type(typ) is TypeAliasType: + return convert_type_alias_type(typ) + typ = get_proper_type(typ) + if isinstance(typ, Instance): + return convert_instance(typ) + elif isinstance(typ, AnyType): + return convert_any_type(typ) + elif isinstance(typ, NoneType): + return convert_none_type(typ) + elif isinstance(typ, UnionType): + return convert_union_type(typ) + elif isinstance(typ, TupleType): + return convert_tuple_type(typ) + elif isinstance(typ, CallableType): + return convert_callable_type(typ) + elif isinstance(typ, Overloaded): + return convert_overloaded(typ) + elif isinstance(typ, LiteralType): + return convert_literal_type(typ) + elif isinstance(typ, TypeVarType): + return convert_type_var_type(typ) + elif isinstance(typ, TypeType): + return convert_type_type(typ) + elif isinstance(typ, UninhabitedType): + return convert_uninhabited_type(typ) + elif isinstance(typ, UnpackType): + return convert_unpack_type(typ) + elif isinstance(typ, ParamSpecType): + return convert_param_spec_type(typ) + elif isinstance(typ, TypeVarTupleType): + return convert_type_var_tuple_type(typ) + elif isinstance(typ, Parameters): + return convert_parameters(typ) + elif isinstance(typ, TypedDictType): + return convert_typeddict_type(typ) + elif isinstance(typ, UnboundType): + return convert_unbound_type(typ) + return {"ERROR": f"{type(typ)!r} unrecognized"} + + +def convert_instance(self: Instance) -> Json: + ready = self.type is not NOT_READY + if not self.args and not self.last_known_value and not self.extra_attrs: + if ready: + return self.type.fullname + elif self.type_ref: + return self.type_ref + + data: dict[str, Any] = { + ".class": "Instance", + "type_ref": self.type.fullname if ready else self.type_ref, + "args": [convert_type(arg) for arg in self.args], + } + if self.last_known_value is not None: + data["last_known_value"] = convert_type(self.last_known_value) + data["extra_attrs"] = convert_extra_attrs(self.extra_attrs) if self.extra_attrs else None + return data + + +def convert_extra_attrs(self: ExtraAttrs) -> Json: + return { + ".class": "ExtraAttrs", + "attrs": {k: convert_type(v) for k, v in self.attrs.items()}, + "immutable": sorted(self.immutable), + "mod_name": self.mod_name, + } + + +def convert_type_alias_type(self: TypeAliasType) -> Json: + data: Json = { + ".class": "TypeAliasType", + "type_ref": self.type_ref, + "args": [convert_type(arg) for arg in self.args], + } + return data + + +def convert_any_type(self: AnyType) -> Json: + return { + ".class": "AnyType", + "type_of_any": self.type_of_any, + "source_any": convert_type(self.source_any) if self.source_any is not None else None, + "missing_import_name": self.missing_import_name, + } + + +def convert_none_type(self: NoneType) -> Json: + return {".class": "NoneType"} + + +def convert_union_type(self: UnionType) -> Json: + return { + ".class": "UnionType", + "items": [convert_type(t) for t in self.items], + "uses_pep604_syntax": self.uses_pep604_syntax, + } + + +def convert_tuple_type(self: TupleType) -> Json: + return { + ".class": "TupleType", + "items": [convert_type(t) for t in self.items], + "partial_fallback": convert_type(self.partial_fallback), + "implicit": self.implicit, + } + + +def convert_literal_type(self: LiteralType) -> Json: + return {".class": "LiteralType", "value": self.value, "fallback": convert_type(self.fallback)} + + +def convert_type_var_type(self: TypeVarType) -> Json: + assert not self.id.is_meta_var() + return { + ".class": "TypeVarType", + "name": self.name, + "fullname": self.fullname, + "id": self.id.raw_id, + "namespace": self.id.namespace, + "values": [convert_type(v) for v in self.values], + "upper_bound": convert_type(self.upper_bound), + "default": convert_type(self.default), + "variance": self.variance, + } + + +def convert_callable_type(self: CallableType) -> Json: + return { + ".class": "CallableType", + "arg_types": [convert_type(t) for t in self.arg_types], + "arg_kinds": [int(x.value) for x in self.arg_kinds], + "arg_names": self.arg_names, + "ret_type": convert_type(self.ret_type), + "fallback": convert_type(self.fallback), + "name": self.name, + # We don't serialize the definition (only used for error messages). + "variables": [convert_type(v) for v in self.variables], + "is_ellipsis_args": self.is_ellipsis_args, + "implicit": self.implicit, + "is_bound": self.is_bound, + "type_guard": convert_type(self.type_guard) if self.type_guard is not None else None, + "type_is": convert_type(self.type_is) if self.type_is is not None else None, + "from_concatenate": self.from_concatenate, + "imprecise_arg_kinds": self.imprecise_arg_kinds, + "unpack_kwargs": self.unpack_kwargs, + } + + +def convert_overloaded(self: Overloaded) -> Json: + return {".class": "Overloaded", "items": [convert_type(t) for t in self.items]} + + +def convert_type_type(self: TypeType) -> Json: + return {".class": "TypeType", "item": convert_type(self.item)} + + +def convert_uninhabited_type(self: UninhabitedType) -> Json: + return {".class": "UninhabitedType"} + + +def convert_unpack_type(self: UnpackType) -> Json: + return {".class": "UnpackType", "type": convert_type(self.type)} + + +def convert_param_spec_type(self: ParamSpecType) -> Json: + assert not self.id.is_meta_var() + return { + ".class": "ParamSpecType", + "name": self.name, + "fullname": self.fullname, + "id": self.id.raw_id, + "namespace": self.id.namespace, + "flavor": self.flavor, + "upper_bound": convert_type(self.upper_bound), + "default": convert_type(self.default), + "prefix": convert_type(self.prefix), + } + + +def convert_type_var_tuple_type(self: TypeVarTupleType) -> Json: + assert not self.id.is_meta_var() + return { + ".class": "TypeVarTupleType", + "name": self.name, + "fullname": self.fullname, + "id": self.id.raw_id, + "namespace": self.id.namespace, + "upper_bound": convert_type(self.upper_bound), + "tuple_fallback": convert_type(self.tuple_fallback), + "default": convert_type(self.default), + "min_len": self.min_len, + } + + +def convert_parameters(self: Parameters) -> Json: + return { + ".class": "Parameters", + "arg_types": [convert_type(t) for t in self.arg_types], + "arg_kinds": [int(x.value) for x in self.arg_kinds], + "arg_names": self.arg_names, + "variables": [convert_type(tv) for tv in self.variables], + "imprecise_arg_kinds": self.imprecise_arg_kinds, + } + + +def convert_typeddict_type(self: TypedDictType) -> Json: + return { + ".class": "TypedDictType", + "items": [[n, convert_type(t)] for (n, t) in self.items.items()], + "required_keys": sorted(self.required_keys), + "readonly_keys": sorted(self.readonly_keys), + "fallback": convert_type(self.fallback), + } + + +def convert_unbound_type(self: UnboundType) -> Json: + return { + ".class": "UnboundType", + "name": self.name, + "args": [convert_type(a) for a in self.args], + "expr": self.original_str_expr, + "expr_fallback": self.original_str_fallback, + } + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Convert binary cache files to JSON. " + "Create files in the same directory with extra .json extension." + ) + parser.add_argument( + "path", nargs="+", help="mypy cache data file to convert (.data.ff extension)" + ) + args = parser.parse_args() + fnams: list[str] = args.path + for fnam in fnams: + if not fnam.endswith(".data.ff"): + sys.exit(f"error: Expected .data.ff extension, but got {fnam}") + with open(fnam, "rb") as f: + data = f.read() + json_data = convert_binary_cache_to_json(data) + new_fnam = fnam + ".json" + with open(new_fnam, "w") as f: + json.dump(json_data, f) + print(f"{fnam} -> {new_fnam}") + + +if __name__ == "__main__": + main() diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index 506194a4b285b..6fd43c08dbac2 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -100,7 +100,9 @@ def expr_to_unanalyzed_type( else: raise TypeTranslationError() elif isinstance(expr, IndexExpr): - base = expr_to_unanalyzed_type(expr.base, options, allow_new_syntax, expr) + base = expr_to_unanalyzed_type( + expr.base, options, allow_new_syntax, expr, lookup_qualified=lookup_qualified + ) if isinstance(base, UnboundType): if base.args: raise TypeTranslationError() @@ -124,9 +126,18 @@ def expr_to_unanalyzed_type( # 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) + return expr_to_unanalyzed_type( + args[0], options, allow_new_syntax, expr, lookup_qualified=lookup_qualified + ) base.args = tuple( - expr_to_unanalyzed_type(arg, options, allow_new_syntax, expr, allow_unpack=True) + expr_to_unanalyzed_type( + arg, + options, + allow_new_syntax, + expr, + allow_unpack=True, + lookup_qualified=lookup_qualified, + ) for arg in args ) if not base.args: @@ -141,8 +152,12 @@ def expr_to_unanalyzed_type( ): return UnionType( [ - expr_to_unanalyzed_type(expr.left, options, allow_new_syntax), - expr_to_unanalyzed_type(expr.right, options, allow_new_syntax), + expr_to_unanalyzed_type( + expr.left, options, allow_new_syntax, lookup_qualified=lookup_qualified + ), + expr_to_unanalyzed_type( + expr.right, options, allow_new_syntax, lookup_qualified=lookup_qualified + ), ], uses_pep604_syntax=True, ) @@ -178,12 +193,16 @@ def expr_to_unanalyzed_type( if typ is not default_type: # Two types raise TypeTranslationError() - typ = expr_to_unanalyzed_type(arg, options, allow_new_syntax, expr) + typ = expr_to_unanalyzed_type( + arg, options, allow_new_syntax, expr, lookup_qualified=lookup_qualified + ) continue else: raise TypeTranslationError() elif i == 0: - typ = expr_to_unanalyzed_type(arg, options, allow_new_syntax, expr) + typ = expr_to_unanalyzed_type( + arg, options, allow_new_syntax, expr, lookup_qualified=lookup_qualified + ) elif i == 1: name = _extract_argument_name(arg) else: @@ -192,7 +211,14 @@ def expr_to_unanalyzed_type( elif isinstance(expr, ListExpr): return TypeList( [ - expr_to_unanalyzed_type(t, options, allow_new_syntax, expr, allow_unpack=True) + expr_to_unanalyzed_type( + t, + options, + allow_new_syntax, + expr, + allow_unpack=True, + lookup_qualified=lookup_qualified, + ) for t in expr.items ], line=expr.line, @@ -203,7 +229,9 @@ def expr_to_unanalyzed_type( elif isinstance(expr, BytesExpr): return parse_type_string(expr.value, "builtins.bytes", expr.line, expr.column) elif isinstance(expr, UnaryExpr): - typ = expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax) + typ = expr_to_unanalyzed_type( + expr.expr, options, allow_new_syntax, lookup_qualified=lookup_qualified + ) if isinstance(typ, RawExpressionType): if isinstance(typ.literal_value, int): if expr.op == "-": @@ -225,7 +253,10 @@ def expr_to_unanalyzed_type( return EllipsisType(expr.line) elif allow_unpack and isinstance(expr, StarExpr): return UnpackType( - expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax), from_star_syntax=True + expr_to_unanalyzed_type( + expr.expr, options, allow_new_syntax, lookup_qualified=lookup_qualified + ), + from_star_syntax=True, ) elif isinstance(expr, DictExpr): if not expr.items: @@ -236,12 +267,18 @@ def expr_to_unanalyzed_type( 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) + expr_to_unanalyzed_type( + value, + options, + allow_new_syntax, + expr, + lookup_qualified=lookup_qualified, + ) ) continue raise TypeTranslationError() items[item_name.value] = expr_to_unanalyzed_type( - value, options, allow_new_syntax, expr + value, options, allow_new_syntax, expr, lookup_qualified=lookup_qualified ) result = TypedDictType( items, set(), set(), Instance(MISSING_FALLBACK, ()), expr.line, expr.column diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 726397adb849f..0e7b418d03752 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -1,11 +1,10 @@ from __future__ import annotations -import copy import re import sys import warnings -from typing import Any, Callable, Final, List, Optional, Sequence, TypeVar, Union, cast -from typing_extensions import Literal, overload +from collections.abc import Sequence +from typing import Any, Callable, Final, Literal, Optional, TypeVar, Union, cast, overload from mypy import defaults, errorcodes as codes, message_registry from mypy.errors import Errors @@ -130,25 +129,25 @@ PY_MINOR_VERSION: Final = sys.version_info[1] import ast as ast3 - -# TODO: Index, ExtSlice are deprecated in 3.9. -from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UAdd, UnaryOp, USub +from ast import AST, Attribute, Call, FunctionType, Name, Starred, UAdd, UnaryOp, USub def ast3_parse( source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION ) -> AST: - return ast3.parse( - source, - filename, - mode, - type_comments=True, # This works the magic - feature_version=feature_version, - ) - + # Ignore warnings that look like: + # :1: SyntaxWarning: invalid escape sequence '\.' + # because `source` could be anything, including literals like r'(re\.match)' + with warnings.catch_warnings(): + warnings.simplefilter("ignore", SyntaxWarning) + return ast3.parse( + source, + filename, + mode, + type_comments=True, # This works the magic + feature_version=feature_version, + ) -NamedExpr = ast3.NamedExpr -Constant = ast3.Constant if sys.version_info >= (3, 10): Match = ast3.Match @@ -189,6 +188,13 @@ def ast3_parse( ast_TypeVar = Any ast_TypeVarTuple = Any +if sys.version_info >= (3, 14): + ast_TemplateStr = ast3.TemplateStr + ast_Interpolation = ast3.Interpolation +else: + ast_TemplateStr = Any + ast_Interpolation = Any + N = TypeVar("N", bound=Node) # There is no way to create reasonable fallbacks at this stage, @@ -228,9 +234,12 @@ def parse( assert options.python_version[0] >= 3 feature_version = options.python_version[1] try: - # Disable deprecation warnings about \u + # Disable + # - deprecation warnings for 'invalid escape sequence' (Python 3.11 and below) + # - syntax warnings for 'invalid escape sequence' (3.12+) and 'return in finally' (3.14+) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=DeprecationWarning) + warnings.filterwarnings("ignore", category=SyntaxWarning) ast = ast3_parse(source, fnam, "exec", feature_version=feature_version) tree = ASTConverter( @@ -240,14 +249,30 @@ def parse( strip_function_bodies=strip_function_bodies, path=fnam, ).visit(ast) + + except RecursionError as e: + # For very complex expressions it is possible to hit recursion limit + # before reaching a leaf node. + # Should reject at top level instead at bottom, since bottom would already + # be at the threshold of the recursion limit, and may fail again later. + # E.G. x1+x2+x3+...+xn -> BinOp(left=BinOp(left=BinOp(left=... + try: + # But to prove that is the cause of this particular recursion error, + # try to walk the tree using builtin visitor + ast3.NodeVisitor().visit(ast) + except RecursionError: + errors.report( + -1, -1, "Source expression too complex to parse", blocker=False, code=codes.MISC + ) + + tree = MypyFile([], [], False, {}) + + else: + # re-raise original recursion error if it *can* be unparsed, + # maybe this is some other issue that shouldn't be silenced/misdirected + raise e + except SyntaxError as e: - # alias to please mypyc - is_py38_or_earlier = sys.version_info < (3, 9) - if is_py38_or_earlier and e.filename == "": - # In Python 3.8 and earlier, syntax errors in f-strings have lineno relative to the - # start of the f-string. This would be misleading, as mypy will report the error as the - # lineno within the file. - e.lineno = None message = e.msg if feature_version > sys.version_info.minor and message.startswith("invalid syntax"): python_version_str = f"{options.python_version[0]}.{options.python_version[1]}" @@ -255,7 +280,9 @@ def parse( errors.report( e.lineno if e.lineno is not None else -1, e.offset, - message, + re.sub( + r"^(\s*\w)", lambda m: m.group(1).upper(), message + ), # Standardizing error message blocker=True, code=codes.SYNTAX, ) @@ -389,7 +416,7 @@ def __init__( def note(self, msg: str, line: int, column: int) -> None: self.errors.report(line, column, msg, severity="note", code=codes.SYNTAX) - def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None: + def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool) -> None: if blocker or not self.options.ignore_errors: # Make sure self.errors reflects any type ignores that we have parsed self.errors.set_file_ignored_lines( @@ -414,6 +441,7 @@ def visit(self, node: AST | None) -> Any: method = "visit_" + node.__class__.__name__ visitor = getattr(self, method) self.visitor_cache[typeobj] = visitor + return visitor(node) def set_line(self, node: N, n: AstNode) -> N: @@ -432,7 +460,7 @@ def translate_opt_expr_list(self, l: Sequence[AST | None]) -> list[Expression | return res def translate_expr_list(self, l: Sequence[AST]) -> list[Expression]: - return cast(List[Expression], self.translate_opt_expr_list(l)) + return cast(list[Expression], self.translate_opt_expr_list(l)) def get_lineno(self, node: ast3.expr | ast3.stmt) -> int: if ( @@ -565,7 +593,7 @@ def from_operator(self, op: ast3.operator) -> str: ast3.Is: "is", ast3.IsNot: "is not", ast3.In: "in", - ast3.NotIn: "not in", + ast3.NotIn: "not in", # codespell:ignore notin } def from_comp_operator(self, op: ast3.cmpop) -> str: @@ -613,7 +641,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: ret: list[Statement] = [] current_overload: list[OverloadPart] = [] current_overload_name: str | None = None - seen_unconditional_func_def = False + last_unconditional_func_def: str | None = None last_if_stmt: IfStmt | None = None last_if_overload: Decorator | FuncDef | OverloadedFuncDef | None = None last_if_stmt_overload_name: str | None = None @@ -623,7 +651,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: if_overload_name: str | None = None if_block_with_overload: Block | None = None if_unknown_truth_value: IfStmt | None = None - if isinstance(stmt, IfStmt) and seen_unconditional_func_def is False: + if isinstance(stmt, IfStmt): # Check IfStmt block to determine if function overloads can be merged if_overload_name = self._check_ifstmt_for_overloads(stmt, current_overload_name) if if_overload_name is not None: @@ -651,11 +679,18 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: last_if_unknown_truth_value = None current_overload.append(stmt) if isinstance(stmt, FuncDef): - seen_unconditional_func_def = True + # This is, strictly speaking, wrong: there might be a decorated + # implementation. However, it only affects the error message we show: + # ideally it's "already defined", but "implementation must come last" + # is also reasonable. + # TODO: can we get rid of this completely and just always emit + # "implementation must come last" instead? + last_unconditional_func_def = stmt.name elif ( current_overload_name is not None and isinstance(stmt, IfStmt) and if_overload_name == current_overload_name + and last_unconditional_func_def != current_overload_name ): # IfStmt only contains stmts relevant to current_overload. # Check if stmts are reachable and add them to current_overload, @@ -675,7 +710,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: current_overload.append(last_if_overload) last_if_stmt, last_if_overload = None, None if isinstance(if_block_with_overload.body[-1], OverloadedFuncDef): - skipped_if_stmts.extend(cast(List[IfStmt], if_block_with_overload.body[:-1])) + skipped_if_stmts.extend(cast(list[IfStmt], if_block_with_overload.body[:-1])) current_overload.extend(if_block_with_overload.body[-1].items) else: current_overload.append( @@ -711,7 +746,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: # most of mypy/mypyc assumes that all the functions in an OverloadedFuncDef are # related, but multiple underscore functions next to each other aren't necessarily # related - seen_unconditional_func_def = False + last_unconditional_func_def = None if isinstance(stmt, Decorator) and not unnamed_function(stmt.name): current_overload = [stmt] current_overload_name = stmt.name @@ -722,7 +757,7 @@ def fix_function_overloads(self, stmts: list[Statement]) -> list[Statement]: last_if_stmt_overload_name = None if if_block_with_overload is not None: skipped_if_stmts.extend( - cast(List[IfStmt], if_block_with_overload.body[:-1]) + cast(list[IfStmt], if_block_with_overload.body[:-1]) ) last_if_overload = cast( Union[Decorator, FuncDef, OverloadedFuncDef], @@ -924,12 +959,17 @@ def do_func_def( # for ellipsis arg if ( len(func_type_ast.argtypes) == 1 - and isinstance(func_type_ast.argtypes[0], Constant) + and isinstance(func_type_ast.argtypes[0], ast3.Constant) and func_type_ast.argtypes[0].value is Ellipsis ): if n.returns: # PEP 484 disallows both type annotations and type comments - self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset) + self.fail( + message_registry.DUPLICATE_TYPE_SIGNATURES, + lineno, + n.col_offset, + blocker=False, + ) arg_types = [ ( a.type_annotation @@ -941,12 +981,17 @@ def do_func_def( else: # PEP 484 disallows both type annotations and type comments if n.returns or any(a.type_annotation is not None for a in args): - self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset) + self.fail( + message_registry.DUPLICATE_TYPE_SIGNATURES, + lineno, + n.col_offset, + blocker=False, + ) translated_args: list[Type] = TypeConverter( self.errors, line=lineno, override_column=n.col_offset ).translate_expr_list(func_type_ast.argtypes) # Use a cast to work around `list` invariance - arg_types = cast(List[Optional[Type]], translated_args) + arg_types = cast(list[Optional[Type]], translated_args) return_type = TypeConverter(self.errors, line=lineno).visit(func_type_ast.returns) # add implicit self type @@ -956,7 +1001,7 @@ def do_func_def( except SyntaxError: stripped_type = n.type_comment.split("#", 2)[0].strip() err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type) - self.fail(err_msg, lineno, n.col_offset) + self.fail(err_msg, lineno, n.col_offset, blocker=False) if n.type_comment and n.type_comment[0] not in ["(", "#"]: self.note( "Suggestion: wrap argument types in parentheses", lineno, n.col_offset @@ -978,7 +1023,12 @@ def do_func_def( func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types): - self.fail(message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, lineno, n.col_offset) + self.fail( + message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, + lineno, + n.col_offset, + blocker=False, + ) elif len(arg_types) > len(arg_kinds): self.fail( message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS, @@ -1017,28 +1067,22 @@ def do_func_def( func_def.is_coroutine = True if func_type is not None: func_type.definition = func_def - func_type.line = lineno + func_type.set_line(lineno) if n.decorator_list: - # Set deco_line to the old pre-3.8 lineno, in order to keep - # existing "# type: ignore" comments working: - deco_line = n.decorator_list[0].lineno - var = Var(func_def.name) var.is_ready = False var.set_line(lineno) func_def.is_decorated = True - func_def.deco_line = deco_line - func_def.set_line(lineno, n.col_offset, end_line, end_column) + self.set_line(func_def, n) deco = Decorator(func_def, self.translate_expr_list(n.decorator_list), var) first = n.decorator_list[0] deco.set_line(first.lineno, first.col_offset, end_line, end_column) retval: FuncDef | Decorator = deco else: - # FuncDef overrides set_line -- can't use self.set_line - func_def.set_line(lineno, n.col_offset, end_line, end_column) + self.set_line(func_def, n) retval = func_def if self.options.include_docstrings: func_def.docstring = ast3.get_docstring(n, clean=False) @@ -1058,7 +1102,7 @@ def transform_args( ) -> list[Argument]: new_args = [] names: list[ast3.arg] = [] - posonlyargs = getattr(args, "posonlyargs", cast(List[ast3.arg], [])) + posonlyargs = getattr(args, "posonlyargs", cast(list[ast3.arg], [])) args_args = posonlyargs + args.args args_defaults = args.defaults num_no_defaults = len(args_args) - len(args_defaults) @@ -1111,7 +1155,12 @@ def make_argument( annotation = arg.annotation type_comment = arg.type_comment if annotation is not None and type_comment is not None: - self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, arg.lineno, arg.col_offset) + self.fail( + message_registry.DUPLICATE_TYPE_SIGNATURES, + arg.lineno, + arg.col_offset, + blocker=False, + ) arg_type = None if annotation is not None: arg_type = TypeConverter(self.errors, line=arg.lineno).visit(annotation) @@ -1120,7 +1169,9 @@ def make_argument( if argument_elide_name(arg.arg): pos_only = True - argument = Argument(Var(arg.arg, arg_type), arg_type, self.visit(default), kind, pos_only) + var = Var(arg.arg, arg_type) + var.is_inferred = False + argument = Argument(var, arg_type, self.visit(default), kind, pos_only) argument.set_line( arg.lineno, arg.col_offset, @@ -1130,7 +1181,7 @@ def make_argument( return argument def fail_arg(self, msg: str, arg: ast3.arg) -> None: - self.fail(ErrorMessage(msg), arg.lineno, arg.col_offset) + self.fail(ErrorMessage(msg), arg.lineno, arg.col_offset, blocker=True) # ClassDef(identifier name, # expr* bases, @@ -1157,10 +1208,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: type_args=explicit_type_params, ) cdef.decorators = self.translate_expr_list(n.decorator_list) - # Set lines to match the old mypy 0.700 lines, in order to keep - # existing "# type: ignore" comments working: - cdef.line = n.lineno - cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None + self.set_line(cdef, n) if self.options.include_docstrings: cdef.docstring = ast3.get_docstring(n, clean=False) @@ -1179,36 +1227,37 @@ def validate_type_param(self, type_param: ast_TypeVar) -> None: message_registry.TYPE_VAR_YIELD_EXPRESSION_IN_BOUND, type_param.lineno, type_param.col_offset, + blocker=True, ) if isinstance(incorrect_expr, ast3.NamedExpr): self.fail( message_registry.TYPE_VAR_NAMED_EXPRESSION_IN_BOUND, type_param.lineno, type_param.col_offset, + blocker=True, ) if isinstance(incorrect_expr, ast3.Await): self.fail( message_registry.TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND, type_param.lineno, type_param.col_offset, + blocker=True, ) 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] = [] - if sys.version_info >= (3, 13) and p.default_value is not None: - self.fail( - message_registry.TYPE_PARAM_DEFAULT_NOT_SUPPORTED, - p.lineno, - p.col_offset, - blocker=False, - ) + 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: @@ -1224,7 +1273,9 @@ def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: 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) @@ -1255,8 +1306,7 @@ def visit_AnnAssign(self, n: ast3.AnnAssign) -> AssignmentStmt: line = n.lineno if n.value is None: # always allow 'x: int' rvalue: Expression = TempNode(AnyType(TypeOfAny.special_form), no_rhs=True) - rvalue.line = line - rvalue.column = n.col_offset + self.set_line(rvalue, n) else: rvalue = self.visit(n.value) typ = TypeConverter(self.errors, line=line).visit(n.annotation) @@ -1444,7 +1494,7 @@ def visit_Continue(self, n: ast3.Continue) -> ContinueStmt: # --- expr --- - def visit_NamedExpr(self, n: NamedExpr) -> AssignmentExpr: + def visit_NamedExpr(self, n: ast3.NamedExpr) -> AssignmentExpr: s = AssignmentExpr(self.visit(n.target), self.visit(n.value)) return self.set_line(s, n) @@ -1596,12 +1646,12 @@ def visit_Call(self, n: Call) -> CallExpr: self.visit(n.func), arg_types, arg_kinds, - cast("List[Optional[str]]", [None] * len(args)) + keyword_names, + cast("list[Optional[str]]", [None] * len(args)) + keyword_names, ) return self.set_line(e, n) - # Constant(object value) -- a constant, in Python 3.8. - def visit_Constant(self, n: Constant) -> Any: + # Constant(object value) + def visit_Constant(self, n: ast3.Constant) -> Any: val = n.value e: Any = None if val is None: @@ -1665,6 +1715,21 @@ def visit_FormattedValue(self, n: ast3.FormattedValue) -> Expression: ) return self.set_line(result_expression, n) + # TemplateStr(expr* values) + def visit_TemplateStr(self, n: ast_TemplateStr) -> Expression: + self.fail( + ErrorMessage("PEP 750 template strings are not yet supported"), + n.lineno, + n.col_offset, + blocker=False, + ) + e = TempNode(AnyType(TypeOfAny.from_error)) + return self.set_line(e, n) + + # Interpolation(expr value, constant str, int conversion, expr? format_spec) + def visit_Interpolation(self, n: ast_Interpolation) -> Expression: + assert False, "Unreachable" + # Attribute(expr value, identifier attr, expr_context ctx) def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr: value = n.value @@ -1683,19 +1748,7 @@ def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr: # Subscript(expr value, slice slice, expr_context ctx) def visit_Subscript(self, n: ast3.Subscript) -> IndexExpr: e = IndexExpr(self.visit(n.value), self.visit(n.slice)) - self.set_line(e, n) - # alias to please mypyc - is_py38_or_earlier = sys.version_info < (3, 9) - if isinstance(n.slice, ast3.Slice) or ( - is_py38_or_earlier and isinstance(n.slice, ast3.ExtSlice) - ): - # Before Python 3.9, Slice has no line/column in the raw ast. To avoid incompatibility - # visit_Slice doesn't set_line, even in Python 3.9 on. - # ExtSlice also has no line/column info. In Python 3.9 on, line/column is set for - # e.index when visiting n.slice. - e.index.line = e.line - e.index.column = e.column - return e + return self.set_line(e, n) # Starred(expr value, expr_context ctx) def visit_Starred(self, n: Starred) -> StarExpr: @@ -1722,23 +1775,10 @@ def visit_Tuple(self, n: ast3.Tuple) -> TupleExpr: e = TupleExpr(self.translate_expr_list(n.elts)) return self.set_line(e, n) - # --- slice --- - # Slice(expr? lower, expr? upper, expr? step) def visit_Slice(self, n: ast3.Slice) -> SliceExpr: - return SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step)) - - # ExtSlice(slice* dims) - def visit_ExtSlice(self, n: ast3.ExtSlice) -> TupleExpr: - # cast for mypyc's benefit on Python 3.9 - return TupleExpr(self.translate_expr_list(cast(Any, n).dims)) - - # Index(expr value) - def visit_Index(self, n: Index) -> Node: - # cast for mypyc's benefit on Python 3.9 - value = self.visit(cast(Any, n).value) - assert isinstance(value, Node) - return value + e = SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step)) + return self.set_line(e, n) # Match(expr subject, match_case* cases) # python 3.10 and later def visit_Match(self, n: Match) -> MatchStmt: @@ -1817,11 +1857,26 @@ def validate_type_alias(self, n: ast_TypeAlias) -> None: 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) + self.fail( + message_registry.TYPE_ALIAS_WITH_YIELD_EXPRESSION, + n.lineno, + n.col_offset, + blocker=True, + ) if isinstance(incorrect_expr, ast3.NamedExpr): - self.fail(message_registry.TYPE_ALIAS_WITH_NAMED_EXPRESSION, n.lineno, n.col_offset) + self.fail( + message_registry.TYPE_ALIAS_WITH_NAMED_EXPRESSION, + n.lineno, + n.col_offset, + blocker=True, + ) if isinstance(incorrect_expr, ast3.Await): - self.fail(message_registry.TYPE_ALIAS_WITH_AWAIT_EXPRESSION, n.lineno, n.col_offset) + self.fail( + message_registry.TYPE_ALIAS_WITH_AWAIT_EXPRESSION, + n.lineno, + n.col_offset, + blocker=True, + ) # TypeAlias(identifier name, type_param* type_params, expr value) def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: @@ -1975,9 +2030,9 @@ def translate_argument_list(self, l: Sequence[ast3.expr]) -> TypeList: return TypeList([self.visit(e) for e in l], line=self.line) def _extract_argument_name(self, n: ast3.expr) -> str | None: - if isinstance(n, Constant) and isinstance(n.value, str): + if isinstance(n, ast3.Constant) and isinstance(n.value, str): return n.value.strip() - elif isinstance(n, Constant) and n.value is None: + elif isinstance(n, ast3.Constant) and n.value is None: return None self.fail( message_registry.ARG_NAME_EXPECTED_STRING_LITERAL.format(type(n).__name__), @@ -2003,7 +2058,7 @@ def visit_BinOp(self, n: ast3.BinOp) -> Type: uses_pep604_syntax=True, ) - def visit_Constant(self, n: Constant) -> Type: + def visit_Constant(self, n: ast3.Constant) -> Type: val = n.value if val is None: # None is a type. @@ -2023,7 +2078,6 @@ def visit_Constant(self, n: Constant) -> Type: contents = bytes_to_human_readable_repr(val) return RawExpressionType(contents, "builtins.bytes", self.line, column=n.col_offset) # Everything else is invalid. - return self.invalid_type(n) # UnaryOp(op, operand) def visit_UnaryOp(self, n: UnaryOp) -> Type: @@ -2033,7 +2087,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 @@ -2060,59 +2114,31 @@ def numeric_type(self, value: object, n: AST) -> Type: numeric_value, type_name, line=self.line, column=getattr(n, "col_offset", -1) ) - def visit_Index(self, n: ast3.Index) -> Type: - # cast for mypyc's benefit on Python 3.9 - value = self.visit(cast(Any, n).value) - assert isinstance(value, Type) - return value - def visit_Slice(self, n: ast3.Slice) -> Type: return self.invalid_type(n, note="did you mean to use ',' instead of ':' ?") - # Subscript(expr value, slice slice, expr_context ctx) # Python 3.8 and before - # Subscript(expr value, expr slice, expr_context ctx) # Python 3.9 and later + # Subscript(expr value, expr slice, expr_context ctx) def visit_Subscript(self, n: ast3.Subscript) -> Type: - if sys.version_info >= (3, 9): # Really 3.9a5 or later - sliceval: Any = n.slice - # Python 3.8 or earlier use a different AST structure for subscripts - elif isinstance(n.slice, ast3.Index): - sliceval: Any = n.slice.value - elif isinstance(n.slice, ast3.Slice): - sliceval = copy.deepcopy(n.slice) # so we don't mutate passed AST - if getattr(sliceval, "col_offset", None) is None: - # Fix column information so that we get Python 3.9+ message order - sliceval.col_offset = sliceval.lower.col_offset - else: - assert isinstance(n.slice, ast3.ExtSlice) - dims = cast(List[ast3.expr], copy.deepcopy(n.slice.dims)) - for s in dims: - # These fields don't actually have a col_offset attribute but we add - # it manually. - if getattr(s, "col_offset", None) is None: - if isinstance(s, ast3.Index): - s.col_offset = s.value.col_offset - elif isinstance(s, ast3.Slice): - assert s.lower is not None - s.col_offset = s.lower.col_offset - sliceval = ast3.Tuple(dims, n.ctx) - empty_tuple_index = False - if isinstance(sliceval, ast3.Tuple): - params = self.translate_expr_list(sliceval.elts) - if len(sliceval.elts) == 0: + if isinstance(n.slice, ast3.Tuple): + params = self.translate_expr_list(n.slice.elts) + if len(n.slice.elts) == 0: empty_tuple_index = True else: - params = [self.visit(sliceval)] + params = [self.visit(n.slice)] value = self.visit(n.value) if isinstance(value, UnboundType) and not value.args: - return UnboundType( + result = UnboundType( value.name, params, line=self.line, column=value.column, empty_tuple_index=empty_tuple_index, ) + result.end_column = getattr(n, "end_col_offset", None) + result.end_line = getattr(n, "end_lineno", None) + return result else: return self.invalid_type(n) @@ -2146,7 +2172,7 @@ def visit_Attribute(self, n: Attribute) -> Type: before_dot = self.visit(n.value) if isinstance(before_dot, UnboundType) and not before_dot.args: - return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line) + return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line, column=n.col_offset) else: return self.invalid_type(n) @@ -2213,12 +2239,12 @@ def visit_index_expr(self, e: IndexExpr) -> None: pass def visit_member_expr(self, e: MemberExpr) -> None: - if self.lvalue: + if self.lvalue and isinstance(e.expr, NameExpr): self.found = True class FindYield(TraverserVisitor): - """Check if an AST contains yields or yield froms.""" + """Check if an AST contains yields or yield froms.""" # codespell:ignore froms def __init__(self) -> None: self.found = False diff --git a/mypy/find_sources.py b/mypy/find_sources.py index 3565fc4609cd9..ececbf9c1cb80 100644 --- a/mypy/find_sources.py +++ b/mypy/find_sources.py @@ -4,10 +4,17 @@ import functools import os -from typing import Final, Sequence +from collections.abc import Sequence +from typing import Final from mypy.fscache import FileSystemCache -from mypy.modulefinder import PYTHON_EXTENSIONS, BuildSource, matches_exclude, mypy_path +from mypy.modulefinder import ( + PYTHON_EXTENSIONS, + BuildSource, + matches_exclude, + matches_gitignore, + mypy_path, +) from mypy.options import Options PY_EXTENSIONS: Final = tuple(PYTHON_EXTENSIONS) @@ -93,6 +100,7 @@ def __init__(self, fscache: FileSystemCache, options: Options) -> None: self.explicit_package_bases = get_explicit_package_bases(options) self.namespace_packages = options.namespace_packages self.exclude = options.exclude + self.exclude_gitignore = options.exclude_gitignore self.verbosity = options.verbosity def is_explicit_package_base(self, path: str) -> bool: @@ -112,6 +120,10 @@ def find_sources_in_dir(self, path: str) -> list[BuildSource]: if matches_exclude(subpath, self.exclude, self.fscache, self.verbosity >= 2): continue + if self.exclude_gitignore and matches_gitignore( + subpath, self.fscache, self.verbosity >= 2 + ): + continue if self.fscache.isdir(subpath): sub_sources = self.find_sources_in_dir(subpath) @@ -175,8 +187,7 @@ def _crawl_up_helper(self, dir: str) -> tuple[str, str] | None: return "", dir parent, name = os.path.split(dir) - if name.endswith("-stubs"): - name = name[:-6] # PEP-561 stub-only directory + name = name.removesuffix("-stubs") # PEP-561 stub-only directory # recurse if there's an __init__.py init_file = self.get_init_file(dir) diff --git a/mypy/fixup.py b/mypy/fixup.py index f2b5bc17d32e3..d0205f64b7207 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -29,6 +29,7 @@ Overloaded, Parameters, ParamSpecType, + ProperType, TupleType, TypeAliasType, TypedDictType, @@ -96,6 +97,8 @@ def visit_type_info(self, info: TypeInfo) -> None: info.declared_metaclass.accept(self.type_fixer) if info.metaclass_type: info.metaclass_type.accept(self.type_fixer) + if info.self_type: + info.self_type.accept(self.type_fixer) if info.alt_promote: info.alt_promote.accept(self.type_fixer) instance = Instance(info, []) @@ -117,7 +120,8 @@ def visit_type_info(self, info: TypeInfo) -> None: # NOTE: This method *definitely* isn't part of the NodeVisitor API. def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None: # Copy the items because we may mutate symtab. - for key, value in list(symtab.items()): + for key in list(symtab): + value = symtab[key] cross_ref = value.cross_ref if cross_ref is not None: # Fix up cross-reference. value.cross_ref = None @@ -164,6 +168,8 @@ def visit_func_def(self, func: FuncDef) -> None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer) + if isinstance(func.type, CallableType): + func.type.definition = func def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: if self.current_info is not None: @@ -174,6 +180,10 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: item.accept(self) if o.impl: o.impl.accept(self) + if isinstance(o.type, Overloaded): + # For error messages we link the original definition for each item. + for typ, item in zip(o.type.items, o.items): + typ.definition = item def visit_decorator(self, d: Decorator) -> None: if self.current_info is not None: @@ -184,6 +194,9 @@ def visit_decorator(self, d: Decorator) -> None: d.var.accept(self) for node in d.decorators: node.accept(self) + typ = d.var.type + if isinstance(typ, ProperType) and isinstance(typ, CallableType): + typ.definition = d.func def visit_class_def(self, c: ClassDef) -> None: for v in c.type_vars: @@ -209,6 +222,8 @@ def visit_var(self, v: Var) -> None: v.info = self.current_info if v.type is not None: v.type.accept(self.type_fixer) + if v.setter_type is not None: + v.setter_type.accept(self.type_fixer) def visit_type_alias(self, a: TypeAlias) -> None: a.target.accept(self.type_fixer) @@ -268,9 +283,6 @@ def visit_callable_type(self, ct: CallableType) -> None: ct.ret_type.accept(self) for v in ct.variables: v.accept(self) - for arg in ct.bound_args: - if arg: - arg.accept(self) if ct.type_guard is not None: ct.type_guard.accept(self) if ct.type_is is not None: @@ -335,6 +347,7 @@ def visit_type_var(self, tvt: TypeVarType) -> None: def visit_param_spec(self, p: ParamSpecType) -> None: p.upper_bound.accept(self) p.default.accept(self) + p.prefix.accept(self) def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: t.tuple_fallback.accept(self) @@ -428,4 +441,4 @@ def missing_info(modules: dict[str, MypyFile]) -> TypeInfo: def missing_alias() -> TypeAlias: suggestion = _SUGGESTION.format("alias") - return TypeAlias(AnyType(TypeOfAny.special_form), suggestion, line=-1, column=-1) + return TypeAlias(AnyType(TypeOfAny.special_form), suggestion, "", line=-1, column=-1) diff --git a/mypy/fscache.py b/mypy/fscache.py index 15679ad03e859..240370159fff6 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,15 +109,20 @@ 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): return False ok = False - drive, path = os.path.splitdrive(path) # Ignore Windows drive name + + # skip if on a different drive + current_drive, _ = os.path.splitdrive(os.getcwd()) + drive, _ = os.path.splitdrive(path) + if drive != current_drive: + return False if os.path.isabs(path): path = os.path.relpath(path) path = os.path.normpath(path) @@ -145,7 +147,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 +155,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 +182,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 +248,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 +265,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 +290,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..d5873f3a0a996 100644 --- a/mypy/fswatcher.py +++ b/mypy/fswatcher.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import AbstractSet, Iterable, NamedTuple +import os +from collections.abc import Iterable, Set as AbstractSet +from typing import NamedTuple from mypy.fscache import FileSystemCache @@ -56,8 +58,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 +66,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 +76,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..bc908bdb6107d 100644 --- a/mypy/gclogger.py +++ b/mypy/gclogger.py @@ -2,7 +2,7 @@ import gc import time -from typing import Mapping +from collections.abc import Mapping class GcLogger: @@ -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/graph_utils.py b/mypy/graph_utils.py index 399301a6b0fdd..154efcef48a93 100644 --- a/mypy/graph_utils.py +++ b/mypy/graph_utils.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import AbstractSet, Iterable, Iterator, TypeVar +from collections.abc import Iterable, Iterator, Set as AbstractSet +from typing import TypeVar T = TypeVar("T") @@ -57,7 +58,11 @@ def prepare_sccs( sccs: list[set[T]], edges: dict[T, list[T]] ) -> dict[AbstractSet[T], set[AbstractSet[T]]]: """Use original edges to organize SCCs in a graph by dependencies between them.""" - sccsmap = {v: frozenset(scc) for scc in sccs for v in scc} + sccsmap = {} + for scc in sccs: + scc_frozen = frozenset(scc) + for v in scc: + sccsmap[v] = scc_frozen data: dict[AbstractSet[T], set[AbstractSet[T]]] = {} for scc in sccs: deps: set[AbstractSet[T]] = set() diff --git a/mypy/indirection.py b/mypy/indirection.py index 00356d7a4ddbe..95023e303cbdc 100644 --- a/mypy/indirection.py +++ b/mypy/indirection.py @@ -1,121 +1,163 @@ from __future__ import annotations -from typing import Iterable, Set +from collections.abc import Iterable import mypy.types as types from mypy.types import TypeVisitor -from mypy.util import split_module_names -def extract_module_names(type_name: str | None) -> list[str]: - """Returns the module names of a fully qualified type name.""" - if type_name is not None: - # Discard the first one, which is just the qualified name of the type - possible_module_names = split_module_names(type_name) - return possible_module_names[1:] - else: - return [] - - -class TypeIndirectionVisitor(TypeVisitor[Set[str]]): +class TypeIndirectionVisitor(TypeVisitor[None]): """Returns all module references within a particular type.""" def __init__(self) -> None: - self.cache: dict[types.Type, set[str]] = {} - self.seen_aliases: set[types.TypeAliasType] = set() + # Module references are collected here + self.modules: set[str] = set() + # User to avoid infinite recursion with recursive types + self.seen_types: set[types.TypeAliasType | types.Instance] = set() def find_modules(self, typs: Iterable[types.Type]) -> set[str]: - self.seen_aliases.clear() - return self._visit(typs) + self.modules = set() + self.seen_types = set() + for typ in typs: + self._visit(typ) + return self.modules + + def _visit(self, typ: types.Type) -> None: + # Note: instances are needed for `class str(Sequence[str]): ...` + if ( + isinstance(typ, types.TypeAliasType) + or isinstance(typ, types.ProperType) + and isinstance(typ, types.Instance) + ): + # Avoid infinite recursion for recursive types. + if typ in self.seen_types: + return + self.seen_types.add(typ) + typ.accept(self) + + def _visit_type_tuple(self, typs: tuple[types.Type, ...]) -> None: + # Micro-optimization: Specialized version of _visit for lists + for typ in typs: + if ( + isinstance(typ, types.TypeAliasType) + or isinstance(typ, types.ProperType) + and isinstance(typ, types.Instance) + ): + # Avoid infinite recursion for recursive types. + if typ in self.seen_types: + continue + self.seen_types.add(typ) + typ.accept(self) - def _visit(self, typ_or_typs: types.Type | Iterable[types.Type]) -> set[str]: - typs = [typ_or_typs] if isinstance(typ_or_typs, types.Type) else typ_or_typs - output: set[str] = set() + def _visit_type_list(self, typs: list[types.Type]) -> None: + # Micro-optimization: Specialized version of _visit for tuples for typ in typs: - if isinstance(typ, types.TypeAliasType): - # Avoid infinite recursion for recursive type aliases. - if typ in self.seen_aliases: + if ( + isinstance(typ, types.TypeAliasType) + or isinstance(typ, types.ProperType) + and isinstance(typ, types.Instance) + ): + # Avoid infinite recursion for recursive types. + if typ in self.seen_types: continue - self.seen_aliases.add(typ) - if typ in self.cache: - modules = self.cache[typ] - else: - modules = typ.accept(self) - self.cache[typ] = set(modules) - output.update(modules) - return output + self.seen_types.add(typ) + typ.accept(self) - def visit_unbound_type(self, t: types.UnboundType) -> set[str]: - return self._visit(t.args) + def visit_unbound_type(self, t: types.UnboundType) -> None: + self._visit_type_tuple(t.args) - def visit_any(self, t: types.AnyType) -> set[str]: - return set() + def visit_any(self, t: types.AnyType) -> None: + pass - def visit_none_type(self, t: types.NoneType) -> set[str]: - return set() + def visit_none_type(self, t: types.NoneType) -> None: + pass - def visit_uninhabited_type(self, t: types.UninhabitedType) -> set[str]: - return set() + def visit_uninhabited_type(self, t: types.UninhabitedType) -> None: + pass - def visit_erased_type(self, t: types.ErasedType) -> set[str]: - return set() + def visit_erased_type(self, t: types.ErasedType) -> None: + pass - def visit_deleted_type(self, t: types.DeletedType) -> set[str]: - return set() + def visit_deleted_type(self, t: types.DeletedType) -> None: + pass - def visit_type_var(self, t: types.TypeVarType) -> set[str]: - return self._visit(t.values) | self._visit(t.upper_bound) | self._visit(t.default) + def visit_type_var(self, t: types.TypeVarType) -> None: + self._visit_type_list(t.values) + self._visit(t.upper_bound) + self._visit(t.default) - def visit_param_spec(self, t: types.ParamSpecType) -> set[str]: - return self._visit(t.upper_bound) | self._visit(t.default) + def visit_param_spec(self, t: types.ParamSpecType) -> None: + self._visit(t.upper_bound) + self._visit(t.default) + self._visit(t.prefix) - def visit_type_var_tuple(self, t: types.TypeVarTupleType) -> set[str]: - return self._visit(t.upper_bound) | self._visit(t.default) + def visit_type_var_tuple(self, t: types.TypeVarTupleType) -> None: + self._visit(t.upper_bound) + self._visit(t.default) - def visit_unpack_type(self, t: types.UnpackType) -> set[str]: - return t.type.accept(self) + def visit_unpack_type(self, t: types.UnpackType) -> None: + t.type.accept(self) - def visit_parameters(self, t: types.Parameters) -> set[str]: - return self._visit(t.arg_types) + def visit_parameters(self, t: types.Parameters) -> None: + self._visit_type_list(t.arg_types) - def visit_instance(self, t: types.Instance) -> set[str]: - out = self._visit(t.args) + def visit_instance(self, t: types.Instance) -> None: + # Instance is named, record its definition and continue digging into + # components that constitute semantic meaning of this type: bases, metaclass, + # tuple type, and typeddict type. + # Note: we cannot simply record the MRO, in case an intermediate base contains + # a reference to type alias, this affects meaning of map_instance_to_supertype(), + # see e.g. testDoubleReexportGenericUpdated. + self._visit_type_tuple(t.args) if t.type: - # Uses of a class depend on everything in the MRO, - # as changes to classes in the MRO can add types to methods, - # change property types, change the MRO itself, etc. + # Important optimization: instead of simply recording the definition and + # recursing into bases, record the MRO and only traverse generic bases. for s in t.type.mro: - out.update(split_module_names(s.module_name)) - if t.type.metaclass_type is not None: - out.update(split_module_names(t.type.metaclass_type.type.module_name)) - return out - - def visit_callable_type(self, t: types.CallableType) -> set[str]: - out = self._visit(t.arg_types) | self._visit(t.ret_type) - if t.definition is not None: - out.update(extract_module_names(t.definition.fullname)) - return out - - def visit_overloaded(self, t: types.Overloaded) -> set[str]: - return self._visit(t.items) | self._visit(t.fallback) - - def visit_tuple_type(self, t: types.TupleType) -> set[str]: - return self._visit(t.items) | self._visit(t.partial_fallback) - - def visit_typeddict_type(self, t: types.TypedDictType) -> set[str]: - return self._visit(t.items.values()) | self._visit(t.fallback) - - def visit_literal_type(self, t: types.LiteralType) -> set[str]: - return self._visit(t.fallback) - - def visit_union_type(self, t: types.UnionType) -> set[str]: - return self._visit(t.items) - - def visit_partial_type(self, t: types.PartialType) -> set[str]: - return set() - - def visit_type_type(self, t: types.TypeType) -> set[str]: - return self._visit(t.item) - - def visit_type_alias_type(self, t: types.TypeAliasType) -> set[str]: - return self._visit(types.get_proper_type(t)) + self.modules.add(s.module_name) + for base in s.bases: + if base.args: + self._visit_type_tuple(base.args) + if t.type.metaclass_type: + self._visit(t.type.metaclass_type) + if t.type.typeddict_type: + self._visit(t.type.typeddict_type) + if t.type.tuple_type: + self._visit(t.type.tuple_type) + + def visit_callable_type(self, t: types.CallableType) -> None: + self._visit_type_list(t.arg_types) + self._visit(t.ret_type) + self._visit_type_tuple(t.variables) + + def visit_overloaded(self, t: types.Overloaded) -> None: + for item in t.items: + self._visit(item) + self._visit(t.fallback) + + def visit_tuple_type(self, t: types.TupleType) -> None: + self._visit_type_list(t.items) + self._visit(t.partial_fallback) + + def visit_typeddict_type(self, t: types.TypedDictType) -> None: + self._visit_type_list(list(t.items.values())) + self._visit(t.fallback) + + def visit_literal_type(self, t: types.LiteralType) -> None: + self._visit(t.fallback) + + def visit_union_type(self, t: types.UnionType) -> None: + self._visit_type_list(t.items) + + def visit_partial_type(self, t: types.PartialType) -> None: + pass + + def visit_type_type(self, t: types.TypeType) -> None: + self._visit(t.item) + + def visit_type_alias_type(self, t: types.TypeAliasType) -> None: + # Type alias is named, record its definition and continue digging into + # components that constitute semantic meaning of this type: target and args. + if t.alias: + self.modules.add(t.alias.module) + self._visit(t.alias.target) + self._visit_type_list(t.args) diff --git a/mypy/infer.py b/mypy/infer.py index bcf0c95808ab6..cdc43797d3b16 100644 --- a/mypy/infer.py +++ b/mypy/infer.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import NamedTuple, Sequence +from collections.abc import Sequence +from typing import NamedTuple from mypy.constraints import ( SUBTYPE_OF, diff --git a/mypy/inspections.py b/mypy/inspections.py index 3e660a0bd7a69..ac48fac56fa4b 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -335,7 +335,7 @@ def expr_attrs(self, expression: Expression) -> tuple[str, bool]: node = expression.node names = sorted(node.names) if "__builtins__" in names: - # This is just to make tests stable. No one will really need ths name. + # This is just to make tests stable. No one will really need this name. names.remove("__builtins__") mod_dict = {f'"<{node.fullname}>"': [f'"{name}"' for name in names]} else: @@ -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: @@ -565,7 +564,7 @@ def run_inspection( ) -> dict[str, object]: """Top-level logic to inspect expression(s) at a location. - This can be re-used by various simple inspections. + This can be reused by various simple inspections. """ try: file, pos = parse_location(location) diff --git a/mypy/ipc.py b/mypy/ipc.py index ab01f1b79e7d8..b2046a47ab157 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 @@ -304,6 +303,10 @@ def cleanup(self) -> None: def connection_name(self) -> str: if sys.platform == "win32": return self.name + elif sys.platform == "gnu0": + # GNU/Hurd returns empty string from getsockname() + # for AF_UNIX sockets + return os.path.join(self.sock_directory, self.name) else: name = self.sock.getsockname() assert isinstance(name, str) diff --git a/mypy/join.py b/mypy/join.py index 865dd073d0819..a074fa522588c 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import Sequence, overload +from collections.abc import Sequence +from typing import overload import mypy.typeops from mypy.expandtype import expand_type from mypy.maptype import map_instance_to_supertype -from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY +from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY, TypeInfo from mypy.state import state from mypy.subtypes import ( SubtypeContext, @@ -167,9 +168,20 @@ def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType: # Compute the "best" supertype of t when joined with s. # The definition of "best" may evolve; for now it is the one with # the longest MRO. Ties are broken by using the earlier base. - best: ProperType | None = None + + # Go over both sets of bases in case there's an explicit Protocol base. This is important + # to ensure commutativity of join (although in cases where both classes have relevant + # Protocol bases this maybe might still not be commutative) + base_types: dict[TypeInfo, None] = {} # dict to deduplicate but preserve order for base in t.type.bases: - mapped = map_instance_to_supertype(t, base.type) + base_types[base.type] = None + for base in s.type.bases: + if base.type.is_protocol and is_subtype(t, base): + base_types[base.type] = None + + best: ProperType | None = None + for base_type in base_types: + mapped = map_instance_to_supertype(t, base_type) res = self.join_instances(mapped, s) if best is None or is_better(res, best): best = res @@ -182,55 +194,6 @@ def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType: return best -def join_simple(declaration: Type | None, s: Type, t: Type) -> ProperType: - """Return a simple least upper bound given the declared type. - - This function should be only used by binder, and should not recurse. - For all other uses, use `join_types()`. - """ - declaration = get_proper_type(declaration) - s = get_proper_type(s) - t = get_proper_type(t) - - if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): - # if types are restricted in different ways, use the more general versions - s = mypy.typeops.true_or_false(s) - t = mypy.typeops.true_or_false(t) - - if isinstance(s, AnyType): - return s - - if isinstance(s, ErasedType): - return t - - if is_proper_subtype(s, t, ignore_promotions=True): - return t - - if is_proper_subtype(t, s, ignore_promotions=True): - return s - - if isinstance(declaration, UnionType): - return mypy.typeops.make_simplified_union([s, t]) - - if isinstance(s, NoneType) and not isinstance(t, NoneType): - s, t = t, s - - if isinstance(s, UninhabitedType) and not isinstance(t, UninhabitedType): - s, t = t, s - - # Meets/joins require callable type normalization. - s, t = normalize_callables(s, t) - - if isinstance(s, UnionType) and not isinstance(t, UnionType): - s, t = t, s - - value = t.accept(TypeJoinVisitor(s)) - if declaration is None or is_subtype(value, declaration): - return value - - return declaration - - def trivial_join(s: Type, t: Type) -> Type: """Return one of types (expanded) if it is a supertype of other, otherwise top type.""" if is_subtype(s, t): @@ -317,7 +280,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]) @@ -334,8 +297,15 @@ def visit_erased_type(self, t: ErasedType) -> ProperType: return self.s def visit_type_var(self, t: TypeVarType) -> ProperType: - if isinstance(self.s, TypeVarType) and self.s.id == t.id: - return self.s + if isinstance(self.s, TypeVarType): + if self.s.id == t.id: + if self.s.upper_bound == t.upper_bound: + return self.s + return self.s.copy_modified( + upper_bound=join_types(self.s.upper_bound, t.upper_bound) + ) + # Fix non-commutative joins + return get_proper_type(join_types(self.s.upper_bound, t.upper_bound)) else: return self.default(self.s) @@ -347,6 +317,9 @@ def visit_param_spec(self, t: ParamSpecType) -> ProperType: def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: if self.s == t: return t + if isinstance(self.s, Instance) and is_subtype(t.upper_bound, self.s): + # TODO: should we do this more generally and for all TypeVarLikeTypes? + return self.s return self.default(self.s) def visit_unpack_type(self, t: UnpackType) -> UnpackType: @@ -354,7 +327,8 @@ def visit_unpack_type(self, t: UnpackType) -> UnpackType: def visit_parameters(self, t: Parameters) -> ProperType: if isinstance(self.s, Parameters): - if len(t.arg_types) != len(self.s.arg_types): + if not is_similar_params(t, self.s): + # TODO: it would be prudent to return [*object, **object] instead of Any. return self.default(self.s) from mypy.meet import meet_types @@ -397,6 +371,8 @@ def visit_instance(self, t: Instance) -> ProperType: return join_types(t, self.s) elif isinstance(self.s, LiteralType): return join_types(t, self.s) + elif isinstance(self.s, TypeVarTupleType) and is_subtype(self.s.upper_bound, t): + return t else: return self.default(self.s) @@ -498,7 +474,7 @@ def join_tuples(self, s: TupleType, t: TupleType) -> list[Type] | None: return items return None if s_unpack_index is not None and t_unpack_index is not None: - # The most complex case: both tuples have an upack item. + # The most complex case: both tuples have an unpack item. s_unpack = s.items[s_unpack_index] assert isinstance(s_unpack, UnpackType) s_unpacked = get_proper_type(s_unpack.type) @@ -609,6 +585,10 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: assert isinstance(fallback, Instance) items = self.join_tuples(self.s, t) if items is not None: + if len(items) == 1 and isinstance(item := items[0], UnpackType): + if isinstance(unpacked := get_proper_type(item.type), Instance): + # Avoid double-wrapping tuple[*tuple[X, ...]] + return unpacked return TupleType(items, fallback) else: # TODO: should this be a default fallback behaviour like for meet? @@ -650,6 +630,8 @@ def visit_literal_type(self, t: LiteralType) -> ProperType: if self.s.fallback.type.is_enum and t.fallback.type.is_enum: return mypy.typeops.make_simplified_union([self.s, t]) return join_types(self.s.fallback, t.fallback) + elif isinstance(self.s, Instance) and self.s.last_known_value == t: + return t else: return join_types(self.s, t.fallback) @@ -660,7 +642,11 @@ def visit_partial_type(self, t: PartialType) -> ProperType: def visit_type_type(self, t: TypeType) -> ProperType: if isinstance(self.s, TypeType): - return TypeType.make_normalized(join_types(t.item, self.s.item), line=t.line) + return TypeType.make_normalized( + join_types(t.item, self.s.item), + line=t.line, + is_type_form=self.s.is_type_form or t.is_type_form, + ) elif isinstance(self.s, Instance) and self.s.type.fullname == "builtins.type": return self.s else: @@ -673,6 +659,8 @@ def default(self, typ: Type) -> ProperType: typ = get_proper_type(typ) if isinstance(typ, Instance): return object_from_instance(typ) + elif isinstance(typ, TypeType): + return self.default(typ.item) elif isinstance(typ, UnboundType): return AnyType(TypeOfAny.special_form) elif isinstance(typ, TupleType): @@ -698,6 +686,10 @@ def is_better(t: Type, s: Type) -> bool: if isinstance(t, Instance): if not isinstance(s, Instance): return True + if t.type.is_protocol != s.type.is_protocol: + if t.type.fullname != "builtins.object" and s.type.fullname != "builtins.object": + # mro of protocol is not really relevant + return not t.type.is_protocol # Use len(mro) as a proxy for the better choice. if len(t.type.mro) > len(s.type.mro): return True @@ -723,6 +715,15 @@ def is_similar_callables(t: CallableType, s: CallableType) -> bool: ) +def is_similar_params(t: Parameters, s: Parameters) -> bool: + # This matches the logic in is_similar_callables() above. + return ( + len(t.arg_types) == len(s.arg_types) + and t.min_args == s.min_args + and (t.var_arg() is not None) == (s.var_arg() is not None) + ) + + def update_callable_ids(c: CallableType, ids: list[TypeVarId]) -> CallableType: tv_map = {} tvs = [] diff --git a/mypy/literals.py b/mypy/literals.py index cba5712644be2..fd17e04714403 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Final, Iterable, Optional, Tuple +from collections.abc import Iterable +from typing import Any, Final, Optional from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( @@ -47,6 +48,7 @@ TypeAliasExpr, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeVarExpr, TypeVarTupleExpr, UnaryExpr, @@ -95,7 +97,27 @@ # of an index expression, or the operands of an operator expression). +Key: _TypeAlias = tuple[Any, ...] + + +def literal_hash(e: Expression) -> Key | None: + """Generate a hashable, (mostly) opaque key for expressions supported by the binder. + + These allow using expressions as dictionary keys based on structural/value + matching (instead of based on expression identity). + + Return None if the expression type is not supported (it cannot be narrowed). + + See the comment above for more information. + + NOTE: This is not directly related to literal types. + """ + return e.accept(_hasher) + + def literal(e: Expression) -> int: + """Return the literal kind for an expression.""" + if isinstance(e, ComparisonExpr): return min(literal(o) for o in e.operands) @@ -128,17 +150,10 @@ def literal(e: Expression) -> int: return LITERAL_NO -Key: _TypeAlias = Tuple[Any, ...] - - def subkeys(key: Key) -> Iterable[Key]: return [elt for elt in key if isinstance(elt, tuple)] -def literal_hash(e: Expression) -> Key | None: - return e.accept(_hasher) - - def extract_var_from_literal_hash(key: Key) -> Var | None: """If key refers to a Var node, return it. @@ -230,6 +245,9 @@ def visit_slice_expr(self, e: SliceExpr) -> None: def visit_cast_expr(self, e: CastExpr) -> None: return None + def visit_type_form_expr(self, e: TypeFormExpr) -> None: + return None + def visit_assert_type_expr(self, e: AssertTypeExpr) -> None: return None diff --git a/mypy/lookup.py b/mypy/lookup.py index 8fc8cf8be3c24..640481ff703c6 100644 --- a/mypy/lookup.py +++ b/mypy/lookup.py @@ -22,9 +22,11 @@ def lookup_fully_qualified( This function should *not* be used to find a module. Those should be looked in the modules dictionary. """ - head = name + # 1. Exclude the names of ad hoc instance intersections from step 2. + i = name.find(" os.stat_result: @@ -63,10 +85,14 @@ def main( util.check_python_version("mypy") t0 = time.time() # To log stat() calls: os.stat = stat_proxy - sys.setrecursionlimit(2**14) + sys.setrecursionlimit(RECURSION_LIMIT) if args is None: args = sys.argv[1:] + # Write an escape sequence instead of raising an exception on encoding errors. + if isinstance(stdout, TextIOWrapper) and stdout.errors == "strict": + stdout.reconfigure(errors="backslashreplace") + fscache = FileSystemCache() sources, options = process_options(args, stdout=stdout, stderr=stderr, fscache=fscache) if clean_exit: @@ -76,6 +102,13 @@ def main( stdout, stderr, options.hide_error_codes, hide_success=bool(options.output) ) + if options.allow_redefinition_new and not options.local_partial_types: + fail( + "error: --local-partial-types must be enabled if using --allow-redefinition-new", + stderr, + options, + ) + if options.install_types and (stdout is not sys.stdout or stderr is not sys.stderr): # Since --install-types performs user input, we want regular stdout and stderr. fail("error: --install-types not supported in this mode of running mypy", stderr, options) @@ -224,17 +257,26 @@ def show_messages( # Make the help output a little less jarring. class AugmentedHelpFormatter(argparse.RawDescriptionHelpFormatter): - def __init__(self, prog: str) -> None: - super().__init__(prog=prog, max_help_position=28) + def __init__(self, prog: str, **kwargs: Any) -> None: + super().__init__(prog=prog, max_help_position=28, **kwargs) def _fill_text(self, text: str, width: int, indent: str) -> str: if "\n" in text: # Assume we want to manually format the text return super()._fill_text(text, width, indent) - else: - # Assume we want argparse to manage wrapping, indenting, and - # formatting the text for us. - return argparse.HelpFormatter._fill_text(self, text, width, indent) + # Format the text like argparse, but overflow rather than + # breaking long words (like URLs) + text = self._whitespace_matcher.sub(" ", text).strip() + import textwrap + + return textwrap.fill( + text, + width, + initial_indent=indent, + subsequent_indent=indent, + break_on_hyphens=False, + break_long_words=False, + ) # Define pairs of flag prefixes with inverse meaning. @@ -360,17 +402,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # ===================== # Help-printing methods # ===================== - def print_usage(self, file: IO[str] | None = None) -> None: + def print_usage(self, file: SupportsWrite[str] | None = None) -> None: if file is None: file = self.stdout self._print_message(self.format_usage(), file) - def print_help(self, file: IO[str] | None = None) -> None: + def print_help(self, file: SupportsWrite[str] | None = None) -> None: if file is None: file = self.stdout self._print_message(self.format_help(), file) - def _print_message(self, message: str, file: IO[str] | None = None) -> None: + def _print_message(self, message: str, file: SupportsWrite[str] | None = None) -> None: if message: if file is None: file = self.stderr @@ -437,24 +479,18 @@ def __call__( parser.exit() -def process_options( - args: list[str], - stdout: TextIO | None = None, - stderr: TextIO | None = None, - require_targets: bool = True, - server_options: bool = False, - fscache: FileSystemCache | None = None, +def define_options( program: str = "mypy", header: str = HEADER, -) -> tuple[list[BuildSource], Options]: - """Parse command line arguments. - - If a FileSystemCache is passed in, and package_root options are given, - call fscache.set_package_root() to set the cache's package root. - """ - stdout = stdout or sys.stdout - stderr = stderr or sys.stderr - + stdout: TextIO = sys.stdout, + stderr: TextIO = sys.stderr, + server_options: bool = False, +) -> tuple[CapturableArgumentParser, list[str], list[tuple[str, bool]]]: + """Define the options in the parser (by calling a bunch of methods that express/build our desired command-line flags). + Returns a tuple of: + a parser object, that can parse command line arguments to mypy (expected consumer: main's process_options), + a list of what flags are strict (expected consumer: docs' html_builder's _add_strict_list), + strict_flag_assignments (expected consumer: main's process_options).""" parser = CapturableArgumentParser( prog=program, usage=header, @@ -508,8 +544,32 @@ def add_invertible_flag( # their `dest` prefixed with `special-opts:`, which will cause them to be # parsed into the separate special_opts namespace object. - # Note: we have a style guide for formatting the mypy --help text. See - # https://github.com/python/mypy/wiki/Documentation-Conventions + # Our style guide for formatting the output of running `mypy --help`: + # Flags: + # 1. The flag help text should start with a capital letter but never end with a period. + # 2. Keep the flag help text brief -- ideally just a single sentence. + # 3. All flags must be a part of a group, unless the flag is deprecated or suppressed. + # 4. Avoid adding new flags to the "miscellaneous" groups -- instead add them to an + # existing group or, if applicable, create a new group. Feel free to move existing + # flags to a new group: just be sure to also update the documentation to match. + # + # Groups: + # 1. The group title and description should start with a capital letter. + # 2. The first sentence of a group description should be written in the bare infinitive. + # Tip: try substituting the group title and description into the following sentence: + # > {group_title}: these flags will {group_description} + # Feel free to add subsequent sentences that add additional details. + # 3. If you cannot think of a meaningful description for a new group, omit it entirely. + # (E.g. see the "miscellaneous" sections). + # 4. The text of the group description should end with a period, optionally followed + # by a documentation reference (URL). + # 5. If you want to include a documentation reference, place it at the end of the + # description. Feel free to open with a brief reference ("See also:", "For more + # information:", etc.), followed by a space, then the entire URL including + # "https://" scheme identifier and fragment ("#some-target-heading"), if any. + # Do not end with a period (or any other characters not part of the URL). + # URLs longer than the available terminal width will overflow without being + # broken apart. This facilitates both URL detection, and manual copy-pasting. general_group = parser.add_argument_group(title="Optional arguments") general_group.add_argument( @@ -545,15 +605,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_NAMES + defaults.SHARED_CONFIG_NAMES)})" + ), ) 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, ) @@ -572,6 +633,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"], @@ -582,8 +648,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( @@ -616,8 +681,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", @@ -638,12 +702,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, @@ -654,8 +712,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", @@ -670,6 +727,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, @@ -736,7 +799,7 @@ def add_invertible_flag( title="None and Optional handling", description="Adjust how values of type 'None' are handled. For more context on " "how mypy handles values of type 'None', see: " - "https://mypy.readthedocs.io/en/stable/kinds_of_types.html#no-strict-optional", + "https://mypy.readthedocs.io/en/stable/kinds_of_types.html#optional-types-and-the-none-type", ) add_invertible_flag( "--implicit-optional", @@ -752,6 +815,7 @@ def add_invertible_flag( help="Disable strict Optional checks (inverse: --strict-optional)", ) + # This flag is deprecated, Mypy only supports Python 3.9+ add_invertible_flag( "--force-uppercase-builtins", default=False, help=argparse.SUPPRESS, group=none_group ) @@ -799,6 +863,21 @@ 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, + ) + lint_group.add_argument( + "--deprecated-calls-exclude", + metavar="MODULE", + action="append", + default=[], + help="Disable deprecated warnings for functions/methods coming" + " from specific package, module, or class", + ) # Note: this group is intentionally added here even though we don't add # --strict to this group near the end. @@ -821,7 +900,15 @@ def add_invertible_flag( "--allow-redefinition", default=False, strict_flag=False, - help="Allow unconditional variable redefinition with a new type", + help="Allow restricted, unconditional variable redefinition with a new type", + group=strictness_group, + ) + + add_invertible_flag( + "--allow-redefinition-new", + default=False, + strict_flag=False, + help="Allow more flexible variable redefinition semantics (experimental)", group=strictness_group, ) @@ -838,7 +925,24 @@ def add_invertible_flag( "--strict-equality", default=False, strict_flag=True, - help="Prohibit equality, identity, and container checks for non-overlapping types", + help="Prohibit equality, identity, and container checks for non-overlapping types " + "(except `None`)", + group=strictness_group, + ) + + add_invertible_flag( + "--strict-equality-for-none", + default=False, + strict_flag=False, + help="Extend `--strict-equality` for `None` checks", + group=strictness_group, + ) + + add_invertible_flag( + "--strict-bytes", + default=False, + strict_flag=True, + help="Disable treating bytearray and memoryview as subtypes of bytes", group=strictness_group, ) @@ -952,7 +1056,7 @@ def add_invertible_flag( "Mypy caches type information about modules into a cache to " "let you speed up future invocations of mypy. Also see " "mypy's daemon mode: " - "mypy.readthedocs.io/en/stable/mypy_daemon.html#mypy-daemon", + "https://mypy.readthedocs.io/en/stable/mypy_daemon.html#mypy-daemon", ) incremental_group.add_argument( "-i", "--incremental", action="store_true", help=argparse.SUPPRESS @@ -981,6 +1085,11 @@ def add_invertible_flag( action="store_true", help="Include fine-grained dependency information in the cache for the mypy daemon", ) + incremental_group.add_argument( + "--fixed-format-cache", + action="store_true", + help="Use new fast and compact fixed format cache", + ) incremental_group.add_argument( "--skip-version-check", action="store_true", @@ -1009,13 +1118,10 @@ def add_invertible_flag( help="Use a custom typing module", ) internals_group.add_argument( - "--old-type-inference", - action="store_true", - help="Disable new experimental type inference algorithm", + "--old-type-inference", action="store_true", help=argparse.SUPPRESS ) - # Deprecated reverse variant of the above. internals_group.add_argument( - "--new-type-inference", action="store_true", help=argparse.SUPPRESS + "--disable-expression-cache", action="store_true", help=argparse.SUPPRESS ) parser.add_argument( "--enable-incomplete-feature", @@ -1065,22 +1171,36 @@ def add_invertible_flag( dest=f"special-opts:{report_type}_report", ) - other_group = parser.add_argument_group(title="Miscellaneous") - other_group.add_argument("--quickstart-file", help=argparse.SUPPRESS) - other_group.add_argument("--junit-xml", help="Write junit.xml to the given file") - imports_group.add_argument( + # Undocumented mypyc feature: generate annotated HTML source file + report_group.add_argument( + "-a", dest="mypyc_annotation_file", type=str, default=None, help=argparse.SUPPRESS + ) + # Hidden mypyc feature: do not write any C files (keep existing ones and assume they exist). + # This can be useful when debugging mypyc bugs. + report_group.add_argument( + "--skip-c-gen", dest="mypyc_skip_c_generation", action="store_true", help=argparse.SUPPRESS + ) + + misc_group = parser.add_argument_group(title="Miscellaneous") + misc_group.add_argument("--quickstart-file", help=argparse.SUPPRESS) + misc_group.add_argument( + "--junit-xml", + metavar="JUNIT_XML_OUTPUT_FILE", + help="Write a JUnit XML test result document with type checking results to the given file", + ) + misc_group.add_argument( "--junit-format", choices=["global", "per_file"], default="global", - help="If --junit-xml is set, specifies format. global: single test with all errors; per_file: one test entry per file with failures", + help="If --junit-xml is set, specifies format. global (default): single test with all errors; per_file: one test entry per file with failures", ) - other_group.add_argument( + misc_group.add_argument( "--find-occurrences", metavar="CLASS.MEMBER", dest="special-opts:find_occurrences", help="Print out all usages of a class member (experimental)", ) - other_group.add_argument( + misc_group.add_argument( "--scripts-are-modules", action="store_true", help="Script x becomes module x instead of __main__", @@ -1091,7 +1211,7 @@ def add_invertible_flag( default=False, strict_flag=False, help="Install detected missing library stub packages using pip", - group=other_group, + group=misc_group, ) add_invertible_flag( "--non-interactive", @@ -1101,19 +1221,12 @@ def add_invertible_flag( "Install stubs without asking for confirmation and hide " + "errors, with --install-types" ), - group=other_group, + group=misc_group, inverse="--interactive", ) if server_options: - # TODO: This flag is superfluous; remove after a short transition (2018-03-16) - other_group.add_argument( - "--experimental", - action="store_true", - dest="fine_grained_incremental", - help="Enable fine-grained incremental mode", - ) - other_group.add_argument( + misc_group.add_argument( "--use-fine-grained-cache", action="store_true", help="Use the cache in fine-grained incremental mode", @@ -1149,7 +1262,7 @@ def add_invertible_flag( parser.add_argument("--test-env", action="store_true", help=argparse.SUPPRESS) # --local-partial-types disallows partial types spanning module top level and a function # (implicitly defined in fine-grained incremental mode) - parser.add_argument("--local-partial-types", action="store_true", help=argparse.SUPPRESS) + add_invertible_flag("--local-partial-types", default=False, help=argparse.SUPPRESS) # --logical-deps adds some more dependencies that are not semantically needed, but # may be helpful to determine relative importance of classes and functions for overall # type precision in a code base. It also _removes_ some deps, so this flag should be never @@ -1187,7 +1300,7 @@ def add_invertible_flag( code_group = parser.add_argument_group( title="Running code", description="Specify the code you want to type check. For more details, see " - "mypy.readthedocs.io/en/stable/running_mypy.html#running-mypy", + "https://mypy.readthedocs.io/en/stable/running_mypy.html#running-mypy", ) add_invertible_flag( "--explicit-package-bases", @@ -1209,6 +1322,15 @@ def add_invertible_flag( "May be specified more than once, eg. --exclude a --exclude b" ), ) + add_invertible_flag( + "--exclude-gitignore", + default=False, + help=( + "Use .gitignore file(s) to exclude files from checking " + "(in addition to any explicit --exclude if present)" + ), + group=code_group, + ) code_group.add_argument( "-m", "--module", @@ -1241,6 +1363,32 @@ def add_invertible_flag( dest="special-opts:files", help="Type-check given files or directories", ) + return parser, strict_flag_names, strict_flag_assignments + + +def process_options( + args: list[str], + stdout: TextIO | None = None, + stderr: TextIO | None = None, + require_targets: bool = True, + server_options: bool = False, + fscache: FileSystemCache | None = None, + program: str = "mypy", + header: str = HEADER, +) -> tuple[list[BuildSource], Options]: + """Parse command line arguments. + + If a FileSystemCache is passed in, and package_root options are given, + call fscache.set_package_root() to set the cache's package root. + + Returns a tuple of: a list of source files, an Options collected from flags. + """ + stdout = stdout if stdout is not None else sys.stdout + stderr = stderr if stderr is not None else sys.stderr + + parser, _, strict_flag_assignments = define_options( + program, header, stdout, stderr, server_options + ) # Parse arguments once into a dummy namespace so we can get the # filename for the config file and know if the user requested all strict options. @@ -1334,8 +1482,8 @@ def set_strict_flags() -> None: ) validate_package_allow_list(options.untyped_calls_exclude) + validate_package_allow_list(options.deprecated_calls_exclude) - 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). @@ -1369,6 +1517,9 @@ def set_strict_flags() -> None: process_cache_map(parser, special_opts, options) + # Process --strict-bytes + options.process_strict_bytes() + # An explicitly specified cache_fine_grained implies local_partial_types # (because otherwise the cache is not compatible with dmypy) if options.cache_fine_grained: @@ -1382,15 +1533,12 @@ def set_strict_flags() -> None: if options.logical_deps: options.cache_fine_grained = True - if options.new_type_inference: - print( - "Warning: --new-type-inference flag is deprecated;" - " new type inference algorithm is already enabled by default" - ) - if options.strict_concatenate and not strict_option_set: print("Warning: --strict-concatenate is deprecated; use --extra-checks instead") + if options.force_uppercase_builtins: + print("Warning: --force-uppercase-builtins is deprecated; mypy only supports Python 3.9+") + # Set target. if special_opts.modules + special_opts.packages: options.build_type = BuildType.MODULE @@ -1406,15 +1554,21 @@ 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)) - return targets, options elif special_opts.command: options.build_type = BuildType.PROGRAM_TEXT targets = [BuildSource(None, None, "\n".join(special_opts.command))] - return targets, options else: try: targets = create_source_list(special_opts.files, options, fscache) @@ -1423,7 +1577,7 @@ def set_strict_flags() -> None: # exceptions of different types. except InvalidSourceList as e2: fail(str(e2), stderr, options) - return targets, options + return targets, options def process_package_roots( @@ -1457,7 +1611,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) @@ -1527,8 +1681,9 @@ def read_types_packages_to_install(cache_dir: str, after_run: bool) -> list[str] + "(and no cache from previous mypy run)\n" ) else: - sys.stderr.write("error: --install-types failed (no mypy cache directory)\n") - sys.exit(2) + sys.stderr.write( + "error: --install-types failed (an error blocked analysis of which types to install)\n" + ) fnam = build.missing_stubs_file(cache_dir) if not os.path.isfile(fnam): # No missing stubs. diff --git a/mypy/meet.py b/mypy/meet.py index 9f5c2d72a8cbd..42229f9b23c15 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -50,6 +50,8 @@ find_unpack_in_list, get_proper_type, get_proper_types, + has_type_vars, + is_named_instance, split_with_prefix_and_suffix, ) @@ -114,8 +116,11 @@ def meet_types(s: Type, t: Type) -> ProperType: def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" # TODO: check infinite recursion for aliases here. - if isinstance(narrowed, TypeGuardedType): # type: ignore[misc] - # A type guard forces the new type even if it doesn't overlap the old. + if isinstance(narrowed, TypeGuardedType): + # A type guard forces the new type even if it doesn't overlap the old... + if is_proper_subtype(declared, narrowed.type_guard, ignore_promotions=True): + # ...unless it is a proper supertype of declared type. + return declared return narrowed.type_guard original_declared = declared @@ -126,23 +131,48 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type: if declared == narrowed: return original_declared if isinstance(declared, UnionType): + declared_items = declared.relevant_items() + if isinstance(narrowed, UnionType): + narrowed_items = narrowed.relevant_items() + else: + narrowed_items = [narrowed] return make_simplified_union( [ - narrow_declared_type(x, narrowed) - for x in declared.relevant_items() + narrow_declared_type(d, n) + for d in declared_items + for n in narrowed_items # This (ugly) special-casing is needed to support checking # branches like this: # x: Union[float, complex] # if isinstance(x, int): # ... + # And assignments like this: + # x: float | None + # y: int | None + # x = y if ( - is_overlapping_types(x, narrowed, ignore_promotions=True) - or is_subtype(narrowed, x, ignore_promotions=False) + is_overlapping_types(d, n, ignore_promotions=True) + or is_subtype(n, d, ignore_promotions=False) ) ] ) if is_enum_overlapping_union(declared, narrowed): - return original_narrowed + # Quick check before reaching `is_overlapping_types`. If it's enum/literal overlap, + # avoid full expansion and make it faster. + assert isinstance(narrowed, UnionType) + return make_simplified_union( + [narrow_declared_type(declared, x) for x in narrowed.relevant_items()] + ) + elif ( + isinstance(declared, TypeVarType) + and not has_type_vars(original_narrowed) + and is_subtype(original_narrowed, declared.upper_bound) + ): + # We put this branch early to get T(bound=Union[A, B]) instead of + # Union[T(bound=A), T(bound=B)] that will be confusing for users. + return declared.copy_modified( + upper_bound=narrow_declared_type(declared.upper_bound, original_narrowed) + ) elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() @@ -157,12 +187,24 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type: elif isinstance(narrowed, TypeVarType) and is_subtype(narrowed.upper_bound, declared): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): - return TypeType.make_normalized(narrow_declared_type(declared.item, narrowed.item)) + return TypeType.make_normalized( + narrow_declared_type(declared.item, narrowed.item), + is_type_form=declared.is_type_form and narrowed.is_type_form, + ) elif ( isinstance(declared, TypeType) and isinstance(narrowed, Instance) and narrowed.type.is_metaclass() ): + if declared.is_type_form: + # The declared TypeForm[T] after narrowing must be a kind of + # type object at least as narrow as Type[T] + return narrow_declared_type( + TypeType.make_normalized( + declared.item, line=declared.line, column=declared.column, is_type_form=False + ), + original_narrowed, + ) # We'd need intersection types, so give up. return original_declared elif isinstance(declared, Instance): @@ -223,7 +265,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 +289,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() ) ) @@ -262,6 +308,31 @@ def is_object(t: ProperType) -> bool: return isinstance(t, Instance) and t.type.fullname == "builtins.object" +def is_none_typevarlike_overlap(t1: ProperType, t2: ProperType) -> bool: + return isinstance(t1, NoneType) and isinstance(t2, TypeVarLikeType) + + +def is_none_object_overlap(t1: ProperType, t2: ProperType) -> bool: + return ( + isinstance(t1, NoneType) + and isinstance(t2, Instance) + and t2.type.fullname == "builtins.object" + ) + + +def are_related_types( + left: Type, right: Type, *, proper_subtype: bool, ignore_promotions: bool +) -> bool: + if proper_subtype: + return is_proper_subtype( + left, right, ignore_promotions=ignore_promotions + ) or is_proper_subtype(right, left, ignore_promotions=ignore_promotions) + else: + return is_subtype(left, right, ignore_promotions=ignore_promotions) or is_subtype( + right, left, ignore_promotions=ignore_promotions + ) + + def is_overlapping_types( left: Type, right: Type, @@ -279,35 +350,19 @@ def is_overlapping_types( positives), for example: None only overlaps with explicitly optional types, Any doesn't overlap with anything except object, we don't ignore positional argument names. """ - if isinstance(left, TypeGuardedType) or isinstance( # type: ignore[misc] - right, TypeGuardedType - ): + if isinstance(left, TypeGuardedType) or isinstance(right, TypeGuardedType): # A type guard forces the new type even if it doesn't overlap the old. return True if seen_types is None: seen_types = set() - if (left, right) in seen_types: + elif (left, right) in seen_types: return True - if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType): + if is_recursive_pair(left, right): seen_types.add((left, right)) left, right = get_proper_types((left, right)) - def _is_overlapping_types(left: Type, right: Type) -> bool: - """Encode the kind of overlapping check to perform. - - This function mostly exists, so we don't have to repeat keyword arguments everywhere. - """ - return is_overlapping_types( - left, - right, - ignore_promotions=ignore_promotions, - prohibit_none_typevar_overlap=prohibit_none_typevar_overlap, - overlap_for_overloads=overlap_for_overloads, - seen_types=seen_types.copy(), - ) - # We should never encounter this type. if isinstance(left, PartialType) or isinstance(right, PartialType): assert False, "Unexpectedly encountered partial type" @@ -353,25 +408,13 @@ def _is_overlapping_types(left: Type, right: Type) -> bool: ): return True - def is_none_object_overlap(t1: Type, t2: Type) -> bool: - t1, t2 = get_proper_types((t1, t2)) - return ( - isinstance(t1, NoneType) - and isinstance(t2, Instance) - and t2.type.fullname == "builtins.object" - ) - if overlap_for_overloads: if is_none_object_overlap(left, right) or is_none_object_overlap(right, left): return False - def _is_subtype(left: Type, right: Type) -> bool: - if overlap_for_overloads: - return is_proper_subtype(left, right, ignore_promotions=ignore_promotions) - else: - return is_subtype(left, right, ignore_promotions=ignore_promotions) - - if _is_subtype(left, right) or _is_subtype(right, left): + if are_related_types( + left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions + ): return True # See the docstring for 'get_possible_variants' for more info on what the @@ -390,14 +433,24 @@ def _is_subtype(left: Type, right: Type) -> bool: # If both types are singleton variants (and are not TypeVarLikes), we've hit the base case: # we skip these checks to avoid infinitely recursing. - def is_none_typevarlike_overlap(t1: Type, t2: Type) -> bool: - t1, t2 = get_proper_types((t1, t2)) - return isinstance(t1, NoneType) and isinstance(t2, TypeVarLikeType) - if prohibit_none_typevar_overlap: if is_none_typevarlike_overlap(left, right) or is_none_typevarlike_overlap(right, left): return False + def _is_overlapping_types(left: Type, right: Type) -> bool: + """Encode the kind of overlapping check to perform. + + This function mostly exists, so we don't have to repeat keyword arguments everywhere. + """ + return is_overlapping_types( + left, + right, + ignore_promotions=ignore_promotions, + prohibit_none_typevar_overlap=prohibit_none_typevar_overlap, + overlap_for_overloads=overlap_for_overloads, + seen_types=seen_types.copy(), + ) + if ( len(left_possible) > 1 or len(right_possible) > 1 @@ -453,27 +506,28 @@ def is_none_typevarlike_overlap(t1: Type, t2: Type) -> bool: if isinstance(left, TypeType) and isinstance(right, TypeType): return _is_overlapping_types(left.item, right.item) - def _type_object_overlap(left: Type, right: Type) -> bool: - """Special cases for type object types overlaps.""" - # TODO: these checks are a bit in gray area, adjust if they cause problems. - left, right = get_proper_types((left, right)) - # 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object. - if isinstance(left, TypeType) and isinstance(right, CallableType): - return _is_overlapping_types(left.item, right.ret_type) - # 2. Type[C] vs Meta, where Meta is a metaclass for C. - if isinstance(left, TypeType) and isinstance(right, Instance): - if isinstance(left.item, Instance): - left_meta = left.item.type.metaclass_type - if left_meta is not None: - return _is_overlapping_types(left_meta, right) - # builtins.type (default metaclass) overlaps with all metaclasses - return right.type.has_base("builtins.type") - elif isinstance(left.item, AnyType): - return right.type.has_base("builtins.type") - # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks. - return False - if isinstance(left, TypeType) or isinstance(right, TypeType): + + def _type_object_overlap(left: Type, right: Type) -> bool: + """Special cases for type object types overlaps.""" + # TODO: these checks are a bit in gray area, adjust if they cause problems. + left, right = get_proper_types((left, right)) + # 1. Type[C] vs Callable[..., C] overlap even if the latter is not class object. + if isinstance(left, TypeType) and isinstance(right, CallableType): + return _is_overlapping_types(left.item, right.ret_type) + # 2. Type[C] vs Meta, where Meta is a metaclass for C. + if isinstance(left, TypeType) and isinstance(right, Instance): + if isinstance(left.item, Instance): + left_meta = left.item.type.metaclass_type + if left_meta is not None: + return _is_overlapping_types(left_meta, right) + # builtins.type (default metaclass) overlaps with all metaclasses + return right.type.has_base("builtins.type") + elif isinstance(left.item, AnyType): + return right.type.has_base("builtins.type") + # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks. + return False + return _type_object_overlap(left, right) or _type_object_overlap(right, left) if isinstance(left, Parameters) and isinstance(right, Parameters): @@ -534,7 +588,9 @@ def _type_object_overlap(left: Type, right: Type) -> bool: if isinstance(left, Instance) and isinstance(right, Instance): # First we need to handle promotions and structural compatibility for instances # that came as fallbacks, so simply call is_subtype() to avoid code duplication. - if _is_subtype(left, right) or _is_subtype(right, left): + if are_related_types( + left, right, proper_subtype=overlap_for_overloads, ignore_promotions=ignore_promotions + ): return True if right.type.fullname == "builtins.int" and left.type.fullname in MYPYC_NATIVE_INT_NAMES: @@ -548,7 +604,27 @@ def _type_object_overlap(left: Type, right: Type) -> bool: else: return False - if len(left.args) == len(right.args): + if right.type.has_type_var_tuple_type: + # Similar to subtyping, we delegate the heavy lifting to the tuple overlap. + assert right.type.type_var_tuple_prefix is not None + assert right.type.type_var_tuple_suffix is not None + prefix = right.type.type_var_tuple_prefix + suffix = right.type.type_var_tuple_suffix + tvt = right.type.defn.type_vars[prefix] + assert isinstance(tvt, TypeVarTupleType) + fallback = tvt.tuple_fallback + left_prefix, left_middle, left_suffix = split_with_prefix_and_suffix( + left.args, prefix, suffix + ) + right_prefix, right_middle, right_suffix = split_with_prefix_and_suffix( + right.args, prefix, suffix + ) + left_args = left_prefix + (TupleType(list(left_middle), fallback),) + left_suffix + right_args = right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix + else: + left_args = left.args + right_args = right.args + if len(left_args) == len(right_args): # Note: we don't really care about variance here, since the overlapping check # is symmetric and since we want to return 'True' even for partial overlaps. # @@ -565,7 +641,7 @@ def _type_object_overlap(left: Type, right: Type) -> bool: # to contain only instances of B at runtime. if all( _is_overlapping_types(left_arg, right_arg) - for left_arg, right_arg in zip(left.args, right.args) + for left_arg, right_arg in zip(left_args, right_args) ): return True @@ -641,7 +717,16 @@ def are_tuples_overlapping( if len(left.items) != len(right.items): return False - return all(is_overlapping(l, r) for l, r in zip(left.items, right.items)) + if not all(is_overlapping(l, r) for l, r in zip(left.items, right.items)): + return False + + # Check that the tuples aren't from e.g. different NamedTuples. + if is_named_instance(right.partial_fallback, "builtins.tuple") or is_named_instance( + left.partial_fallback, "builtins.tuple" + ): + return True + else: + return is_overlapping(left.partial_fallback, right.partial_fallback) def expand_tuple_if_possible(tup: TupleType, target: int) -> TupleType: @@ -687,7 +772,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): @@ -738,7 +823,9 @@ def visit_erased_type(self, t: ErasedType) -> ProperType: def visit_type_var(self, t: TypeVarType) -> ProperType: if isinstance(self.s, TypeVarType) and self.s.id == t.id: - return self.s + if self.s.upper_bound == t.upper_bound: + return self.s + return self.s.copy_modified(upper_bound=self.meet(self.s.upper_bound, t.upper_bound)) else: return self.default(self.s) @@ -1040,7 +1127,9 @@ def visit_type_type(self, t: TypeType) -> ProperType: if isinstance(self.s, TypeType): typ = self.meet(t.item, self.s.item) if not isinstance(typ, NoneType): - typ = TypeType.make_normalized(typ, line=t.line) + typ = TypeType.make_normalized( + typ, line=t.line, is_type_form=self.s.is_type_form and t.is_type_form + ) return typ elif isinstance(self.s, Instance) and self.s.type.fullname == "builtins.type": return t diff --git a/mypy/memprofile.py b/mypy/memprofile.py index 48c0cb5ce0228..4bab4ecb262ee 100644 --- a/mypy/memprofile.py +++ b/mypy/memprofile.py @@ -9,7 +9,8 @@ import gc import sys from collections import defaultdict -from typing import Dict, Iterable, cast +from collections.abc import Iterable +from typing import cast from mypy.nodes import FakeInfo, Node from mypy.types import Type @@ -108,7 +109,7 @@ def visit(o: object) -> None: # Processing these would cause a crash. continue if type(obj) in (dict, defaultdict): - for key, val in cast(Dict[object, object], obj).items(): + for key, val in cast(dict[object, object], obj).items(): visit(key) visit(val) if type(obj) in (list, tuple, set): diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 507af6fdad74c..b0f9ed1b0dfee 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -11,11 +11,12 @@ from typing import Final, NamedTuple from mypy import errorcodes as codes +from mypy.errorcodes import ErrorCode class ErrorMessage(NamedTuple): value: str - code: codes.ErrorCode | None = None + code: ErrorCode | None = None def format(self, *args: object, **kwargs: object) -> ErrorMessage: return ErrorMessage(self.value.format(*args, **kwargs), code=self.code) @@ -188,7 +189,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: # TypeVar INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}' -CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = 'Type variable "{}.{}" cannot be used as an expression' INVALID_TYPEVAR_AS_TYPEARG: Final = 'Type variable "{}" not valid as type argument value for "{}"' INVALID_TYPEVAR_ARG_BOUND: Final = 'Type argument {} of "{}" must be a subtype of {}' INVALID_TYPEVAR_ARG_VALUE: Final = 'Invalid type argument value for "{}"' @@ -196,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)" @@ -241,8 +240,11 @@ def with_additional_msg(self, info: str) -> ErrorMessage: ) CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final") +# Disjoint bases +INCOMPATIBLE_DISJOINT_BASES: Final = ErrorMessage('Class "{}" has incompatible disjoint bases') + # Enum -ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN: Final = ErrorMessage( +ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDDEN: Final = ErrorMessage( 'Assigned "__members__" will be overridden by "Enum" internally' ) @@ -255,7 +257,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: 'Cannot override class variable (previously declared on base class "{}") with instance ' "variable" ) -CLASS_VAR_WITH_TYPEVARS: Final = "ClassVar cannot contain type variables" CLASS_VAR_WITH_GENERIC_SELF: Final = "ClassVar cannot contain Self type in generic classes" CLASS_VAR_OUTSIDE_OF_CLASS: Final = "ClassVar can only be used for assignments in class body" @@ -351,6 +352,14 @@ def with_additional_msg(self, info: str) -> 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_VAR_REDECLARED_IN_NESTED_CLASS: Final = ErrorMessage( + 'Type variable "{}" is bound by an outer class', codes.VALID_TYPE +) + TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage( "Yield expression cannot be used within a type alias", codes.SYNTAX ) @@ -362,8 +371,3 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPE_ALIAS_WITH_AWAIT_EXPRESSION: Final = ErrorMessage( "Await expression cannot be used within a type alias", codes.SYNTAX ) - -TYPE_PARAM_DEFAULT_NOT_SUPPORTED: Final = ErrorMessage( - "Type parameter default types not supported when using Python 3.12 type parameter syntax", - codes.MISC, -) diff --git a/mypy/messages.py b/mypy/messages.py index adf150eab50ac..9fdfb748b2882 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -14,15 +14,22 @@ import difflib import itertools import re +from collections.abc import Collection, Iterable, Iterator, Sequence from contextlib import contextmanager from textwrap import dedent -from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, Sequence, cast +from typing import Any, Callable, Final, cast import mypy.typeops from mypy import errorcodes as codes, message_registry from mypy.erasetype import erase_type from mypy.errorcodes import ErrorCode -from mypy.errors import ErrorInfo, Errors, ErrorWatcher +from mypy.errors import ( + ErrorInfo, + Errors, + ErrorWatcher, + IterationDependentErrors, + IterationErrorWatcher, +) from mypy.nodes import ( ARG_NAMED, ARG_NAMED_OPT, @@ -48,6 +55,7 @@ SymbolTable, TypeInfo, Var, + get_func_def, reverse_builtin_aliases, ) from mypy.operators import op_methods, op_methods_to_symbols @@ -55,6 +63,7 @@ from mypy.subtypes import ( IS_CLASS_OR_STATIC, IS_CLASSVAR, + IS_EXPLICIT_SETTER, IS_SETTABLE, IS_VAR, find_member, @@ -185,9 +194,15 @@ def filter_errors( *, filter_errors: bool | Callable[[str, ErrorInfo], bool] = True, save_filtered_errors: bool = False, + filter_deprecated: bool = False, + filter_revealed_type: bool = False, ) -> ErrorWatcher: return ErrorWatcher( - self.errors, filter_errors=filter_errors, save_filtered_errors=save_filtered_errors + self.errors, + filter_errors=filter_errors, + save_filtered_errors=save_filtered_errors, + filter_deprecated=filter_deprecated, + filter_revealed_type=filter_revealed_type, ) def add_errors(self, errors: list[ErrorInfo]) -> None: @@ -209,7 +224,7 @@ def are_type_names_disabled(self) -> bool: def prefer_simple_messages(self) -> bool: """Should we generate simple/fast error messages? - If errors aren't shown to the user, we don't want to waste cyles producing + If errors aren't shown to the user, we don't want to waste cycles producing complex error messages. """ return self.errors.prefer_simple_messages() @@ -224,9 +239,9 @@ def report( file: str | None = None, origin: Context | None = None, offset: int = 0, - allow_dups: bool = False, secondary_context: Context | None = None, - ) -> None: + parent_error: ErrorInfo | None = None, + ) -> ErrorInfo: """Report an error or note (unless disabled). Note that context controls where error is reported, while origin controls @@ -243,7 +258,7 @@ def span_from_context(ctx: Context) -> Iterable[int]: TODO: address this in follow up PR """ if isinstance(ctx, (ClassDef, FuncDef)): - return range(ctx.deco_line or ctx.line, ctx.line + 1) + return range(ctx.line, ctx.line + 1) elif not isinstance(ctx, Expression): return [ctx.line] else: @@ -261,7 +276,7 @@ def span_from_context(ctx: Context) -> Iterable[int]: assert origin_span is not None origin_span = itertools.chain(origin_span, span_from_context(secondary_context)) - self.errors.report( + return self.errors.report( context.line if context else -1, context.column if context else -1, msg, @@ -272,7 +287,7 @@ def span_from_context(ctx: Context) -> Iterable[int]: end_line=context.end_line if context else -1, end_column=context.end_column if context else -1, code=code, - allow_dups=allow_dups, + parent_error=parent_error, ) def fail( @@ -282,18 +297,11 @@ def fail( *, code: ErrorCode | None = None, file: str | None = None, - allow_dups: bool = False, secondary_context: Context | None = None, - ) -> None: + ) -> ErrorInfo: """Report an error message (unless disabled).""" - self.report( - msg, - context, - "error", - code=code, - file=file, - allow_dups=allow_dups, - secondary_context=secondary_context, + return self.report( + msg, context, "error", code=code, file=file, secondary_context=secondary_context ) def note( @@ -303,10 +311,10 @@ def note( file: str | None = None, origin: Context | None = None, offset: int = 0, - allow_dups: bool = False, *, code: ErrorCode | None = None, secondary_context: Context | None = None, + parent_error: ErrorInfo | None = None, ) -> None: """Report a note (unless disabled).""" self.report( @@ -316,9 +324,9 @@ def note( file=file, origin=origin, offset=offset, - allow_dups=allow_dups, code=code, secondary_context=secondary_context, + parent_error=parent_error, ) def note_multiline( @@ -327,7 +335,6 @@ def note_multiline( context: Context, file: str | None = None, offset: int = 0, - allow_dups: bool = False, code: ErrorCode | None = None, *, secondary_context: Context | None = None, @@ -340,7 +347,6 @@ def note_multiline( "note", file=file, offset=offset, - allow_dups=allow_dups, code=code, secondary_context=secondary_context, ) @@ -568,7 +574,7 @@ def unsupported_operand_types( context: Context, *, code: ErrorCode = codes.OPERATOR, - ) -> None: + ) -> ErrorInfo: """Report unsupported operand types for a binary operation. Types can be Type objects or strings. @@ -589,7 +595,7 @@ def unsupported_operand_types( msg = f"Unsupported operand types for {op} (likely involving Union)" else: msg = f"Unsupported operand types for {op} ({left_str} and {right_str})" - self.fail(msg, context, code=code) + return self.fail(msg, context, code=code) def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None: if self.are_type_names_disabled(): @@ -621,7 +627,7 @@ def incompatible_argument( object_type: Type | None, context: Context, outer_context: Context, - ) -> ErrorCode | None: + ) -> ErrorInfo: """Report an error about an incompatible argument type. The argument type is arg_type, argument number is n and the @@ -638,51 +644,45 @@ def incompatible_argument( callee_name = callable_name(callee) if callee_name is not None: name = callee_name - if callee.bound_args and callee.bound_args[0] is not None: - base = format_type(callee.bound_args[0], self.options) + if object_type is not None: + base = format_type(object_type, self.options) else: base = extract_type(name) - for method, op in op_methods_to_symbols.items(): - for variant in method, "__r" + method[2:]: - # FIX: do not rely on textual formatting - if name.startswith(f'"{variant}" of'): - if op == "in" or variant != method: - # Reversed order of base/argument. - self.unsupported_operand_types( - op, arg_type, base, context, code=codes.OPERATOR - ) - else: - self.unsupported_operand_types( - op, base, arg_type, context, code=codes.OPERATOR - ) - return codes.OPERATOR - if name.startswith('"__getitem__" of'): - self.invalid_index_type( + return self.invalid_index_type( arg_type, callee.arg_types[n - 1], base, context, code=codes.INDEX ) - return codes.INDEX - - if name.startswith('"__setitem__" of'): + elif name.startswith('"__setitem__" of'): if n == 1: - self.invalid_index_type( + return self.invalid_index_type( arg_type, callee.arg_types[n - 1], base, context, code=codes.INDEX ) - return codes.INDEX else: arg_type_str, callee_type_str = format_type_distinctly( 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) ) - self.fail(error_msg.value, context, code=error_msg.code) - return error_msg.code + return self.fail(error_msg.value, context, code=error_msg.code) + elif name.startswith('"__'): + for method, op in op_methods_to_symbols.items(): + for variant in method, "__r" + method[2:]: + # FIX: do not rely on textual formatting + if name.startswith(f'"{variant}" of'): + if op == "in" or variant != method: + # Reversed order of base/argument. + return self.unsupported_operand_types( + op, arg_type, base, context, code=codes.OPERATOR + ) + else: + return self.unsupported_operand_types( + op, base, arg_type, context, code=codes.OPERATOR + ) target = f"to {name} " @@ -836,18 +836,18 @@ def incompatible_argument( code = codes.TYPEDDICT_ITEM else: code = codes.ARG_TYPE - self.fail(msg, context, code=code) + error = self.fail(msg, context, code=code) if notes: for note_msg in notes: self.note(note_msg, context, code=code) - return code + return error def incompatible_argument_note( self, original_caller_type: ProperType, callee_type: ProperType, context: Context, - code: ErrorCode | None, + parent_error: ErrorInfo, ) -> None: if self.prefer_simple_messages(): return @@ -856,23 +856,28 @@ def incompatible_argument_note( ): if isinstance(callee_type, Instance) and callee_type.type.is_protocol: self.report_protocol_problems( - original_caller_type, callee_type, context, code=code + original_caller_type, callee_type, context, parent_error=parent_error ) if isinstance(callee_type, UnionType): for item in callee_type.items: item = get_proper_type(item) if isinstance(item, Instance) and item.type.is_protocol: self.report_protocol_problems( - original_caller_type, item, context, code=code + original_caller_type, item, context, parent_error=parent_error ) if isinstance(callee_type, CallableType) and isinstance(original_caller_type, Instance): call = find_member( "__call__", original_caller_type, original_caller_type, is_operator=True ) if call: - self.note_call(original_caller_type, call, context, code=code) - - self.maybe_note_concatenate_pos_args(original_caller_type, callee_type, context, code) + self.note_call(original_caller_type, call, context, code=parent_error.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=parent_error.code) + self.maybe_note_concatenate_pos_args( + original_caller_type, callee_type, context, parent_error.code + ) def maybe_note_concatenate_pos_args( self, @@ -914,11 +919,11 @@ def invalid_index_type( context: Context, *, code: ErrorCode, - ) -> None: + ) -> ErrorInfo: index_str, expected_str = format_type_distinctly( index_type, expected_type, options=self.options ) - self.fail( + return self.fail( "Invalid index type {} for {}; expected type {}".format( index_str, base_str, expected_str ), @@ -952,7 +957,7 @@ def too_few_arguments( msg = "Missing positional arguments" callee_name = callable_name(callee) if callee_name is not None and diff and all(d is not None for d in diff): - args = '", "'.join(cast(List[str], diff)) + args = '", "'.join(cast(list[str], diff)) msg += f' "{args}" in call to {callee_name}' else: msg = "Too few arguments" + for_function(callee) @@ -998,7 +1003,7 @@ def maybe_note_about_special_args(self, callee: CallableType, context: Context) if self.prefer_simple_messages(): return # https://github.com/python/mypy/issues/11309 - first_arg = callee.def_extras.get("first_arg") + first_arg = get_first_arg(callee) if first_arg and first_arg not in {"self", "cls", "mcs"}: self.note( "Looks like the first special argument in a method " @@ -1161,6 +1166,20 @@ def overload_signature_incompatible_with_supertype( note_template = 'Overload variants must be defined in the same order as they are in "{}"' self.note(note_template.format(supertype), context, code=codes.OVERRIDE) + def incompatible_setter_override( + self, defn: Context, typ: Type, original_type: Type, base: TypeInfo + ) -> None: + self.fail("Incompatible override of a setter type", defn, code=codes.OVERRIDE) + base_str, override_str = format_type_distinctly(original_type, typ, options=self.options) + self.note( + f' (base class "{base.name}" defined the type as {base_str},', + defn, + code=codes.OVERRIDE, + ) + self.note(f" override has type {override_str})", defn, code=codes.OVERRIDE) + if is_subtype(typ, original_type): + self.note(" Setter types should behave contravariantly", defn, code=codes.OVERRIDE) + def signature_incompatible_with_supertype( self, name: str, @@ -1171,16 +1190,16 @@ def signature_incompatible_with_supertype( original: ProperType, override: ProperType, ) -> None: - code = codes.OVERRIDE target = self.override_target(name, name_in_super, supertype) - self.fail(f'Signature of "{name}" incompatible with {target}', context, code=code) + error = self.fail( + f'Signature of "{name}" incompatible with {target}', context, code=codes.OVERRIDE + ) original_str, override_str = format_type_distinctly( original, override, options=self.options, bare=True ) INCLUDE_DECORATOR = True # Include @classmethod and @staticmethod decorators, if any - ALLOW_DUPS = True # Allow duplicate notes, needed when signatures are duplicates ALIGN_OFFSET = 1 # One space, to account for the difference between error and note OFFSET = 4 # Four spaces, so that notes will look like this: # error: Signature of "f" incompatible with supertype "A" @@ -1188,69 +1207,49 @@ def signature_incompatible_with_supertype( # note: def f(self) -> str # note: Subclass: # note: def f(self, x: str) -> None - self.note( - "Superclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code - ) + self.note("Superclass:", context, offset=ALIGN_OFFSET + OFFSET, parent_error=error) if isinstance(original, (CallableType, Overloaded)): self.pretty_callable_or_overload( original, context, offset=ALIGN_OFFSET + 2 * OFFSET, add_class_or_static_decorator=INCLUDE_DECORATOR, - allow_dups=ALLOW_DUPS, - code=code, + parent_error=error, ) else: - self.note( - original_str, - context, - offset=ALIGN_OFFSET + 2 * OFFSET, - allow_dups=ALLOW_DUPS, - code=code, - ) + self.note(original_str, context, offset=ALIGN_OFFSET + 2 * OFFSET, parent_error=error) - self.note( - "Subclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code - ) + self.note("Subclass:", context, offset=ALIGN_OFFSET + OFFSET, parent_error=error) if isinstance(override, (CallableType, Overloaded)): self.pretty_callable_or_overload( override, context, offset=ALIGN_OFFSET + 2 * OFFSET, add_class_or_static_decorator=INCLUDE_DECORATOR, - allow_dups=ALLOW_DUPS, - code=code, + parent_error=error, ) else: - self.note( - override_str, - context, - offset=ALIGN_OFFSET + 2 * OFFSET, - allow_dups=ALLOW_DUPS, - code=code, - ) + self.note(override_str, context, offset=ALIGN_OFFSET + 2 * OFFSET, parent_error=error) def pretty_callable_or_overload( self, tp: CallableType | Overloaded, context: Context, *, + parent_error: ErrorInfo, offset: int = 0, add_class_or_static_decorator: bool = False, - allow_dups: bool = False, - code: ErrorCode | None = None, ) -> None: if isinstance(tp, CallableType): if add_class_or_static_decorator: decorator = pretty_class_or_static_decorator(tp) if decorator is not None: - self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code) + self.note(decorator, context, offset=offset, parent_error=parent_error) self.note( pretty_callable(tp, self.options), context, offset=offset, - allow_dups=allow_dups, - code=code, + parent_error=parent_error, ) elif isinstance(tp, Overloaded): self.pretty_overload( @@ -1258,8 +1257,7 @@ def pretty_callable_or_overload( context, offset, add_class_or_static_decorator=add_class_or_static_decorator, - allow_dups=allow_dups, - code=code, + parent_error=parent_error, ) def argument_incompatible_with_supertype( @@ -1380,11 +1378,14 @@ def incompatible_type_application( self.fail(f"Type application has too few types ({s})", context) def could_not_infer_type_arguments( - self, callee_type: CallableType, n: int, context: Context + self, callee_type: CallableType, tv: TypeVarLikeType, context: Context ) -> None: callee_name = callable_name(callee_type) - if callee_name is not None and n > 0: - self.fail(f"Cannot infer type argument {n} of {callee_name}", context) + if callee_name is not None: + self.fail( + f"Cannot infer value of type parameter {format_type(tv, self.options)} of {callee_name}", + context, + ) if callee_name == "": # Invariance in key type causes more of these errors than we would want. self.note( @@ -1395,7 +1396,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) @@ -1433,8 +1434,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, ) @@ -1512,14 +1512,14 @@ def incompatible_self_argument( def incompatible_conditional_function_def( self, defn: FuncDef, old_type: FunctionLike, new_type: FunctionLike ) -> None: - self.fail("All conditional function variants must have identical signatures", defn) + error = self.fail("All conditional function variants must have identical signatures", defn) if isinstance(old_type, (CallableType, Overloaded)) and isinstance( new_type, (CallableType, Overloaded) ): self.note("Original:", defn) - self.pretty_callable_or_overload(old_type, defn, offset=4) + self.pretty_callable_or_overload(old_type, defn, offset=4, parent_error=error) self.note("Redefinition:", defn) - self.pretty_callable_or_overload(new_type, defn, offset=4) + self.pretty_callable_or_overload(new_type, defn, offset=4, parent_error=error) def cannot_instantiate_abstract_class( self, class_name: str, abstract_attributes: dict[str, bool], context: Context @@ -1588,8 +1588,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, ) @@ -1676,15 +1678,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, ) @@ -1707,8 +1710,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, @@ -1744,6 +1746,24 @@ def invalid_signature_for_special_method( ) def reveal_type(self, typ: Type, context: Context) -> None: + + # Search for an error watcher that modifies the "normal" behaviour (we do not + # rely on the normal `ErrorWatcher` filtering approach because we might need to + # collect the original types for a later unionised response): + for watcher in self.errors.get_watchers(): + # The `reveal_type` statement should be ignored: + if watcher.filter_revealed_type: + return + # The `reveal_type` statement might be visited iteratively due to being + # placed in a loop or so. Hence, we collect the respective types of + # individual iterations so that we can report them all in one step later: + if isinstance(watcher, IterationErrorWatcher): + watcher.iteration_dependent_errors.revealed_types[ + (context.line, context.column, context.end_line, context.end_column) + ].append(typ) + return + + # Nothing special here; just create the note: visitor = TypeStrVisitor(options=self.options) self.note(f'Revealed type is "{typ.accept(visitor)}"', context) @@ -1761,7 +1781,7 @@ def reveal_locals(self, type_map: dict[str, Type | None], context: Context) -> N def unsupported_type_type(self, item: Type, context: Context) -> None: self.fail( - f'Cannot instantiate type "Type[{format_type_bare(item, self.options)}]"', context + f'Cannot instantiate type "type[{format_type_bare(item, self.options)}]"', context ) def redundant_cast(self, typ: Type, context: Context) -> None: @@ -1783,30 +1803,26 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N ) def need_annotation_for_var( - self, node: SymbolNode, context: Context, python_version: tuple[int, int] | None = None + self, node: SymbolNode, context: Context, options: Options | None = None ) -> None: hint = "" - pep604_supported = not python_version or python_version >= (3, 10) # type to recommend the user adds recommended_type = None # Only gives hint if it's a variable declaration and the partial type is a builtin type - if python_version and isinstance(node, Var) and isinstance(node.type, PartialType): + if options and isinstance(node, Var) and isinstance(node.type, PartialType): type_dec = "" if not node.type.type: # partial None - if pep604_supported: + if options.use_or_syntax(): recommended_type = f"{type_dec} | None" else: recommended_type = f"Optional[{type_dec}]" elif node.type.type.fullname in reverse_builtin_aliases: # partial types other than partial None - alias = reverse_builtin_aliases[node.type.type.fullname] - alias = alias.split(".")[-1] - if alias == "Dict": + name = node.type.type.fullname.partition(".")[2] + if name == "dict": type_dec = f"{type_dec}, {type_dec}" - if self.options.use_lowercase_names(): - alias = alias.lower() - recommended_type = f"{alias}[{type_dec}]" + recommended_type = f"{name}[{type_dec}]" if recommended_type is not None: hint = f' (hint: "{node.name}: {recommended_type} = ...")' @@ -1817,7 +1833,7 @@ def need_annotation_for_var( ) def explicit_any(self, ctx: Context) -> None: - self.fail('Explicit "Any" is not allowed', ctx) + self.fail('Explicit "Any" is not allowed', ctx, code=codes.EXPLICIT_ANY) def unsupported_target_for_star_typeddict(self, typ: Type, ctx: Context) -> None: self.fail( @@ -1969,7 +1985,7 @@ def incorrect__exit__return(self, context: Context) -> None: code=codes.EXIT_RETURN, ) self.note( - 'Use "typing_extensions.Literal[False]" as the return type or change it to "None"', + 'Use "typing.Literal[False]" as the return type or change it to "None"', context, code=codes.EXIT_RETURN, ) @@ -1991,14 +2007,17 @@ def untyped_decorated_function(self, typ: Type, context: Context) -> None: ) def typed_function_untyped_decorator(self, func_name: str, context: Context) -> None: - self.fail(f'Untyped decorator makes function "{func_name}" untyped', context) + self.fail( + f'Untyped decorator makes function "{func_name}" untyped', + context, + code=codes.UNTYPED_DECORATOR, + ) 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) ) ) @@ -2101,7 +2120,7 @@ def report_protocol_problems( supertype: Instance, context: Context, *, - code: ErrorCode | None, + parent_error: ErrorInfo, ) -> None: """Report possible protocol conflicts between 'subtype' and 'supertype'. @@ -2129,12 +2148,8 @@ def report_protocol_problems( is_module = False skip = [] if isinstance(subtype, TupleType): - if not isinstance(subtype.partial_fallback, Instance): - return subtype = subtype.partial_fallback elif isinstance(subtype, TypedDictType): - if not isinstance(subtype.fallback, Instance): - return subtype = subtype.fallback elif isinstance(subtype, TypeType): if not isinstance(subtype.item, Instance): @@ -2169,7 +2184,7 @@ def report_protocol_problems( subtype.type.name, supertype.type.name ), context, - code=code, + parent_error=parent_error, ) else: self.note( @@ -2177,9 +2192,9 @@ def report_protocol_problems( subtype.type.name, supertype.type.name, plural_s(missing) ), context, - code=code, + parent_error=parent_error, ) - self.note(", ".join(missing), context, offset=OFFSET, code=code) + self.note(", ".join(missing), context, offset=OFFSET, parent_error=parent_error) elif len(missing) > MAX_ITEMS or len(missing) == len(supertype.type.protocol_members): # This is an obviously wrong type: too many missing members return @@ -2197,73 +2212,107 @@ def report_protocol_problems( or supertype.type.has_param_spec_type ): type_name = format_type(subtype, self.options, module_names=True) - self.note(f"Following member(s) of {type_name} have conflicts:", context, code=code) - for name, got, exp in conflict_types[:MAX_ITEMS]: + self.note( + f"Following member(s) of {type_name} have conflicts:", + context, + parent_error=parent_error, + ) + for name, got, exp, is_lvalue in conflict_types[:MAX_ITEMS]: exp = get_proper_type(exp) got = get_proper_type(got) - if not isinstance(exp, (CallableType, Overloaded)) or not isinstance( - got, (CallableType, Overloaded) + setter_suffix = " setter type" if is_lvalue else "" + if ( + not isinstance(exp, (CallableType, Overloaded)) + or not isinstance(got, (CallableType, Overloaded)) + # If expected type is a type object, it means it is a nested class. + # Showing constructor signature in errors would be confusing in this case, + # since we don't check the signature, only subclassing of type objects. + or exp.is_type_obj() ): self.note( - "{}: expected {}, got {}".format( - name, *format_type_distinctly(exp, got, options=self.options) + "{}: expected{} {}, got {}".format( + name, + setter_suffix, + *format_type_distinctly(exp, got, options=self.options), ), context, offset=OFFSET, - code=code, + parent_error=parent_error, ) + if is_lvalue and is_subtype(got, exp, options=self.options): + self.note( + "Setter types should behave contravariantly", + context, + offset=OFFSET, + parent_error=parent_error, + ) else: - self.note("Expected:", context, offset=OFFSET, code=code) + self.note( + "Expected{}:".format(setter_suffix), + context, + offset=OFFSET, + parent_error=parent_error, + ) if isinstance(exp, CallableType): self.note( pretty_callable(exp, self.options, skip_self=class_obj or is_module), context, offset=2 * OFFSET, - code=code, + parent_error=parent_error, ) else: assert isinstance(exp, Overloaded) self.pretty_overload( - exp, context, 2 * OFFSET, code=code, skip_self=class_obj or is_module + exp, + context, + 2 * OFFSET, + parent_error=parent_error, + skip_self=class_obj or is_module, ) - self.note("Got:", context, offset=OFFSET, code=code) + self.note("Got:", context, offset=OFFSET, parent_error=parent_error) if isinstance(got, CallableType): self.note( pretty_callable(got, self.options, skip_self=class_obj or is_module), context, offset=2 * OFFSET, - code=code, + parent_error=parent_error, ) else: assert isinstance(got, Overloaded) self.pretty_overload( - got, context, 2 * OFFSET, code=code, skip_self=class_obj or is_module + got, + context, + 2 * OFFSET, + parent_error=parent_error, + skip_self=class_obj or is_module, ) - self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=code) + self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=parent_error.code) # Report flag conflicts (i.e. settable vs read-only etc.) conflict_flags = get_bad_protocol_flags(subtype, supertype, class_obj=class_obj) 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, + parent_error=parent_error, ) 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, + parent_error=parent_error, ) if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: self.note( "Protocol member {}.{} expected settable variable," " got read-only attribute".format(supertype.type.name, name), context, - code=code, + parent_error=parent_error, ) if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags: self.note( @@ -2271,7 +2320,7 @@ def report_protocol_problems( supertype.type.name, name ), context, - code=code, + parent_error=parent_error, ) if ( class_obj @@ -2282,7 +2331,7 @@ def report_protocol_problems( "Only class variables allowed for class object access on protocols," ' {} is an instance variable of "{}"'.format(name, subtype.type.name), context, - code=code, + parent_error=parent_error, ) if class_obj and IS_CLASSVAR in superflags: self.note( @@ -2290,9 +2339,9 @@ def report_protocol_problems( supertype.type.name, name ), context, - code=code, + parent_error=parent_error, ) - self.print_more(conflict_flags, context, OFFSET, MAX_ITEMS, code=code) + self.print_more(conflict_flags, context, OFFSET, MAX_ITEMS, code=parent_error.code) def pretty_overload( self, @@ -2300,25 +2349,23 @@ def pretty_overload( context: Context, offset: int, *, + parent_error: ErrorInfo, add_class_or_static_decorator: bool = False, - allow_dups: bool = False, - code: ErrorCode | None = None, skip_self: bool = False, ) -> None: for item in tp.items: - self.note("@overload", context, offset=offset, allow_dups=allow_dups, code=code) + self.note("@overload", context, offset=offset, parent_error=parent_error) if add_class_or_static_decorator: decorator = pretty_class_or_static_decorator(item) if decorator is not None: - self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code) + self.note(decorator, context, offset=offset, parent_error=parent_error) self.note( pretty_callable(item, self.options, skip_self=skip_self), context, offset=offset, - allow_dups=allow_dups, - code=code, + parent_error=parent_error, ) def print_more( @@ -2387,14 +2434,13 @@ def format_long_tuple_type(self, typ: TupleType) -> str: """Format very long tuple type using an ellipsis notation""" item_cnt = len(typ.items) if item_cnt > MAX_TUPLE_ITEMS: - return "{}[{}, {}, ... <{} more items>]".format( - "tuple" if self.options.use_lowercase_names() else "Tuple", + return '"tuple[{}, {}, ... <{} more items>]"'.format( format_type_bare(typ.items[0], self.options), format_type_bare(typ.items[1], self.options), str(item_cnt - 2), ) else: - return format_type_bare(typ, self.options) + return format_type(typ, self.options) def generate_incompatible_tuple_error( self, @@ -2407,7 +2453,7 @@ def generate_incompatible_tuple_error( error_cnt = 0 notes: list[str] = [] for i, (lhs_t, rhs_t) in enumerate(zip(lhs_types, rhs_types)): - if not is_subtype(lhs_t, rhs_t): + if not is_subtype(rhs_t, lhs_t): if error_cnt < 3: notes.append( "Expression tuple item {} has type {}; {} expected; ".format( @@ -2454,22 +2500,44 @@ def type_parameters_should_be_declared(self, undeclared: list[str], context: Con code=codes.VALID_TYPE, ) + def match_statement_inexhaustive_match(self, typ: Type, context: Context) -> None: + type_str = format_type(typ, self.options) + msg = f"Match statement has unhandled case for values of type {type_str}" + self.fail(msg, context, code=codes.EXHAUSTIVE_MATCH) + self.note( + "If match statement is intended to be non-exhaustive, add `case _: pass`", + context, + code=codes.EXHAUSTIVE_MATCH, + ) + + def iteration_dependent_errors(self, iter_errors: IterationDependentErrors) -> None: + for error_info in iter_errors.yield_uselessness_error_infos(): + self.fail(*error_info[:2], code=error_info[2]) + for types, context in iter_errors.yield_revealed_type_infos(): + self.reveal_type(mypy.typeops.make_simplified_union(types), context) + def quote_type_string(type_string: str) -> str: """Quotes a type representation for use in messages.""" - no_quote_regex = r"^<(tuple|union): \d+ items>$" if ( type_string in ["Module", "overloaded function", ""] or type_string.startswith("Module ") - or re.match(no_quote_regex, type_string) is not None or type_string.endswith("?") ): - # Messages are easier to read if these aren't quoted. We use a - # regex to match strings with variable contents. + # These messages are easier to read if these aren't quoted. return type_string return f'"{type_string}"' +def should_format_arg_as_type(arg_kind: ArgKind, arg_name: str | None, verbosity: int) -> bool: + """ + Determine whether a function argument should be formatted as its Type or with name. + """ + return (arg_kind == ARG_POS and arg_name is None) or ( + verbosity == 0 and arg_kind.is_positional() + ) + + def format_callable_args( arg_types: list[Type], arg_kinds: list[ArgKind], @@ -2480,7 +2548,7 @@ def format_callable_args( """Format a bunch of Callable arguments into a string""" arg_strings = [] for arg_name, arg_type, arg_kind in zip(arg_names, arg_types, arg_kinds): - if arg_kind == ARG_POS and arg_name is None or verbosity == 0 and arg_kind.is_positional(): + if should_format_arg_as_type(arg_kind, arg_name, verbosity): arg_strings.append(format(arg_type)) else: constructor = ARG_CONSTRUCTOR_NAMES[arg_kind] @@ -2498,13 +2566,18 @@ def format_type_inner( options: Options, fullnames: set[str] | None, module_names: bool = False, + use_pretty_callable: bool = True, ) -> str: """ Convert a type to a relatively short string suitable for error messages. Args: + typ: type to be formatted verbosity: a coarse grained control on the verbosity of the type + options: Options object controlling formatting fullnames: a set of names that should be printed in full + module_names: whether to show module names for module types + use_pretty_callable: use pretty_callable to format Callable types. """ def format(typ: Type) -> str: @@ -2563,10 +2636,7 @@ def format_literal_value(typ: LiteralType) -> str: if itype.type.fullname == "typing._SpecialForm": # This is not a real type but used for some typing-related constructs. return "" - if itype.type.fullname in reverse_builtin_aliases and not options.use_lowercase_names(): - alias = reverse_builtin_aliases[itype.type.fullname] - base_str = alias.split(".")[-1] - elif verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): + if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): base_str = itype.type.fullname else: base_str = itype.type.name @@ -2577,7 +2647,7 @@ def format_literal_value(typ: LiteralType) -> str: return base_str elif itype.type.fullname == "builtins.tuple": item_type_str = format(itype.args[0]) - return f"{'tuple' if options.use_lowercase_names() else 'Tuple'}[{item_type_str}, ...]" + return f"tuple[{item_type_str}, ...]" else: # There are type arguments. Convert the arguments to strings. return f"{base_str}[{format_list(itype.args)}]" @@ -2613,11 +2683,7 @@ def format_literal_value(typ: LiteralType) -> str: if typ.partial_fallback.type.fullname != "builtins.tuple": return format(typ.partial_fallback) type_items = format_list(typ.items) or "()" - if options.use_lowercase_names(): - s = f"tuple[{type_items}]" - else: - s = f"Tuple[{type_items}]" - return s + return f"tuple[{type_items}]" elif isinstance(typ, TypedDictType): # If the TypedDictType is named, return the name if not typ.is_anonymous(): @@ -2689,14 +2755,17 @@ def format_literal_value(typ: LiteralType) -> str: elif isinstance(typ, UninhabitedType): return "Never" elif isinstance(typ, TypeType): - type_name = "type" if options.use_lowercase_names() else "Type" + if typ.is_type_form: + type_name = "TypeForm" + else: + type_name = "type" return f"{type_name}[{format(typ.item)}]" elif isinstance(typ, FunctionLike): func = typ if func.is_type_obj(): # The type of a type object type can be derived from the # return type (this always works). - return format(TypeType.make_normalized(erase_type(func.items[0].ret_type))) + return format(TypeType.make_normalized(func.items[0].ret_type)) elif isinstance(func, CallableType): if func.type_guard is not None: return_type = f"TypeGuard[{format(func.type_guard)}]" @@ -2709,6 +2778,16 @@ def format_literal_value(typ: LiteralType) -> str: param_spec = func.param_spec() if param_spec is not None: return f"Callable[{format(param_spec)}, {return_type}]" + + # Use pretty format (def-style) for complex signatures with named, optional, or star args. + # Use compact Callable[[...], ...] only for signatures with all simple positional args. + if use_pretty_callable: + if any( + not should_format_arg_as_type(kind, name, verbosity) + for kind, name in zip(func.arg_kinds, func.arg_names) + ): + return pretty_callable(func, options) + args = format_callable_args( func.arg_types, func.arg_kinds, func.arg_names, format, verbosity ) @@ -2850,7 +2929,29 @@ def format_type_distinctly(*types: Type, options: Options, bare: bool = False) - quoting them (such as prepending * or **) should use this. """ overlapping = find_type_overlaps(*types) - for verbosity in range(2): + + def format_single(arg: Type) -> str: + return format_type_inner(arg, verbosity=0, options=options, fullnames=overlapping) + + min_verbosity = 0 + # Prevent emitting weird errors like: + # ... has incompatible type "Callable[[int], Child]"; expected "Callable[[int], Parent]" + if len(types) == 2: + left, right = types + left = get_proper_type(left) + right = get_proper_type(right) + # If the right type has named arguments, they may be the reason for incompatibility. + # This excludes cases when right is Callable[[Something], None] without named args, + # because that's usually the right thing to do. + if ( + isinstance(left, CallableType) + and isinstance(right, CallableType) + and any(right.arg_names) + and is_subtype(left, right, ignore_pos_arg_names=True) + ): + min_verbosity = 1 + + for verbosity in range(min_verbosity, 2): strs = [ format_type_inner(type, verbosity=verbosity, options=options, fullnames=overlapping) for type in types @@ -2865,10 +2966,11 @@ def format_type_distinctly(*types: Type, options: Options, bare: bool = False) - def pretty_class_or_static_decorator(tp: CallableType) -> str | None: """Return @classmethod or @staticmethod, if any, for the given callable type.""" - if tp.definition is not None and isinstance(tp.definition, SYMBOL_FUNCBASE_TYPES): - if tp.definition.is_class: + definition = get_func_def(tp) + if definition is not None and isinstance(definition, SYMBOL_FUNCBASE_TYPES): + if definition.is_class: return "@classmethod" - if tp.definition.is_static: + if definition.is_static: return "@staticmethod" return None @@ -2918,12 +3020,13 @@ def [T <: int] f(self, x: int, y: T) -> None slash = True # If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list + definition = get_func_def(tp) if ( - isinstance(tp.definition, FuncDef) - and hasattr(tp.definition, "arguments") + isinstance(definition, FuncDef) + and hasattr(definition, "arguments") and not tp.from_concatenate ): - definition_arg_names = [arg.variable.name for arg in tp.definition.arguments] + definition_arg_names = [arg.variable.name for arg in definition.arguments] if ( len(definition_arg_names) > len(tp.arg_names) and definition_arg_names[0] @@ -2932,9 +3035,9 @@ def [T <: int] f(self, x: int, y: T) -> None if s: s = ", " + s s = definition_arg_names[0] + s - s = f"{tp.definition.name}({s})" + s = f"{definition.name}({s})" elif tp.name: - first_arg = tp.def_extras.get("first_arg") + first_arg = get_first_arg(tp) if first_arg: if s: s = ", " + s @@ -2977,6 +3080,13 @@ def [T <: int] f(self, x: int, y: T) -> None return f"def {s}" +def get_first_arg(tp: CallableType) -> str | None: + definition = get_func_def(tp) + if not isinstance(definition, FuncDef) or not definition.info or definition.is_static: + return None + return definition.original_first_arg + + def variance_string(variance: int) -> str: if variance == COVARIANT: return "covariant" @@ -3002,12 +3112,12 @@ def get_missing_protocol_members(left: Instance, right: Instance, skip: list[str def get_conflict_protocol_types( left: Instance, right: Instance, class_obj: bool = False, options: Options | None = None -) -> list[tuple[str, Type, Type]]: +) -> list[tuple[str, Type, Type, bool]]: """Find members that are defined in 'left' but have incompatible types. - Return them as a list of ('member', 'got', 'expected'). + Return them as a list of ('member', 'got', 'expected', 'is_lvalue'). """ assert right.type.is_protocol - conflicts: list[tuple[str, Type, Type]] = [] + conflicts: list[tuple[str, Type, Type, bool]] = [] for member in right.type.protocol_members: if member in ("__init__", "__new__"): continue @@ -3017,10 +3127,29 @@ def get_conflict_protocol_types( if not subtype: continue is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True, options=options) - if IS_SETTABLE in get_member_flags(member, right): - is_compat = is_compat and is_subtype(supertype, subtype, options=options) if not is_compat: - conflicts.append((member, subtype, supertype)) + conflicts.append((member, subtype, supertype, False)) + superflags = get_member_flags(member, right) + if IS_SETTABLE not in superflags: + continue + different_setter = False + if IS_EXPLICIT_SETTER in superflags: + set_supertype = find_member(member, right, left, is_lvalue=True) + if set_supertype and not is_same_type(set_supertype, supertype): + different_setter = True + supertype = set_supertype + if IS_EXPLICIT_SETTER in get_member_flags(member, left): + set_subtype = mypy.typeops.get_protocol_member(left, member, class_obj, is_lvalue=True) + if set_subtype and not is_same_type(set_subtype, subtype): + different_setter = True + subtype = set_subtype + if not is_compat and not different_setter: + # We already have this conflict listed, avoid duplicates. + continue + assert supertype is not None and subtype is not None + is_compat = is_subtype(supertype, subtype, options=options) + if not is_compat: + conflicts.append((member, subtype, supertype, different_setter)) return conflicts @@ -3033,9 +3162,14 @@ def get_bad_protocol_flags( assert right.type.is_protocol all_flags: list[tuple[str, set[int], set[int]]] = [] for member in right.type.protocol_members: - if find_member(member, left, left): - item = (member, get_member_flags(member, left), get_member_flags(member, right)) - all_flags.append(item) + if find_member(member, left, left, class_obj=class_obj): + all_flags.append( + ( + member, + get_member_flags(member, left, class_obj=class_obj), + get_member_flags(member, right), + ) + ) bad_flags = [] for name, subflags, superflags in all_flags: if ( @@ -3193,7 +3327,7 @@ def append_invariance_notes( and expected_type.type.fullname == "builtins.list" and is_subtype(arg_type.args[0], expected_type.args[0]) ): - invariant_type = "List" + invariant_type = "list" covariant_suggestion = 'Consider using "Sequence" instead, which is covariant' elif ( arg_type.type.fullname == "builtins.dict" @@ -3201,7 +3335,7 @@ def append_invariance_notes( and is_same_type(arg_type.args[0], expected_type.args[0]) and is_subtype(arg_type.args[1], expected_type.args[1]) ): - invariant_type = "Dict" + invariant_type = "dict" covariant_suggestion = ( 'Consider using "Mapping" instead, which is covariant in the value type' ) diff --git a/mypy/metastore.py b/mypy/metastore.py index 4caa7d7f0534e..442c7dc77461f 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -14,7 +14,8 @@ import os import time from abc import abstractmethod -from typing import TYPE_CHECKING, Any, Iterable +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: # We avoid importing sqlite3 unless we are using it so we can mostly work @@ -27,20 +28,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 +87,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 +106,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 +132,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 +150,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 +170,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 +182,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 +196,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 +207,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 +215,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/mixedtraverser.py b/mypy/mixedtraverser.py index dfde41859c674..39fba49cf3c7a 100644 --- a/mypy/mixedtraverser.py +++ b/mypy/mixedtraverser.py @@ -10,9 +10,12 @@ NamedTupleExpr, NewTypeExpr, PromoteExpr, + TypeAlias, TypeAliasExpr, + TypeAliasStmt, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeVarExpr, Var, WithStmt, @@ -30,14 +33,14 @@ def __init__(self) -> None: # Symbol nodes - def visit_var(self, var: Var) -> None: + def visit_var(self, var: Var, /) -> None: self.visit_optional_type(var.type) - def visit_func(self, o: FuncItem) -> None: + def visit_func(self, o: FuncItem, /) -> None: super().visit_func(o) self.visit_optional_type(o.type) - def visit_class_def(self, o: ClassDef) -> None: + def visit_class_def(self, o: ClassDef, /) -> None: # TODO: Should we visit generated methods/variables as well, either here or in # TraverserVisitor? super().visit_class_def(o) @@ -45,68 +48,83 @@ def visit_class_def(self, o: ClassDef) -> None: if info: for base in info.bases: base.accept(self) + if info.special_alias: + info.special_alias.accept(self) - def visit_type_alias_expr(self, o: TypeAliasExpr) -> None: + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: super().visit_type_alias_expr(o) - self.in_type_alias_expr = True - o.node.target.accept(self) - self.in_type_alias_expr = False + o.node.accept(self) - def visit_type_var_expr(self, o: TypeVarExpr) -> None: + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: super().visit_type_var_expr(o) o.upper_bound.accept(self) for value in o.values: value.accept(self) - def visit_typeddict_expr(self, o: TypedDictExpr) -> None: + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: super().visit_typeddict_expr(o) self.visit_optional_type(o.info.typeddict_type) - def visit_namedtuple_expr(self, o: NamedTupleExpr) -> None: + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: super().visit_namedtuple_expr(o) assert o.info.tuple_type o.info.tuple_type.accept(self) - def visit__promote_expr(self, o: PromoteExpr) -> None: + def visit__promote_expr(self, o: PromoteExpr, /) -> None: super().visit__promote_expr(o) o.type.accept(self) - def visit_newtype_expr(self, o: NewTypeExpr) -> None: + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: super().visit_newtype_expr(o) self.visit_optional_type(o.old_type) # Statements - def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: super().visit_assignment_stmt(o) self.visit_optional_type(o.type) - def visit_for_stmt(self, o: ForStmt) -> None: + def visit_type_alias_stmt(self, o: TypeAliasStmt, /) -> None: + super().visit_type_alias_stmt(o) + if o.alias_node is not None: + o.alias_node.accept(self) + + def visit_type_alias(self, o: TypeAlias, /) -> None: + super().visit_type_alias(o) + self.in_type_alias_expr = True + o.target.accept(self) + self.in_type_alias_expr = False + + def visit_for_stmt(self, o: ForStmt, /) -> None: super().visit_for_stmt(o) self.visit_optional_type(o.index_type) - def visit_with_stmt(self, o: WithStmt) -> None: + def visit_with_stmt(self, o: WithStmt, /) -> None: super().visit_with_stmt(o) for typ in o.analyzed_types: typ.accept(self) # Expressions - def visit_cast_expr(self, o: CastExpr) -> None: + def visit_cast_expr(self, o: CastExpr, /) -> None: super().visit_cast_expr(o) o.type.accept(self) - def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + def visit_type_form_expr(self, o: TypeFormExpr, /) -> None: + super().visit_type_form_expr(o) + o.type.accept(self) + + def visit_assert_type_expr(self, o: AssertTypeExpr, /) -> None: super().visit_assert_type_expr(o) o.type.accept(self) - def visit_type_application(self, o: TypeApplication) -> None: + def visit_type_application(self, o: TypeApplication, /) -> None: super().visit_type_application(o) for t in o.types: t.accept(self) # Helpers - def visit_optional_type(self, t: Type | None) -> None: + def visit_optional_type(self, t: Type | None, /) -> None: if t: t.accept(self) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 452cfef20f4c7..5176b7e1df523 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -13,31 +13,54 @@ import subprocess import sys from enum import Enum, unique -from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union +from typing import Final, Optional, Union from typing_extensions import TypeAlias as _TypeAlias +from pathspec import PathSpec +from pathspec.patterns.gitwildmatch import GitWildMatchPatternError + from mypy import pyinfo from mypy.errors import CompileError 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 -OnePackageDir = Tuple[str, bool] -PackageDirs = List[OnePackageDir] +OnePackageDir = tuple[str, bool] +PackageDirs = list[OnePackageDir] # Minimum and maximum Python versions for modules in stdlib as (major, minor) -StdlibVersions: _TypeAlias = Dict[str, Tuple[Tuple[int, int], Optional[Tuple[int, int]]]] +StdlibVersions: _TypeAlias = dict[str, tuple[tuple[int, int], Optional[tuple[int, int]]]] PYTHON_EXTENSIONS: Final = [".pyi", ".py"] @@ -72,8 +95,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 +227,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 +261,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 @@ -298,13 +320,21 @@ def find_module(self, id: str, *, fast_path: bool = False) -> ModuleSearchResult use_typeshed = self._typeshed_has_version(id) elif top_level in self.stdlib_py_versions: use_typeshed = self._typeshed_has_version(top_level) - self.results[id] = self._find_module(id, use_typeshed) - if ( - not (fast_path or (self.options is not None and self.options.fast_module_lookup)) - and self.results[id] is ModuleNotFoundReason.NOT_FOUND - and self._can_find_module_in_parent_dir(id) - ): - self.results[id] = ModuleNotFoundReason.WRONG_WORKING_DIRECTORY + result, should_cache = self._find_module(id, use_typeshed) + if should_cache: + if ( + not ( + fast_path or (self.options is not None and self.options.fast_module_lookup) + ) + and result is ModuleNotFoundReason.NOT_FOUND + and self._can_find_module_in_parent_dir(id) + ): + self.results[id] = ModuleNotFoundReason.WRONG_WORKING_DIRECTORY + else: + self.results[id] = result + return self.results[id] + else: + return result return self.results[id] def _typeshed_has_version(self, module: str) -> bool: @@ -315,13 +345,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 +361,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 @@ -360,11 +392,16 @@ def _can_find_module_in_parent_dir(self, id: str) -> bool: while any(is_init_file(file) for file in os.listdir(working_dir)): working_dir = os.path.dirname(working_dir) parent_search.search_paths = SearchPaths((working_dir,), (), (), ()) - if not isinstance(parent_search._find_module(id, False), ModuleNotFoundReason): + if not isinstance(parent_search._find_module(id, False)[0], ModuleNotFoundReason): return True return False - def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: + def _find_module(self, id: str, use_typeshed: bool) -> tuple[ModuleSearchResult, bool]: + """Try to find a module in all available sources. + + Returns: + ``(result, can_be_cached)`` pair. + """ fscache = self.fscache # Fast path for any modules in the current source set. @@ -400,7 +437,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: else None ) if p: - return p + return p, True # If we're looking for a module like 'foo.bar.baz', it's likely that most of the # many elements of lib_path don't even have a subdirectory 'foo/bar'. Discover @@ -414,13 +451,22 @@ 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) + } + # Caching FOUND_WITHOUT_TYPE_HINTS is not always safe. That causes issues with + # typed subpackages in namespace packages. + can_cache_any_result = True 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 +476,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 +487,15 @@ 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 + can_cache_any_result = False 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() @@ -477,21 +523,24 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: dir_prefix = base_dir for _ in range(len(components) - 1): dir_prefix = os.path.dirname(dir_prefix) + + # Stubs-only packages always take precedence over py.typed packages + path_stubs = f"{base_path}-stubs{sepinit}.pyi" + if fscache.isfile_case(path_stubs, dir_prefix): + if verify and not verify_module(fscache, id, path_stubs, dir_prefix): + near_misses.append((path_stubs, dir_prefix)) + else: + return path_stubs, True + # Prefer package over module, i.e. baz/__init__.py* over baz.py*. for extension in PYTHON_EXTENSIONS: path = base_path + sepinit + extension - path_stubs = base_path + "-stubs" + sepinit + extension if fscache.isfile_case(path, dir_prefix): has_init = True if verify and not verify_module(fscache, id, path, dir_prefix): near_misses.append((path, dir_prefix)) continue - return path - elif fscache.isfile_case(path_stubs, dir_prefix): - if verify and not verify_module(fscache, id, path_stubs, dir_prefix): - near_misses.append((path_stubs, dir_prefix)) - continue - return path_stubs + return path, True # In namespace mode, register a potential namespace package if self.options and self.options.namespace_packages: @@ -509,7 +558,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: if verify and not verify_module(fscache, id, path, dir_prefix): near_misses.append((path, dir_prefix)) continue - return path + return path, True # In namespace mode, re-check those entries that had 'verify'. # Assume search path entries xxx, yyy and zzz, and we're @@ -538,7 +587,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: for path, dir_prefix in near_misses ] index = levels.index(max(levels)) - return near_misses[index][0] + return near_misses[index][0], True # Finally, we may be asked to produce an ancestor for an # installed package with a py.typed marker that is a @@ -546,17 +595,30 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: # if we would otherwise return "not found". ancestor = self.ns_ancestors.get(id) if ancestor is not None: - return ancestor + return ancestor, True + + approved_dist_name = stub_distribution_name(id) + if approved_dist_name: + if len(components) == 1: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED, True + # 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, True + if self.find_module(parent_id) is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED, True + return ModuleNotFoundReason.NOT_FOUND, True - 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: - return ModuleNotFoundReason.NOT_FOUND + if found_possible_third_party_missing_type_hints: + return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS, can_cache_any_result + return ModuleNotFoundReason.NOT_FOUND, True 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,18 +642,24 @@ 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 ): continue + if ( + self.options + and self.options.exclude_gitignore + and matches_gitignore(subpath, self.fscache, self.options.verbosity >= 2) + ): + continue 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)) @@ -616,15 +684,66 @@ def matches_exclude( if fscache.isdir(subpath): subpath_str += "/" for exclude in excludes: - if re.search(exclude, subpath_str): + try: + if re.search(exclude, subpath_str): + if verbose: + print( + f"TRACE: Excluding {subpath_str} (matches pattern {exclude})", + file=sys.stderr, + ) + return True + except re.error as e: + print( + f"error: The exclude {exclude} is an invalid regular expression, because: {e}" + + ( + "\n(Hint: use / as a path separator, even if you're on Windows!)" + if "\\" in exclude + else "" + ) + + "\nFor more information on Python's flavor of regex, see:" + + " https://docs.python.org/3/library/re.html", + file=sys.stderr, + ) + sys.exit(2) + return False + + +def matches_gitignore(subpath: str, fscache: FileSystemCache, verbose: bool) -> bool: + dir, _ = os.path.split(subpath) + for gi_path, gi_spec in find_gitignores(dir): + relative_path = os.path.relpath(subpath, gi_path) + if fscache.isdir(relative_path): + relative_path = relative_path + "/" + if gi_spec.match_file(relative_path): if verbose: print( - f"TRACE: Excluding {subpath_str} (matches pattern {exclude})", file=sys.stderr + f"TRACE: Excluding {relative_path} (matches .gitignore) in {gi_path}", + file=sys.stderr, ) return True return False +@functools.lru_cache +def find_gitignores(dir: str) -> list[tuple[str, PathSpec]]: + parent_dir = os.path.dirname(dir) + if parent_dir == dir: + parent_gitignores = [] + else: + parent_gitignores = find_gitignores(parent_dir) + + gitignore = os.path.join(dir, ".gitignore") + if os.path.isfile(gitignore): + with open(gitignore) as f: + lines = f.readlines() + try: + return parent_gitignores + [(dir, PathSpec.from_lines("gitwildmatch", lines))] + except GitWildMatchPatternError: + print(f"error: could not parse {gitignore}", file=sys.stderr) + return parent_gitignores + return parent_gitignores + + def is_init_file(path: str) -> bool: return os.path.basename(path) in ("__init__.py", "__init__.pyi") @@ -636,7 +755,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 +770,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,18 +787,23 @@ 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") + librt_dir = os.path.join(custom_typeshed_dir, "stubs", "librt") versions_file = os.path.join(typeshed_dir, "VERSIONS") if not os.path.isdir(typeshed_dir) or not os.path.isfile(versions_file): print( "error: --custom-typeshed-dir does not point to a valid typeshed ({})".format( custom_typeshed_dir - ) + ), + file=sys.stderr, ) sys.exit(2) else: @@ -688,11 +812,13 @@ def default_lib_path( data_dir = auto typeshed_dir = os.path.join(data_dir, "typeshed", "stdlib") mypy_extensions_dir = os.path.join(data_dir, "typeshed", "stubs", "mypy-extensions") + librt_dir = os.path.join(data_dir, "typeshed", "stubs", "librt") path.append(typeshed_dir) - # Get mypy-extensions stubs from typeshed, since we treat it as an - # "internal" library, similar to typing and typing-extensions. + # Get mypy-extensions and librt stubs from typeshed, since we treat them as + # "internal" libraries, similar to typing and typing-extensions. path.append(mypy_extensions_dir) + path.append(librt_dir) # Add fallback path that can be used if we have a broken installation. if sys.platform != "win32": @@ -709,9 +835,9 @@ def default_lib_path( return path -@functools.lru_cache(maxsize=None) +@functools.cache 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 +866,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 +900,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 +970,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: @@ -870,6 +998,6 @@ def parse_version(version: str) -> tuple[int, int]: def typeshed_py_version(options: Options) -> tuple[int, int]: """Return Python version used for checking whether module supports typeshed.""" - # Typeshed no longer covers Python 3.x versions before 3.8, so 3.8 is + # Typeshed no longer covers Python 3.x versions before 3.9, so 3.9 is # the earliest we can support. - return max(options.python_version, (3, 8)) + return max(options.python_version, (3, 9)) diff --git a/mypy/nodes.py b/mypy/nodes.py index 39cbee3c85252..e7d7e84d5ac2b 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -5,35 +5,68 @@ import os from abc import abstractmethod from collections import defaultdict +from collections.abc import Iterator, Sequence from enum import Enum, unique -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Final, - Iterator, - List, - Optional, - Sequence, - Tuple, - TypeVar, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, Callable, Final, Optional, TypeVar, Union, cast from typing_extensions import TypeAlias as _TypeAlias, TypeGuard +from librt.internal import ( + read_float as read_float_bare, + read_int as read_int_bare, + read_str as read_str_bare, + write_int as write_int_bare, + write_str as write_str_bare, +) from mypy_extensions import trait import mypy.strconv +from mypy.cache import ( + DICT_STR_GEN, + DT_SPEC, + END_TAG, + LIST_GEN, + LIST_STR, + LITERAL_COMPLEX, + LITERAL_NONE, + ReadBuffer, + Tag, + WriteBuffer, + read_bool, + read_int, + read_int_list, + read_int_opt, + read_json, + read_literal, + read_str, + read_str_list, + read_str_opt, + read_str_opt_list, + read_tag, + write_bool, + write_int, + write_int_list, + write_int_opt, + write_json, + write_literal, + write_str, + write_str_list, + write_str_opt, + write_str_opt_list, + write_tag, +) from mypy.options import Options -from mypy.util import is_typeshed_file, short_type +from mypy.util import is_sunder, is_typeshed_file, short_type from mypy.visitor import ExpressionVisitor, NodeVisitor, StatementVisitor if TYPE_CHECKING: from mypy.patterns import Pattern +@unique +class NotParsed(Enum): + VALUE = "NotParsed" + + class Context: """Base type for objects that are valid as error message locations.""" @@ -81,7 +114,7 @@ def set_line( T = TypeVar("T") -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] # Symbol table node kinds @@ -102,9 +135,14 @@ def set_line( REVEAL_TYPE: Final = 0 REVEAL_LOCALS: Final = 1 -LITERAL_YES: Final = 2 -LITERAL_TYPE: Final = 1 -LITERAL_NO: Final = 0 +# Kinds of 'literal' expressions. +# +# Use the function mypy.literals.literal to calculate these. +# +# TODO: Can we make these less confusing? +LITERAL_YES: Final = 2 # Value of expression known statically +LITERAL_TYPE: Final = 1 # Type of expression can be narrowed (e.g. variable reference) +LITERAL_NO: Final = 0 # None of the above node_kinds: Final = {LDEF: "Ldef", GDEF: "Gdef", MDEF: "Mdef", UNBOUND_IMPORTED: "UnboundImported"} inverse_node_kinds: Final = {_kind: _name for _name, _kind in node_kinds.items()} @@ -156,18 +194,6 @@ def set_line( "builtins.frozenset": "typing.FrozenSet", } -_nongen_builtins: Final = {"builtins.tuple": "typing.Tuple", "builtins.enumerate": ""} -_nongen_builtins.update((name, alias) for alias, name in type_aliases.items()) -# Drop OrderedDict from this for backward compatibility -del _nongen_builtins["collections.OrderedDict"] -# HACK: consequence of hackily treating LiteralString as an alias for str -del _nongen_builtins["builtins.str"] - - -def get_nongen_builtins(python_version: tuple[int, int]) -> dict[str, str]: - # After 3.9 with pep585 generic builtins are allowed - return _nongen_builtins if python_version < (3, 9) else {} - RUNTIME_PROTOCOL_DECOS: Final = ( "typing.runtime_checkable", @@ -184,15 +210,12 @@ class Node(Context): __slots__ = () def __str__(self) -> str: - ans = self.accept(mypy.strconv.StrConv(options=Options())) - if ans is None: - return repr(self) - return ans + return self.accept(mypy.strconv.StrConv(options=Options())) def str_with_options(self, options: Options) -> str: - ans = self.accept(mypy.strconv.StrConv(options=options)) - assert ans - return ans + a = self.accept(mypy.strconv.StrConv(options=options)) + assert a + return a def accept(self, visitor: NodeVisitor[T]) -> T: raise RuntimeError("Not implemented", type(self)) @@ -263,9 +286,16 @@ def deserialize(cls, data: JsonDict) -> SymbolNode: return method(data) raise NotImplementedError(f"unexpected .class {classname}") + def write(self, data: WriteBuffer) -> None: + raise NotImplementedError(f"Cannot serialize {self.__class__.__name__} instance") + + @classmethod + def read(cls, data: ReadBuffer) -> SymbolNode: + raise NotImplementedError(f"Cannot deserialize {cls.__name__} instance") + # Items: fullname, related symbol table node, surrounding type (if any) -Definition: _TypeAlias = Tuple[str, "SymbolTableNode", Optional["TypeInfo"]] +Definition: _TypeAlias = tuple[str, "SymbolTableNode", Optional["TypeInfo"]] class MypyFile(SymbolNode): @@ -276,6 +306,7 @@ class MypyFile(SymbolNode): "path", "defs", "alias_deps", + "module_refs", "is_bom", "names", "imports", @@ -299,6 +330,9 @@ class MypyFile(SymbolNode): defs: list[Statement] # Type alias dependencies as mapping from target to set of alias full names alias_deps: defaultdict[str, set[str]] + # The set of all dependencies (suppressed or not) that this module accesses, either + # directly or indirectly. + module_refs: set[str] # Is there a UTF-8 BOM at the start? is_bom: bool names: SymbolTable @@ -339,6 +373,7 @@ def __init__( self.imports = imports self.is_bom = is_bom self.alias_deps = defaultdict(set) + self.module_refs = set() self.plugin_deps = {} if ignored_lines: self.ignored_lines = ignored_lines @@ -391,7 +426,7 @@ def serialize(self) -> JsonDict: "is_stub": self.is_stub, "path": self.path, "is_partial_stub_package": self.is_partial_stub_package, - "future_import_flags": list(self.future_import_flags), + "future_import_flags": sorted(self.future_import_flags), } @classmethod @@ -407,6 +442,30 @@ def deserialize(cls, data: JsonDict) -> MypyFile: tree.future_import_flags = set(data["future_import_flags"]) return tree + def write(self, data: WriteBuffer) -> None: + write_tag(data, MYPY_FILE) + write_str(data, self._fullname) + self.names.write(data, self._fullname) + write_bool(data, self.is_stub) + write_str(data, self.path) + write_bool(data, self.is_partial_stub_package) + write_str_list(data, sorted(self.future_import_flags)) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> MypyFile: + assert read_tag(data) == MYPY_FILE + tree = MypyFile([], []) + tree._fullname = read_str(data) + tree.names = SymbolTable.read(data) + tree.is_stub = read_bool(data) + tree.path = read_str(data) + tree.is_partial_stub_package = read_bool(data) + tree.future_import_flags = set(read_str_list(data)) + tree.is_cache_skeleton = True + assert read_tag(data) == END_TAG + return tree + class ImportBase(Statement): """Base class for all import statements.""" @@ -531,6 +590,8 @@ def __init__(self) -> None: self.info = FUNC_NO_INFO self.is_property = False self.is_class = False + # Is this a `@staticmethod` (explicit or implicit)? + # Note: use has_self_or_cls_argument to check if there is `self` or `cls` argument self.is_static = False self.is_final = False self.is_explicit_override = False @@ -547,6 +608,15 @@ def name(self) -> str: def fullname(self) -> str: return self._fullname + @property + def has_self_or_cls_argument(self) -> bool: + """If used as a method, does it have an argument for method binding (`self`, `cls`)? + + This is true for `__new__` even though `__new__` does not undergo method binding, + because we still usually assume that `cls` corresponds to the enclosing class. + """ + return not self.is_static or self.name == "__new__" + OverloadPart: _TypeAlias = Union["FuncDef", "Decorator"] @@ -561,17 +631,29 @@ 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", + "setter_index", + "_is_trivial_self", + ) items: list[OverloadPart] unanalyzed_items: list[OverloadPart] impl: OverloadPart | None + deprecated: str | None + setter_index: int | None def __init__(self, items: list[OverloadPart]) -> None: super().__init__() self.items = items self.unanalyzed_items = items.copy() self.impl = None + self.deprecated = None + self.setter_index = None + self._is_trivial_self: bool | None = 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) @@ -585,6 +667,40 @@ def name(self) -> str: assert self.impl is not None return self.impl.name + @property + def is_trivial_self(self) -> bool: + """Check we can use bind_self() fast path for this overload. + + This will return False if at least one overload: + * Has an explicit self annotation, or Self in signature. + * Has a non-trivial decorator. + """ + if self._is_trivial_self is not None: + return self._is_trivial_self + for i, item in enumerate(self.items): + # Note: bare @property is removed in visit_decorator(). + trivial = 1 if i > 0 or not self.is_property else 0 + if isinstance(item, FuncDef): + if not item.is_trivial_self: + self._is_trivial_self = False + return False + elif len(item.decorators) > trivial or not item.func.is_trivial_self: + self._is_trivial_self = False + return False + self._is_trivial_self = True + return True + + @property + def setter(self) -> Decorator: + # Do some consistency checks first. + first_item = self.items[0] + assert isinstance(first_item, Decorator) + assert first_item.var.is_settable_property + assert self.setter_index is not None + item = self.items[self.setter_index] + assert isinstance(item, Decorator) + return item + def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_overloaded_func_def(self) @@ -596,6 +712,8 @@ 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, + "setter_index": self.setter_index, } @classmethod @@ -615,9 +733,50 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef: res.type = typ res._fullname = data["fullname"] set_flags(res, data["flags"]) + res.deprecated = data["deprecated"] + res.setter_index = data["setter_index"] # NOTE: res.info will be set in the fixup phase. return res + def write(self, data: WriteBuffer) -> None: + write_tag(data, OVERLOADED_FUNC_DEF) + write_tag(data, LIST_GEN) + write_int_bare(data, len(self.items)) + for item in self.items: + item.write(data) + mypy.types.write_type_opt(data, self.type) + write_str(data, self._fullname) + if self.impl is None: + write_tag(data, LITERAL_NONE) + else: + self.impl.write(data) + write_flags(data, self, FUNCBASE_FLAGS) + write_str_opt(data, self.deprecated) + write_int_opt(data, self.setter_index) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> OverloadedFuncDef: + assert read_tag(data) == LIST_GEN + res = OverloadedFuncDef([read_overload_part(data) for _ in range(read_int_bare(data))]) + typ = mypy.types.read_type_opt(data) + if typ is not None: + assert isinstance(typ, mypy.types.ProperType) + res.type = typ + res._fullname = read_str(data) + tag = read_tag(data) + if tag != LITERAL_NONE: + res.impl = read_overload_part(data, tag) + # set line for empty overload items, as not set in __init__ + if len(res.items) > 0: + res.set_line(res.impl.line) + read_flags(data, res, FUNCBASE_FLAGS) + res.deprecated = read_str_opt(data) + res.setter_index = read_int_opt(data) + # NOTE: res.info will be set in the fixup phase. + assert read_tag(data) == END_TAG + return res + def is_dynamic(self) -> bool: return all(item.is_dynamic() for item in self.items) @@ -666,7 +825,7 @@ def set_line( class TypeParam: - __slots__ = ("name", "kind", "upper_bound", "values") + __slots__ = ("name", "kind", "upper_bound", "values", "default") def __init__( self, @@ -674,11 +833,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 + [ @@ -752,6 +913,7 @@ def is_dynamic(self) -> bool: "is_decorated", "is_conditional", "is_trivial_body", + "is_trivial_self", "is_mypy_only", ] @@ -775,12 +937,15 @@ class FuncDef(FuncItem, SymbolNode, Statement): "is_conditional", "abstract_status", "original_def", - "deco_line", "is_trivial_body", + "is_trivial_self", + "has_self_attr_def", "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", + "original_first_arg", ) __match_args__ = ("name", "arguments", "type", "body") @@ -804,12 +969,23 @@ def __init__( self.is_trivial_body = False # Original conditional definition self.original_def: None | FuncDef | Var | Decorator = None - # Used for error reporting (to keep backward compatibility with pre-3.8) - self.deco_line: int | None = None # Definitions that appear in if TYPE_CHECKING are marked with this flag. self.is_mypy_only = False self.dataclass_transform_spec: DataclassTransformSpec | None = None self.docstring: str | None = None + self.deprecated: str | None = None + # This is used to simplify bind_self() logic in trivial cases (which are + # the majority). In cases where self is not annotated and there are no Self + # in the signature we can simply drop the first argument. + self.is_trivial_self = False + # Keep track of functions where self attributes are defined. + self.has_self_attr_def = False + # This is needed because for positional-only arguments the name is set to None, + # but we sometimes still want to show it in error messages. + if arguments: + self.original_first_arg: str | None = arguments[0].variable.name + else: + self.original_first_arg = None @property def name(self) -> str: @@ -840,6 +1016,8 @@ def serialize(self) -> JsonDict: if self.dataclass_transform_spec is None else self.dataclass_transform_spec.serialize() ), + "deprecated": self.deprecated, + "original_first_arg": self.original_first_arg, } @classmethod @@ -860,23 +1038,71 @@ def deserialize(cls, data: JsonDict) -> FuncDef: set_flags(ret, data["flags"]) # NOTE: ret.info is set in the fixup phase. ret.arg_names = data["arg_names"] - ret.arg_kinds = [ArgKind(x) for x in data["arg_kinds"]] + ret.original_first_arg = data.get("original_first_arg") + ret.arg_kinds = [ARG_KINDS[x] for x in data["arg_kinds"]] ret.abstract_status = data["abstract_status"] ret.dataclass_transform_spec = ( DataclassTransformSpec.deserialize(data["dataclass_transform_spec"]) 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 + del ret.min_args + return ret + + def write(self, data: WriteBuffer) -> None: + write_tag(data, FUNC_DEF) + write_str(data, self._name) + mypy.types.write_type_opt(data, self.type) + write_str(data, self._fullname) + write_flags(data, self, FUNCDEF_FLAGS) + write_str_opt_list(data, self.arg_names) + write_int_list(data, [int(ak.value) for ak in self.arg_kinds]) + write_int(data, self.abstract_status) + if self.dataclass_transform_spec is None: + write_tag(data, LITERAL_NONE) + else: + self.dataclass_transform_spec.write(data) + write_str_opt(data, self.deprecated) + write_str_opt(data, self.original_first_arg) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> FuncDef: + name = read_str(data) + typ: mypy.types.FunctionLike | None = None + tag = read_tag(data) + if tag != LITERAL_NONE: + typ = mypy.types.read_function_like(data, tag) + ret = FuncDef(name, [], Block([]), typ) + ret._fullname = read_str(data) + read_flags(data, ret, FUNCDEF_FLAGS) + # NOTE: ret.info is set in the fixup phase. + ret.arg_names = read_str_opt_list(data) + ret.arg_kinds = [ARG_KINDS[ak] for ak in read_int_list(data)] + ret.abstract_status = read_int(data) + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == DT_SPEC + ret.dataclass_transform_spec = DataclassTransformSpec.read(data) + ret.deprecated = read_str_opt(data) + ret.original_first_arg = read_str_opt(data) # Leave these uninitialized so that future uses will trigger an error del ret.arguments del ret.max_pos del ret.min_args + assert read_tag(data) == END_TAG return ret # All types that are both SymbolNodes and FuncBases. See the FuncBase # docstring for the rationale. -SYMBOL_FUNCBASE_TYPES = (OverloadedFuncDef, FuncDef) +# See https://github.com/python/mypy/pull/13607#issuecomment-1236357236 +# TODO: we want to remove this at some point and just use `FuncBase` ideally. +SYMBOL_FUNCBASE_TYPES: Final = (OverloadedFuncDef, FuncDef) class Decorator(SymbolNode, Statement): @@ -943,6 +1169,24 @@ def deserialize(cls, data: JsonDict) -> Decorator: dec.is_overload = data["is_overload"] return dec + def write(self, data: WriteBuffer) -> None: + write_tag(data, DECORATOR) + self.func.write(data) + self.var.write(data) + write_bool(data, self.is_overload) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> Decorator: + assert read_tag(data) == FUNC_DEF + func = FuncDef.read(data) + assert read_tag(data) == VAR + var = Var.read(data) + dec = Decorator(func, [], var) + dec.is_overload = read_bool(data) + assert read_tag(data) == END_TAG + return dec + def is_dynamic(self) -> bool: return self.func.is_dynamic() @@ -959,6 +1203,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", @@ -982,6 +1227,7 @@ class Var(SymbolNode): "_fullname", "info", "type", + "setter_type", "final_value", "is_self", "is_cls", @@ -995,6 +1241,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", @@ -1015,6 +1262,8 @@ def __init__(self, name: str, type: mypy.types.Type | None = None) -> None: # TODO: Should be Optional[TypeInfo] self.info = VAR_NO_INFO self.type: mypy.types.Type | None = type # Declared or inferred type, or None + # The setter type for settable properties. + self.setter_type: mypy.types.CallableType | None = None # Is this the first argument to an ordinary method (usually "self")? self.is_self = False # Is this the first argument to a classmethod (typically "cls")? @@ -1029,6 +1278,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 @@ -1068,6 +1318,10 @@ def name(self) -> str: def fullname(self) -> str: return self._fullname + def __repr__(self) -> str: + name = self.fullname or self.name + return f"" + def accept(self, visitor: NodeVisitor[T]) -> T: return visitor.visit_var(self) @@ -1079,6 +1333,7 @@ def serialize(self) -> JsonDict: "name": self._name, "fullname": self._fullname, "type": None if self.type is None else self.type.serialize(), + "setter_type": None if self.setter_type is None else self.setter_type.serialize(), "flags": get_flags(self, VAR_FLAGS), } if self.final_value is not None: @@ -1090,13 +1345,56 @@ def deserialize(cls, data: JsonDict) -> Var: assert data[".class"] == "Var" name = data["name"] type = None if data["type"] is None else mypy.types.deserialize_type(data["type"]) + setter_type = ( + None + if data["setter_type"] is None + else mypy.types.deserialize_type(data["setter_type"]) + ) v = Var(name, type) + assert ( + setter_type is None + or isinstance(setter_type, mypy.types.ProperType) + and isinstance(setter_type, mypy.types.CallableType) + ) + v.setter_type = setter_type v.is_ready = False # Override True default set in __init__ v._fullname = data["fullname"] set_flags(v, data["flags"]) v.final_value = data.get("final_value") return v + def write(self, data: WriteBuffer) -> None: + write_tag(data, VAR) + write_str(data, self._name) + mypy.types.write_type_opt(data, self.type) + mypy.types.write_type_opt(data, self.setter_type) + write_str(data, self._fullname) + write_flags(data, self, VAR_FLAGS) + write_literal(data, self.final_value) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> Var: + name = read_str(data) + typ = mypy.types.read_type_opt(data) + v = Var(name, typ) + setter_type: mypy.types.CallableType | None = None + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == mypy.types.CALLABLE_TYPE + setter_type = mypy.types.CallableType.read(data) + v.setter_type = setter_type + v.is_ready = False # Override True default set in __init__ + v._fullname = read_str(data) + read_flags(data, v, VAR_FLAGS) + tag = read_tag(data) + if tag == LITERAL_COMPLEX: + v.final_value = complex(read_float_bare(data), read_float_bare(data)) + elif tag != LITERAL_NONE: + v.final_value = read_literal(data, tag) + assert read_tag(data) == END_TAG + return v + class ClassDef(Statement): """Class definition""" @@ -1115,7 +1413,6 @@ class ClassDef(Statement): "keywords", "analyzed", "has_incompatible_baseclass", - "deco_line", "docstring", "removed_statements", ) @@ -1166,8 +1463,6 @@ def __init__( self.keywords = dict(keywords) if keywords else {} self.analyzed = None self.has_incompatible_baseclass = False - # Used for error reporting (to keep backwad compatibility with pre-3.8) - self.deco_line: int | None = None self.docstring: str | None = None self.removed_statements = [] @@ -1210,6 +1505,20 @@ def deserialize(cls, data: JsonDict) -> ClassDef: res.fullname = data["fullname"] return res + def write(self, data: WriteBuffer) -> None: + write_tag(data, CLASS_DEF) + write_str(data, self.name) + mypy.types.write_type_list(data, self.type_vars) + write_str(data, self.fullname) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> ClassDef: + res = ClassDef(read_str(data), Block([]), mypy.types.read_type_var_likes(data)) + res.fullname = read_str(data) + assert read_tag(data) == END_TAG + return res + class GlobalDecl(Statement): """Declaration global x, y, ...""" @@ -1250,7 +1559,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 +1567,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) @@ -1621,11 +1930,12 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class MatchStmt(Statement): - __slots__ = ("subject", "patterns", "guards", "bodies") + __slots__ = ("subject", "subject_dummy", "patterns", "guards", "bodies") __match_args__ = ("subject", "patterns", "guards", "bodies") subject: Expression + subject_dummy: NameExpr | None patterns: list[Pattern] guards: list[Expression | None] bodies: list[Block] @@ -1640,6 +1950,7 @@ def __init__( super().__init__() assert len(patterns) == len(guards) == len(bodies) self.subject = subject + self.subject_dummy = None self.patterns = patterns self.guards = guards self.bodies = bodies @@ -1649,7 +1960,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 +1968,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 +1976,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) @@ -1698,21 +2011,26 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: class StrExpr(Expression): """String literal""" - __slots__ = ("value",) + __slots__ = ("value", "as_type") __match_args__ = ("value",) value: str # '' by default + # If this value expression can also be parsed as a valid type expression, + # represents the type denoted by the type expression. + # None means "is not a type expression". + as_type: NotParsed | mypy.types.Type | None def __init__(self, value: str) -> None: super().__init__() self.value = value + self.as_type = NotParsed.VALUE 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) @@ -1932,6 +2250,8 @@ def is_star(self) -> bool: ARG_STAR2: Final = ArgKind.ARG_STAR2 ARG_NAMED_OPT: Final = ArgKind.ARG_NAMED_OPT +ARG_KINDS: Final = (ARG_POS, ARG_OPT, ARG_STAR, ARG_NAMED, ARG_STAR2, ARG_NAMED_OPT) + class CallExpr(Expression): """Call expression. @@ -2005,7 +2325,7 @@ class IndexExpr(Expression): Also wraps type application such as List[int] as a special form. """ - __slots__ = ("base", "index", "method_type", "analyzed") + __slots__ = ("base", "index", "method_type", "analyzed", "as_type") __match_args__ = ("base", "index") @@ -2016,6 +2336,10 @@ class IndexExpr(Expression): # If not None, this is actually semantically a type application # Class[type, ...] or a type alias initializer. analyzed: TypeApplication | TypeAliasExpr | None + # If this value expression can also be parsed as a valid type expression, + # represents the type denoted by the type expression. + # None means "is not a type expression". + as_type: NotParsed | mypy.types.Type | None def __init__(self, base: Expression, index: Expression) -> None: super().__init__() @@ -2023,6 +2347,7 @@ def __init__(self, base: Expression, index: Expression) -> None: self.index = index self.method_type = None self.analyzed = None + self.as_type = NotParsed.VALUE def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_index_expr(self) @@ -2057,7 +2382,7 @@ class AssignmentExpr(Expression): __match_args__ = ("target", "value") - def __init__(self, target: Expression, value: Expression) -> None: + def __init__(self, target: NameExpr, value: Expression) -> None: super().__init__() self.target = target self.value = value @@ -2080,6 +2405,7 @@ class OpExpr(Expression): "right_always", "right_unreachable", "analyzed", + "as_type", ) __match_args__ = ("left", "op", "right") @@ -2095,6 +2421,10 @@ class OpExpr(Expression): right_unreachable: bool # Used for expressions that represent a type "X | Y" in some contexts analyzed: TypeAliasExpr | None + # If this value expression can also be parsed as a valid type expression, + # represents the type denoted by the type expression. + # None means "is not a type expression". + as_type: NotParsed | mypy.types.Type | None def __init__( self, op: str, left: Expression, right: Expression, analyzed: TypeAliasExpr | None = None @@ -2107,11 +2437,19 @@ def __init__( self.right_always = False self.right_unreachable = False self.analyzed = analyzed + self.as_type = NotParsed.VALUE def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_op_expr(self) +# Expression subtypes that could represent the root of a valid type expression. +# +# May have an "as_type" attribute to hold the type for a type expression parsed +# during the SemanticAnalyzer pass. +MaybeTypeExpression = (IndexExpr, MemberExpr, NameExpr, OpExpr, StrExpr) + + class ComparisonExpr(Expression): """Comparison expression (e.g. a < b > c < d).""" @@ -2189,6 +2527,23 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_cast_expr(self) +class TypeFormExpr(Expression): + """TypeForm(type) expression.""" + + __slots__ = ("type",) + + __match_args__ = ("type",) + + type: mypy.types.Type + + def __init__(self, typ: mypy.types.Type) -> None: + super().__init__() + self.type = typ + + def accept(self, visitor: ExpressionVisitor[T]) -> T: + return visitor.visit_type_form_expr(self) + + class AssertTypeExpr(Expression): """Represents a typing.assert_type(expr, type) call.""" @@ -2556,6 +2911,11 @@ def fullname(self) -> str: return self._fullname +# All types that are both SymbolNodes and Expressions. +# Use when common children of them are needed. +SYMBOL_NODE_EXPRESSION_TYPES: Final = (TypeVarLikeExpr,) + + class TypeVarExpr(TypeVarLikeExpr): """Type variable expression TypeVar(...). @@ -2616,6 +2976,29 @@ def deserialize(cls, data: JsonDict) -> TypeVarExpr: data["variance"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_VAR_EXPR) + write_str(data, self._name) + write_str(data, self._fullname) + mypy.types.write_type_list(data, self.values) + self.upper_bound.write(data) + self.default.write(data) + write_int(data, self.variance) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeVarExpr: + ret = TypeVarExpr( + read_str(data), + read_str(data), + mypy.types.read_type_list(data), + mypy.types.read_type(data), + mypy.types.read_type(data), + read_int(data), + ) + assert read_tag(data) == END_TAG + return ret + class ParamSpecExpr(TypeVarLikeExpr): __slots__ = () @@ -2646,6 +3029,27 @@ def deserialize(cls, data: JsonDict) -> ParamSpecExpr: data["variance"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, PARAM_SPEC_EXPR) + write_str(data, self._name) + write_str(data, self._fullname) + self.upper_bound.write(data) + self.default.write(data) + write_int(data, self.variance) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> ParamSpecExpr: + ret = ParamSpecExpr( + read_str(data), + read_str(data), + mypy.types.read_type(data), + mypy.types.read_type(data), + read_int(data), + ) + assert read_tag(data) == END_TAG + return ret + class TypeVarTupleExpr(TypeVarLikeExpr): """Type variable tuple expression TypeVarTuple(...).""" @@ -2696,6 +3100,31 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleExpr: data["variance"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_VAR_TUPLE_EXPR) + self.tuple_fallback.write(data) + write_str(data, self._name) + write_str(data, self._fullname) + self.upper_bound.write(data) + self.default.write(data) + write_int(data, self.variance) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeVarTupleExpr: + assert read_tag(data) == mypy.types.INSTANCE + fallback = mypy.types.Instance.read(data) + ret = TypeVarTupleExpr( + read_str(data), + read_str(data), + mypy.types.read_type(data), + fallback, + mypy.types.read_type(data), + read_int(data), + ) + assert read_tag(data) == END_TAG + return ret + class TypeAliasExpr(Expression): """Type alias expression (rvalue).""" @@ -2887,6 +3316,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. @@ -2909,6 +3342,7 @@ class is generic then it will be a type constructor of higher kind. "_mro_refs", "bad_mro", "is_final", + "is_disjoint_base", "declared_metaclass", "metaclass_type", "names", @@ -2942,6 +3376,8 @@ class is generic then it will be a type constructor of higher kind. "self_type", "dataclass_transform_spec", "is_type_check_only", + "deprecated", + "type_object_type", ) _fullname: str # Fully qualified name @@ -2958,6 +3394,7 @@ class is generic then it will be a type constructor of higher kind. _mro_refs: list[str] | None bad_mro: bool # Could not construct full MRO is_final: bool + is_disjoint_base: bool declared_metaclass: mypy.types.Instance | None metaclass_type: mypy.types.Instance | None @@ -3095,6 +3532,13 @@ 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 + + # Cached value of class constructor type, i.e. the type of class object when it + # appears in runtime context. + type_object_type: mypy.types.FunctionLike | None + FLAGS: Final = [ "is_abstract", "is_enum", @@ -3105,6 +3549,7 @@ class is generic then it will be a type constructor of higher kind. "is_protocol", "runtime_protocol", "is_final", + "is_disjoint_base", "is_intersection", ] @@ -3137,6 +3582,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None self.type_var_tuple_suffix: int | None = None self.add_type_vars() self.is_final = False + self.is_disjoint_base = False self.is_enum = False self.fallback_to_any = False self.meta_fallback_to_any = False @@ -3152,6 +3598,8 @@ 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 + self.type_object_type = None def add_type_vars(self) -> None: self.has_type_var_tuple_type = False @@ -3209,6 +3657,58 @@ def protocol_members(self) -> list[str]: members.add(name) return sorted(members) + @property + def enum_members(self) -> list[str]: + # TODO: cache the results? + members = [] + for name, sym in self.names.items(): + # Case 1: + # + # class MyEnum(Enum): + # @member + # def some(self): ... + if isinstance(sym.node, Decorator): + if any( + dec.fullname == "enum.member" + for dec in sym.node.decorators + if isinstance(dec, RefExpr) + ): + members.append(name) + continue + # Case 2: + # + # class MyEnum(Enum): + # x = 1 + # + # Case 3: + # + # class MyEnum(Enum): + # class Other: ... + elif isinstance(sym.node, (Var, TypeInfo)): + if ( + # TODO: properly support ignored names from `_ignore_` + name in EXCLUDED_ENUM_ATTRIBUTES + or is_sunder(name) + or name.startswith("__") # dunder and private + ): + continue # name is excluded + + if isinstance(sym.node, Var): + if not sym.node.has_explicit_value: + continue # unannotated value not a member + + typ = mypy.types.get_proper_type(sym.node.type) + if ( + isinstance(typ, mypy.types.FunctionLike) and not typ.is_type_obj() + ) or ( # explicit `@member` is required + isinstance(typ, mypy.types.Instance) + and typ.type.fullname == "enum.nonmember" + ): + continue # name is not a member + + members.append(name) + return members + def __getitem__(self, name: str) -> SymbolTableNode: n = self.get(name) if n: @@ -3232,7 +3732,7 @@ def get_method(self, name: str) -> FuncBase | Decorator | None: for cls in self.mro: if name in cls.names: node = cls.names[name].node - if isinstance(node, FuncBase): + if isinstance(node, SYMBOL_FUNCBASE_TYPES): return node elif isinstance(node, Decorator): # Two `if`s make `mypyc` happy return node @@ -3246,21 +3746,68 @@ def calculate_metaclass_type(self) -> mypy.types.Instance | None: return declared if self._fullname == "builtins.type": return mypy.types.Instance(self, []) - candidates = [ - s.declared_metaclass - for s in self.mro - if s.declared_metaclass is not None and s.declared_metaclass.type is not None - ] - for c in candidates: - if all(other.type in c.type.mro for other in candidates): - return c + + winner = declared + for super_class in self.mro[1:]: + super_meta = super_class.declared_metaclass + if super_meta is None or super_meta.type is None: + continue + if winner is None: + winner = super_meta + continue + if winner.type.has_base(super_meta.type.fullname): + continue + if super_meta.type.has_base(winner.type.fullname): + winner = super_meta + continue + # metaclass conflict + winner = None + break + + return winner + + def explain_metaclass_conflict(self) -> str | None: + # Compare to logic in calculate_metaclass_type + declared = self.declared_metaclass + if declared is not None and not declared.type.has_base("builtins.type"): + return None + if self._fullname == "builtins.type": + return None + + winner = declared + if declared is None: + resolution_steps = [] + else: + resolution_steps = [f'"{declared.type.fullname}" (metaclass of "{self.fullname}")'] + for super_class in self.mro[1:]: + super_meta = super_class.declared_metaclass + if super_meta is None or super_meta.type is None: + continue + if winner is None: + winner = super_meta + resolution_steps.append( + f'"{winner.type.fullname}" (metaclass of "{super_class.fullname}")' + ) + continue + if winner.type.has_base(super_meta.type.fullname): + continue + if super_meta.type.has_base(winner.type.fullname): + winner = super_meta + resolution_steps.append( + f'"{winner.type.fullname}" (metaclass of "{super_class.fullname}")' + ) + continue + # metaclass conflict + conflict = f'"{super_meta.type.fullname}" (metaclass of "{super_class.fullname}")' + return f"{' > '.join(resolution_steps)} conflicts with {conflict}" + return None - def is_metaclass(self) -> bool: + def is_metaclass(self, *, precise: bool = False) -> bool: return ( self.has_base("builtins.type") or self.fullname == "abc.ABCMeta" - or self.fallback_to_any + or (self.fallback_to_any and not precise) ) def has_base(self, fullname: str) -> bool: @@ -3288,6 +3835,8 @@ def update_tuple_type(self, typ: mypy.types.TupleType) -> None: self.special_alias = alias else: self.special_alias.target = alias.target + # Invalidate recursive status cache in case it was previously set. + self.special_alias._is_recursive = None def update_typeddict_type(self, typ: mypy.types.TypedDictType) -> None: """Update typeddict_type and special_alias as needed.""" @@ -3297,6 +3846,8 @@ def update_typeddict_type(self, typ: mypy.types.TypedDictType) -> None: self.special_alias = alias else: self.special_alias.target = alias.target + # Invalidate recursive status cache in case it was previously set. + self.special_alias._is_recursive = None def __str__(self) -> str: """Return a string representation of the type. @@ -3374,6 +3925,7 @@ def serialize(self) -> JsonDict: if self.dataclass_transform_spec is not None else None ), + "deprecated": self.deprecated, } return data @@ -3384,7 +3936,6 @@ def deserialize(cls, data: JsonDict) -> TypeInfo: module_name = data["module_name"] ti = TypeInfo(names, defn, module_name) ti._fullname = data["fullname"] - # TODO: Is there a reason to reconstruct ti.subtypes? ti.abstract_attributes = [(attr[0], attr[1]) for attr in data["abstract_attributes"]] ti.type_vars = data["type_vars"] ti.has_param_spec_type = data["has_param_spec_type"] @@ -3441,6 +3992,103 @@ def deserialize(cls, data: JsonDict) -> TypeInfo: ti.dataclass_transform_spec = DataclassTransformSpec.deserialize( data["dataclass_transform_spec"] ) + ti.deprecated = data.get("deprecated") + return ti + + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_INFO) + self.names.write(data, self.fullname) + self.defn.write(data) + write_str(data, self.module_name) + write_str(data, self.fullname) + write_str_list(data, [a for a, _ in self.abstract_attributes]) + write_int_list(data, [s for _, s in self.abstract_attributes]) + write_str_list(data, self.type_vars) + write_bool(data, self.has_param_spec_type) + mypy.types.write_type_list(data, self.bases) + write_str_list(data, [c.fullname for c in self.mro]) + mypy.types.write_type_list(data, self._promote) + mypy.types.write_type_opt(data, self.alt_promote) + mypy.types.write_type_opt(data, self.declared_metaclass) + mypy.types.write_type_opt(data, self.metaclass_type) + mypy.types.write_type_opt(data, self.tuple_type) + mypy.types.write_type_opt(data, self.typeddict_type) + write_flags(data, self, TypeInfo.FLAGS) + write_json(data, self.metadata) + if self.slots is None: + write_tag(data, LITERAL_NONE) + else: + write_str_list(data, sorted(self.slots)) + write_str_list(data, self.deletable_attributes) + mypy.types.write_type_opt(data, self.self_type) + if self.dataclass_transform_spec is None: + write_tag(data, LITERAL_NONE) + else: + self.dataclass_transform_spec.write(data) + write_str_opt(data, self.deprecated) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeInfo: + names = SymbolTable.read(data) + assert read_tag(data) == CLASS_DEF + defn = ClassDef.read(data) + module_name = read_str(data) + ti = TypeInfo(names, defn, module_name) + ti._fullname = read_str(data) + attrs = read_str_list(data) + statuses = read_int_list(data) + ti.abstract_attributes = list(zip(attrs, statuses)) + ti.type_vars = read_str_list(data) + ti.has_param_spec_type = read_bool(data) + ti.bases = [] + assert read_tag(data) == LIST_GEN + for _ in range(read_int_bare(data)): + assert read_tag(data) == mypy.types.INSTANCE + ti.bases.append(mypy.types.Instance.read(data)) + # NOTE: ti.mro will be set in the fixup phase based on these + # names. The reason we need to store the mro instead of just + # recomputing it from base classes has to do with a subtle + # point about fine-grained incremental: the cache files might + # not be loaded until after a class in the mro has changed its + # bases, which causes the mro to change. If we recomputed our + # mro, we would compute the *new* mro, which leaves us with no + # way to detect that the mro has changed! Thus, we need to make + # sure to load the original mro so that once the class is + # rechecked, it can tell that the mro has changed. + ti._mro_refs = read_str_list(data) + ti._promote = cast(list[mypy.types.ProperType], mypy.types.read_type_list(data)) + if (tag := read_tag(data)) != LITERAL_NONE: + assert tag == mypy.types.INSTANCE + ti.alt_promote = mypy.types.Instance.read(data) + if (tag := read_tag(data)) != LITERAL_NONE: + assert tag == mypy.types.INSTANCE + ti.declared_metaclass = mypy.types.Instance.read(data) + if (tag := read_tag(data)) != LITERAL_NONE: + assert tag == mypy.types.INSTANCE + ti.metaclass_type = mypy.types.Instance.read(data) + if (tag := read_tag(data)) != LITERAL_NONE: + assert tag == mypy.types.TUPLE_TYPE + ti.tuple_type = mypy.types.TupleType.read(data) + if (tag := read_tag(data)) != LITERAL_NONE: + assert tag == mypy.types.TYPED_DICT_TYPE + ti.typeddict_type = mypy.types.TypedDictType.read(data) + read_flags(data, ti, TypeInfo.FLAGS) + ti.metadata = read_json(data) + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == LIST_STR + ti.slots = {read_str_bare(data) for _ in range(read_int_bare(data))} + ti.deletable_attributes = read_str_list(data) + if (tag := read_tag(data)) != LITERAL_NONE: + assert tag == mypy.types.TYPE_VAR_TYPE + ti.self_type = mypy.types.TypeVarType.read(data) + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == DT_SPEC + ti.dataclass_transform_spec = DataclassTransformSpec.read(data) + ti.deprecated = read_str_opt(data) + assert read_tag(data) == END_TAG return ti @@ -3554,14 +4202,15 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here type will be initially an instance type with wrong number of type arguments. Such instances are all fixed either during or after main semantic analysis passes. We therefore store the difference between `List` and `List[Any]` rvalues (targets) - using the `no_args` flag. See also TypeAliasExpr.no_args. + using the `no_args` flag. Meaning of other fields: target: The target type. For generic aliases contains bound type variables as nested types (currently TypeVar and ParamSpec are supported). _fullname: Qualified name of this type alias. This is used in particular - to track fine grained dependencies from aliases. + to track fine-grained dependencies from aliases. + module: Module where the alias was defined. alias_tvars: Type variables used to define this alias. normalized: Used to distinguish between `A = List`, and `A = list`. Both are internally stored using `builtins.list` (because `typing.List` is @@ -3575,6 +4224,7 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here __slots__ = ( "target", "_fullname", + "module", "alias_tvars", "no_args", "normalized", @@ -3590,6 +4240,7 @@ def __init__( self, target: mypy.types.Type, fullname: str, + module: str, line: int, column: int, *, @@ -3600,6 +4251,7 @@ def __init__( python_3_12_type_alias: bool = False, ) -> None: self._fullname = fullname + self.module = module self.target = target if alias_tvars is None: alias_tvars = [] @@ -3634,6 +4286,7 @@ def from_tuple_type(cls, info: TypeInfo) -> TypeAlias: ) ), info.fullname, + info.module_name, info.line, info.column, ) @@ -3655,6 +4308,7 @@ def from_typeddict_type(cls, info: TypeInfo) -> TypeAlias: ) ), info.fullname, + info.module_name, info.line, info.column, ) @@ -3671,46 +4325,76 @@ def fullname(self) -> str: def has_param_spec_type(self) -> bool: return any(isinstance(v, mypy.types.ParamSpecType) for v in self.alias_tvars) + def accept(self, visitor: NodeVisitor[T]) -> T: + return visitor.visit_type_alias(self) + def serialize(self) -> JsonDict: data: JsonDict = { ".class": "TypeAlias", "fullname": self._fullname, + "module": self.module, "target": self.target.serialize(), "alias_tvars": [v.serialize() for v in self.alias_tvars], "no_args": self.no_args, "normalized": self.normalized, - "line": self.line, - "column": self.column, "python_3_12_type_alias": self.python_3_12_type_alias, } return data - def accept(self, visitor: NodeVisitor[T]) -> T: - return visitor.visit_type_alias(self) - @classmethod def deserialize(cls, data: JsonDict) -> TypeAlias: assert data[".class"] == "TypeAlias" fullname = data["fullname"] + module = data["module"] alias_tvars = [mypy.types.deserialize_type(v) for v in data["alias_tvars"]] assert all(isinstance(t, mypy.types.TypeVarLikeType) for t in alias_tvars) target = mypy.types.deserialize_type(data["target"]) no_args = data["no_args"] normalized = data["normalized"] - line = data["line"] - column = data["column"] python_3_12_type_alias = data["python_3_12_type_alias"] return cls( target, fullname, - line, - column, - alias_tvars=cast(List[mypy.types.TypeVarLikeType], alias_tvars), + module, + -1, + -1, + alias_tvars=cast(list[mypy.types.TypeVarLikeType], alias_tvars), no_args=no_args, normalized=normalized, python_3_12_type_alias=python_3_12_type_alias, ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_ALIAS) + write_str(data, self._fullname) + write_str(data, self.module) + self.target.write(data) + mypy.types.write_type_list(data, self.alias_tvars) + write_bool(data, self.no_args) + write_bool(data, self.normalized) + write_bool(data, self.python_3_12_type_alias) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeAlias: + fullname = read_str(data) + module = read_str(data) + target = mypy.types.read_type(data) + alias_tvars = mypy.types.read_type_var_likes(data) + ret = TypeAlias( + target, + fullname, + module, + -1, + -1, + alias_tvars=alias_tvars, + no_args=read_bool(data), + normalized=read_bool(data), + python_3_12_type_alias=read_bool(data), + ) + assert read_tag(data) == END_TAG + return ret + class PlaceholderNode(SymbolNode): """Temporary symbol node that will later become a real SymbolNode. @@ -3969,8 +4653,55 @@ def deserialize(cls, data: JsonDict) -> SymbolTableNode: stnode.plugin_generated = data["plugin_generated"] return stnode + def write(self, data: WriteBuffer, prefix: str, name: str) -> None: + write_tag(data, SYMBOL_TABLE_NODE) + write_int(data, self.kind) + write_bool(data, self.module_hidden) + write_bool(data, self.module_public) + write_bool(data, self.implicit) + write_bool(data, self.plugin_generated) + + cross_ref = None + if isinstance(self.node, MypyFile): + cross_ref = self.node.fullname + else: + assert self.node is not None, f"{prefix}:{name}" + if prefix is not None: + fullname = self.node.fullname + if ( + "." in fullname + and fullname != prefix + "." + name + and not (isinstance(self.node, Var) and self.node.from_module_getattr) + ): + assert not isinstance( + self.node, PlaceholderNode + ), f"Definition of {fullname} is unexpectedly incomplete" + cross_ref = fullname + + write_str_opt(data, cross_ref) + if cross_ref is None: + assert self.node is not None + self.node.write(data) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> SymbolTableNode: + assert read_tag(data) == SYMBOL_TABLE_NODE + sym = SymbolTableNode(read_int(data), None) + sym.module_hidden = read_bool(data) + sym.module_public = read_bool(data) + sym.implicit = read_bool(data) + sym.plugin_generated = read_bool(data) + cross_ref = read_str_opt(data) + if cross_ref is None: + sym.node = read_symbol(data) + else: + sym.cross_ref = cross_ref + assert read_tag(data) == END_TAG + return sym + -class SymbolTable(Dict[str, SymbolTableNode]): +class SymbolTable(dict[str, SymbolTableNode]): """Static representation of a namespace dictionary. This is used for module, class and function namespaces. @@ -3989,7 +4720,8 @@ def __str__(self) -> str: ): a.append(" " + str(key) + " : " + str(value)) else: - a.append(" ") + # Used in debugging: + a.append(" ") # type: ignore[unreachable] a = sorted(a) a.insert(0, "SymbolTable(") a[-1] += ")" @@ -4019,6 +4751,34 @@ def deserialize(cls, data: JsonDict) -> SymbolTable: st[key] = SymbolTableNode.deserialize(value) return st + def write(self, data: WriteBuffer, fullname: str) -> None: + size = 0 + for key, value in self.items(): + # Skip __builtins__: it's a reference to the builtins + # module that gets added to every module by + # SemanticAnalyzerPass2.visit_file(), but it shouldn't be + # accessed by users of the module. + if key == "__builtins__" or value.no_serialize: + continue + size += 1 + # We intentionally tag SymbolTable as a simple dictionary str -> SymbolTableNode. + write_tag(data, DICT_STR_GEN) + write_int_bare(data, size) + for key in sorted(self): + value = self[key] + if key == "__builtins__" or value.no_serialize: + continue + write_str_bare(data, key) + value.write(data, fullname, key) + + @classmethod + def read(cls, data: ReadBuffer) -> SymbolTable: + assert read_tag(data) == DICT_STR_GEN + size = read_int_bare(data) + return SymbolTable( + [(read_str_bare(data), SymbolTableNode.read(data)) for _ in range(size)] + ) + class DataclassTransformSpec: """Specifies how a dataclass-like transform should be applied. The fields here are based on the @@ -4069,6 +4829,27 @@ def deserialize(cls, data: JsonDict) -> DataclassTransformSpec: field_specifiers=tuple(data.get("field_specifiers", [])), ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, DT_SPEC) + write_bool(data, self.eq_default) + write_bool(data, self.order_default) + write_bool(data, self.kw_only_default) + write_bool(data, self.frozen_default) + write_str_list(data, self.field_specifiers) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> DataclassTransformSpec: + ret = DataclassTransformSpec( + eq_default=read_bool(data), + order_default=read_bool(data), + kw_only_default=read_bool(data), + frozen_default=read_bool(data), + field_specifiers=tuple(read_str_list(data)), + ) + assert read_tag(data) == END_TAG + return ret + def get_flags(node: Node, names: list[str]) -> list[str]: return [name for name in names if getattr(node, name)] @@ -4079,6 +4860,17 @@ def set_flags(node: Node, flags: list[str]) -> None: setattr(node, name, True) +def write_flags(data: WriteBuffer, node: SymbolNode, flags: list[str]) -> None: + for flag in flags: + write_bool(data, getattr(node, flag)) + + +def read_flags(data: ReadBuffer, node: SymbolNode, flags: list[str]) -> None: + for flag in flags: + if read_bool(data): + setattr(node, flag, True) + + def get_member_expr_fullname(expr: MemberExpr) -> str | None: """Return the qualified name representation of a member expression. @@ -4168,6 +4960,13 @@ def is_final_node(node: SymbolNode | None) -> bool: return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final +def get_func_def(typ: mypy.types.CallableType) -> SymbolNode | None: + definition = typ.definition + if isinstance(definition, Decorator): + definition = definition.func + return definition + + def local_definitions( names: SymbolTable, name_prefix: str, info: TypeInfo | None = None ) -> Iterator[Definition]: @@ -4187,3 +4986,52 @@ def local_definitions( yield fullname, symnode, info if isinstance(node, TypeInfo): yield from local_definitions(node.names, fullname, node) + + +# See docstring for mypy/cache.py for reserved tag ranges. +MYPY_FILE: Final[Tag] = 50 +OVERLOADED_FUNC_DEF: Final[Tag] = 51 +FUNC_DEF: Final[Tag] = 52 +DECORATOR: Final[Tag] = 53 +VAR: Final[Tag] = 54 +TYPE_VAR_EXPR: Final[Tag] = 55 +PARAM_SPEC_EXPR: Final[Tag] = 56 +TYPE_VAR_TUPLE_EXPR: Final[Tag] = 57 +TYPE_INFO: Final[Tag] = 58 +TYPE_ALIAS: Final[Tag] = 59 +CLASS_DEF: Final[Tag] = 60 +SYMBOL_TABLE_NODE: Final[Tag] = 61 + + +def read_symbol(data: ReadBuffer) -> SymbolNode: + tag = read_tag(data) + # The branches here are ordered manually by type "popularity". + if tag == VAR: + return Var.read(data) + if tag == FUNC_DEF: + return FuncDef.read(data) + if tag == DECORATOR: + return Decorator.read(data) + if tag == TYPE_INFO: + return TypeInfo.read(data) + if tag == OVERLOADED_FUNC_DEF: + return OverloadedFuncDef.read(data) + if tag == TYPE_VAR_EXPR: + return TypeVarExpr.read(data) + if tag == TYPE_ALIAS: + return TypeAlias.read(data) + if tag == PARAM_SPEC_EXPR: + return ParamSpecExpr.read(data) + if tag == TYPE_VAR_TUPLE_EXPR: + return TypeVarTupleExpr.read(data) + assert False, f"Unknown symbol tag {tag}" + + +def read_overload_part(data: ReadBuffer, tag: Tag | None = None) -> OverloadPart: + if tag is None: + tag = read_tag(data) + if tag == DECORATOR: + return Decorator.read(data) + if tag == FUNC_DEF: + return FuncDef.read(data) + assert False, f"Invalid tag for an OverloadPart {tag}" diff --git a/mypy/options.py b/mypy/options.py index 56bd92957b410..39490c9f0beeb 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -4,7 +4,9 @@ import re import sys import sysconfig -from typing import Any, Callable, Final, Mapping, Pattern +import warnings +from re import Pattern +from typing import Any, Callable, Final from mypy import defaults from mypy.errorcodes import ErrorCode, error_codes @@ -20,6 +22,7 @@ class BuildType: PER_MODULE_OPTIONS: Final = { # Please keep this list sorted "allow_redefinition", + "allow_redefinition_new", "allow_untyped_globals", "always_false", "always_true", @@ -42,6 +45,7 @@ class BuildType: "extra_checks", "follow_imports_for_stubs", "follow_imports", + "follow_untyped_imports", "ignore_errors", "ignore_missing_imports", "implicit_optional", @@ -50,6 +54,7 @@ class BuildType: "mypyc", "strict_concatenate", "strict_equality", + "strict_equality_for_none", "strict_optional", "warn_no_return", "warn_return_any", @@ -66,6 +71,9 @@ class BuildType: "plugins", "disable_bytearray_promotion", "disable_memoryview_promotion", + "strict_bytes", + "fixed_format_cache", + "untyped_calls_exclude", } ) - {"debug_cache"} @@ -75,7 +83,8 @@ class BuildType: PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" NEW_GENERIC_SYNTAX: Final = "NewGenericSyntax" INLINE_TYPEDDICT: Final = "InlineTypedDict" -INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, INLINE_TYPEDDICT)) +TYPE_FORM: Final = "TypeForm" +INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, INLINE_TYPEDDICT, TYPE_FORM)) COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK, NEW_GENERIC_SYNTAX)) @@ -113,6 +122,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. @@ -130,6 +141,7 @@ def __init__(self) -> None: self.explicit_package_bases = False # File names, directory names or subpaths to avoid checking self.exclude: list[str] = [] + self.exclude_gitignore: bool = False # disallow_any options self.disallow_any_generics = False @@ -173,6 +185,13 @@ 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 + + # Allow deprecated calls from function coming from modules/packages + # in this list (each item effectively acts as a prefix match) + self.deprecated_calls_exclude: list[str] = [] + # Warn about unused '# type: ignore' comments self.warn_unused_ignores = False @@ -205,10 +224,20 @@ def __init__(self) -> None: # and the same nesting level as the initialization self.allow_redefinition = False + # Allow flexible variable redefinition with an arbitrary type, in different + # blocks and and at different nesting levels + self.allow_redefinition_new = False + # Prohibit equality, identity, and container checks for non-overlapping types. # This makes 1 == '1', 1 in ['1'], and 1 is '1' errors. self.strict_equality = False + # Extend the logic of `strict_equality` to comparisons with `None`. + self.strict_equality_for_none = False + + # Disable treating bytearray and memoryview as subtypes of bytes + self.strict_bytes = False + # Deprecated, use extra_checks instead. self.strict_concatenate = False @@ -263,6 +292,7 @@ def __init__(self) -> None: self.incremental = True self.cache_dir = defaults.CACHE_DIR self.sqlite_cache = False + self.fixed_format_cache = False self.debug_cache = False self.skip_version_check = False self.skip_cache_mtime_checks = False @@ -368,26 +398,36 @@ def __init__(self) -> None: # skip most errors after this many messages have been reported. # -1 means unlimited. self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD - # Disable new experimental type inference algorithm. + # Disable new type inference algorithm. self.old_type_inference = False - # Deprecated reverse version of the above, do not use. - self.new_type_inference = False + # Disable expression cache (for debugging). + self.disable_expression_cache = False # Export line-level, limited, fine-grained dependency information in cache data # (undocumented feature). self.export_ref_info = False self.disable_bytearray_promotion = False self.disable_memoryview_promotion = False + # Deprecated, Mypy only supports Python 3.9+ self.force_uppercase_builtins = False self.force_union_syntax = False # Sets custom output format self.output: str | None = None + # Output html file for mypyc -a + self.mypyc_annotation_file: str | None = None + # Skip writing C output files, but perform all other steps of a build (allows + # preserving manual tweaks to generated C file) + self.mypyc_skip_c_generation = False + def use_lowercase_names(self) -> bool: - if self.python_version >= (3, 9): - return not self.force_uppercase_builtins - return False + warnings.warn( + "options.use_lowercase_names() is deprecated and will be removed in a future version", + DeprecationWarning, + stacklevel=2, + ) + return True def use_or_syntax(self) -> bool: if self.python_version >= (3, 10): @@ -397,17 +437,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("_")} @@ -443,6 +478,16 @@ def process_incomplete_features( if feature in COMPLETE_FEATURES: warning_callback(f"Warning: {feature} is already enabled by default") + def process_strict_bytes(self) -> None: + # Sync `--strict-bytes` and `--disable-{bytearray,memoryview}-promotion` + if self.strict_bytes: + # backwards compatibility + self.disable_bytearray_promotion = True + self.disable_memoryview_promotion = True + elif self.disable_bytearray_promotion and self.disable_memoryview_promotion: + # forwards compatibility + self.strict_bytes = True + def apply_changes(self, changes: dict[str, object]) -> Options: # Note: effects of this method *must* be idempotent. new_options = Options() @@ -467,7 +512,6 @@ def apply_changes(self, changes: dict[str, object]) -> Options: code = error_codes[code_str] new_options.enabled_error_codes.add(code) new_options.disabled_error_codes.discard(code) - return new_options def compare_stable(self, other_snapshot: dict[str, object]) -> bool: @@ -577,7 +621,7 @@ def compile_glob(self, s: str) -> Pattern[str]: expr += re.escape("." + part) if part != "*" else r"(\..*)?" return re.compile(expr + "\\Z") - def select_options_affecting_cache(self) -> Mapping[str, object]: + def select_options_affecting_cache(self) -> dict[str, object]: result: dict[str, object] = {} for opt in OPTIONS_AFFECTING_CACHE: val = getattr(self, opt) diff --git a/mypy/partially_defined.py b/mypy/partially_defined.py index da0bb517189ac..38154cf697e15 100644 --- a/mypy/partially_defined.py +++ b/mypy/partially_defined.py @@ -45,7 +45,7 @@ from mypy.patterns import AsPattern, StarredPattern from mypy.reachability import ALWAYS_TRUE, infer_pattern_value from mypy.traverser import ExtendedTraverserVisitor -from mypy.types import Type, UninhabitedType +from mypy.types import Type, UninhabitedType, get_proper_type class BranchState: @@ -507,7 +507,8 @@ def visit_break_stmt(self, o: BreakStmt) -> None: self.tracker.skip_branch() def visit_expression_stmt(self, o: ExpressionStmt) -> None: - if isinstance(self.type_map.get(o.expr, None), (UninhabitedType, type(None))): + typ = self.type_map.get(o.expr) + if typ is None or isinstance(get_proper_type(typ), UninhabitedType): self.tracker.skip_branch() super().visit_expression_stmt(o) diff --git a/mypy/plugin.py b/mypy/plugin.py index a1af7fa763508..9019e3c2256f1 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -114,22 +114,19 @@ 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 from abc import abstractmethod -from typing import Any, Callable, NamedTuple, TypeVar +from typing import TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar from mypy_extensions import mypyc_attr, trait from mypy.errorcodes import ErrorCode +from mypy.errors import ErrorInfo from mypy.lookup import lookup_fully_qualified from mypy.message_registry import ErrorMessage -from mypy.messages import MessageBuilder from mypy.nodes import ( ArgKind, CallExpr, @@ -141,7 +138,6 @@ class C: pass TypeInfo, ) from mypy.options import Options -from mypy.tvar_scope import TypeVarLikeScope from mypy.types import ( CallableType, FunctionLike, @@ -152,6 +148,10 @@ class C: pass UnboundType, ) +if TYPE_CHECKING: + from mypy.messages import MessageBuilder + from mypy.tvar_scope import TypeVarLikeScope + @trait class TypeAnalyzerPluginInterface: @@ -173,12 +173,12 @@ def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None raise NotImplementedError @abstractmethod - def named_type(self, name: str, args: list[Type]) -> Instance: + def named_type(self, fullname: str, args: list[Type], /) -> Instance: """Construct an instance of a builtin type with given name.""" raise NotImplementedError @abstractmethod - def analyze_type(self, typ: Type) -> Type: + def analyze_type(self, typ: Type, /) -> Type: """Analyze an unbound type using the default mypy logic.""" raise NotImplementedError @@ -241,7 +241,7 @@ def type_context(self) -> list[Type | None]: @abstractmethod def fail( self, msg: str | ErrorMessage, ctx: Context, /, *, code: ErrorCode | None = None - ) -> None: + ) -> ErrorInfo | None: """Emit an error message at given location.""" raise NotImplementedError @@ -322,7 +322,8 @@ def fail( @abstractmethod def anal_type( self, - t: Type, + typ: Type, + /, *, tvar_scope: TypeVarLikeScope | None = None, allow_tuple_literal: bool = False, @@ -343,7 +344,7 @@ def class_type(self, self_type: Type) -> Type: raise NotImplementedError @abstractmethod - def lookup_fully_qualified(self, name: str) -> SymbolTableNode: + def lookup_fully_qualified(self, fullname: str, /) -> SymbolTableNode: """Lookup a symbol by its fully qualified name. Raise an error if not found. @@ -351,7 +352,7 @@ def lookup_fully_qualified(self, name: str) -> SymbolTableNode: raise NotImplementedError @abstractmethod - def lookup_fully_qualified_or_none(self, name: str) -> SymbolTableNode | None: + def lookup_fully_qualified_or_none(self, fullname: str, /) -> SymbolTableNode | None: """Lookup a symbol by its fully qualified name. Return None if not found. @@ -387,12 +388,12 @@ def add_plugin_dependency(self, trigger: str, target: str | None = None) -> None raise NotImplementedError @abstractmethod - def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> Any: + def add_symbol_table_node(self, name: str, symbol: SymbolTableNode) -> Any: """Add node to global symbol table (or to nearest class if there is one).""" raise NotImplementedError @abstractmethod - def qualified_name(self, n: str) -> str: + def qualified_name(self, name: str) -> str: """Make qualified name using current module and enclosing class (if any).""" raise NotImplementedError @@ -495,6 +496,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 @@ -844,12 +846,22 @@ def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]: return deps def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None: - return self._find_hook(lambda plugin: plugin.get_type_analyze_hook(fullname)) + # Micro-optimization: Inline iteration over plugins + for plugin in self._plugins: + hook = plugin.get_type_analyze_hook(fullname) + if hook is not None: + return hook + return None def get_function_signature_hook( self, fullname: str ) -> Callable[[FunctionSigContext], FunctionLike] | None: - return self._find_hook(lambda plugin: plugin.get_function_signature_hook(fullname)) + # Micro-optimization: Inline iteration over plugins + for plugin in self._plugins: + hook = plugin.get_function_signature_hook(fullname) + if hook is not None: + return hook + return None def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: return self._find_hook(lambda plugin: plugin.get_function_hook(fullname)) @@ -857,13 +869,28 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] def get_method_signature_hook( self, fullname: str ) -> Callable[[MethodSigContext], FunctionLike] | None: - return self._find_hook(lambda plugin: plugin.get_method_signature_hook(fullname)) + # Micro-optimization: Inline iteration over plugins + for plugin in self._plugins: + hook = plugin.get_method_signature_hook(fullname) + if hook is not None: + return hook + return None def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: - return self._find_hook(lambda plugin: plugin.get_method_hook(fullname)) + # Micro-optimization: Inline iteration over plugins + for plugin in self._plugins: + hook = plugin.get_method_hook(fullname) + if hook is not None: + return hook + return None def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: - return self._find_hook(lambda plugin: plugin.get_attribute_hook(fullname)) + # Micro-optimization: Inline iteration over plugins + for plugin in self._plugins: + hook = plugin.get_attribute_hook(fullname) + if hook is not None: + return hook + return None def get_class_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: return self._find_hook(lambda plugin: plugin.get_class_attribute_hook(fullname)) @@ -895,6 +922,6 @@ def get_dynamic_class_hook( def _find_hook(self, lookup: Callable[[Plugin], T]) -> T | None: for plugin in self._plugins: hook = lookup(plugin) - if hook: + if hook is not None: return hook return None diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index b67a285af11da..47c6ad9f305a2 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -3,9 +3,9 @@ from __future__ import annotations from collections import defaultdict +from collections.abc import Iterable, Mapping from functools import reduce -from typing import Final, Iterable, List, Mapping, cast -from typing_extensions import Literal +from typing import Final, Literal, cast import mypy.plugin # To avoid circular imports. from mypy.applytype import apply_generic_arguments @@ -56,7 +56,12 @@ ) 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.typeops import ( + get_type_vars, + make_simplified_union, + map_type_from_supertype, + type_object_type, +) from mypy.types import ( AnyType, CallableType, @@ -453,7 +458,7 @@ def _analyze_class( if isinstance(node, PlaceholderNode): # This node is not ready yet. continue - assert isinstance(node, Var) + assert isinstance(node, Var), node node.is_initialized_in_class = False # Traverse the MRO and collect attributes from the parents. @@ -726,8 +731,6 @@ def _parse_converter( ): converter_type = converter_expr.node.type elif isinstance(converter_expr.node, TypeInfo): - from mypy.checkmember import type_object_type # To avoid import cycle. - converter_type = type_object_type(converter_expr.node, ctx.api.named_type) elif ( isinstance(converter_expr, IndexExpr) @@ -736,8 +739,6 @@ def _parse_converter( and isinstance(converter_expr.base.node, TypeInfo) ): # The converter is a generic type. - from mypy.checkmember import type_object_type # To avoid import cycle. - converter_type = type_object_type(converter_expr.base.node, ctx.api.named_type) if isinstance(converter_type, CallableType): converter_type = apply_generic_arguments( @@ -806,7 +807,7 @@ def _parse_assignments( rvalues: list[Expression] = [] if isinstance(lvalue, (TupleExpr, ListExpr)): if all(isinstance(item, NameExpr) for item in lvalue.items): - lvalues = cast(List[NameExpr], lvalue.items) + lvalues = cast(list[NameExpr], lvalue.items) if isinstance(stmt.rvalue, (TupleExpr, ListExpr)): rvalues = stmt.rvalue.items elif isinstance(lvalue, NameExpr): @@ -1087,7 +1088,7 @@ def _get_expanded_attr_types( return None init_func = expand_type_by_instance(init_func, typ) # [1:] to skip the self argument of AttrClass.__init__ - field_names = cast(List[str], init_func.arg_names[1:]) + field_names = cast(list[str], init_func.arg_names[1:]) field_types = init_func.arg_types[1:] return [dict(zip(field_names, field_types))] else: diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index f0ff6f30a3b94..ed2a91d102f4b 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -164,7 +164,7 @@ def find_shallow_matching_overload_item(overload: Overloaded, call: CallExpr) -> def _get_callee_type(call: CallExpr) -> CallableType | None: - """Return the type of the callee, regardless of its syntatic form.""" + """Return the type of the callee, regardless of its syntactic form.""" callee_node: Node | None = call.callee @@ -282,7 +282,6 @@ def add_overloaded_method_to_class( var = Var(func.name, func.type) var.set_line(func.line) func.is_decorated = True - func.deco_line = func.line deco = Decorator(func, [], var) else: @@ -361,7 +360,7 @@ def _add_method_by_spec( signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_defs: - signature.variables = tvar_defs + signature.variables = tuple(tvar_defs) func = FuncDef(name, args, Block([PassStmt()])) func.info = info diff --git a/mypy/plugins/constants.py b/mypy/plugins/constants.py new file mode 100644 index 0000000000000..9a09e89202de9 --- /dev/null +++ b/mypy/plugins/constants.py @@ -0,0 +1,20 @@ +"""Constant definitions for plugins kept here to help with import cycles.""" + +from typing import Final + +from mypy.semanal_enum import ENUM_BASES + +SINGLEDISPATCH_TYPE: Final = "functools._SingleDispatchCallable" +SINGLEDISPATCH_REGISTER_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.register" +SINGLEDISPATCH_CALLABLE_CALL_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.__call__" +SINGLEDISPATCH_REGISTER_RETURN_CLASS: Final = "_SingleDispatchRegisterCallable" +SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD: Final = ( + f"functools.{SINGLEDISPATCH_REGISTER_RETURN_CLASS}.__call__" +) + +ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | { + f"{prefix}._name_" for prefix in ENUM_BASES +} +ENUM_VALUE_ACCESS: Final = {f"{prefix}.value" for prefix in ENUM_BASES} | { + f"{prefix}._value_" for prefix in ENUM_BASES +} diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index edfc6840fc378..e916ded01dd2a 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Iterator, Literal +from collections.abc import Iterator +from typing import TYPE_CHECKING, Final, Literal from mypy import errorcodes, message_registry from mypy.expandtype import expand_type, expand_type_by_instance @@ -78,6 +79,8 @@ # The set of decorators that generate dataclasses. dataclass_makers: Final = {"dataclass", "dataclasses.dataclass"} +# Default field specifiers for dataclasses +DATACLASS_FIELD_SPECIFIERS: Final = ("dataclasses.Field", "dataclasses.field") SELF_TVAR_NAME: Final = "_DT" @@ -86,7 +89,7 @@ order_default=False, kw_only_default=False, frozen_default=False, - field_specifiers=("dataclasses.Field", "dataclasses.field"), + field_specifiers=DATACLASS_FIELD_SPECIFIERS, ) _INTERNAL_REPLACE_SYM_NAME: Final = "__mypy-replace" _INTERNAL_POST_INIT_SYM_NAME: Final = "__mypy-post_init" @@ -356,12 +359,12 @@ def transform(self) -> bool: if decorator_arguments["frozen"]: if any(not parent["frozen"] for parent in parent_decorator_arguments): - self._api.fail("Cannot inherit frozen dataclass from a non-frozen one", info) + self._api.fail("Frozen dataclass cannot inherit from a non-frozen dataclass", info) self._propertize_callables(attributes, settable=False) self._freeze(attributes) else: if any(parent["frozen"] for parent in parent_decorator_arguments): - self._api.fail("Cannot inherit non-frozen dataclass from a frozen one", info) + self._api.fail("Non-frozen dataclass cannot inherit from a frozen dataclass", info) self._propertize_callables(attributes) if decorator_arguments["slots"]: @@ -378,7 +381,9 @@ def transform(self) -> bool: ): str_type = self._api.named_type("builtins.str") literals: list[Type] = [ - LiteralType(attr.name, str_type) for attr in attributes if attr.is_in_init + LiteralType(attr.name, str_type) + for attr in attributes + if attr.is_in_init and not attr.kw_only ] match_args_type = TupleType(literals, self._api.named_type("builtins.tuple")) add_attribute_to_class(self._api, self._cls, "__match_args__", match_args_type) @@ -400,14 +405,17 @@ def transform(self) -> bool: 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] - type_vars = [tv for tv in self._cls.type_vars] + args = [ + attr.to_argument(self._cls.info, of="replace") + for attr in attributes + if attr.is_in_init + ] add_method_to_class( self._api, self._cls, "__replace__", args=args, - return_type=Instance(self._cls.info, type_vars), + return_type=fill_typevars(self._cls.info), ) def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> None: @@ -444,8 +452,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 @@ -538,7 +545,6 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: # in the parent. We can implement this via a dict without disrupting the attr order # because dicts preserve insertion order in Python 3.7+. found_attrs: dict[str, DataclassAttribute] = {} - found_dataclass_supertype = False for info in reversed(cls.info.mro[1:-1]): if "dataclass_tag" in info.metadata and "dataclass" not in info.metadata: # We haven't processed the base class yet. Need another pass. @@ -548,7 +554,6 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: # Each class depends on the set of attributes in its dataclass ancestors. self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname)) - found_dataclass_supertype = True for data in info.metadata["dataclass"]["attributes"]: name: str = data["name"] @@ -604,7 +609,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: # We will issue an error later. continue - assert isinstance(node, Var) + assert isinstance(node, Var), node # x: ClassVar[int] is ignored by dataclasses. if node.is_classvar: @@ -712,8 +717,7 @@ def collect_attributes(self) -> list[DataclassAttribute] | None: ) all_attrs = list(found_attrs.values()) - if found_dataclass_supertype: - all_attrs.sort(key=lambda a: a.kw_only) + all_attrs.sort(key=lambda a: a.kw_only) # Third, ensure that arguments without a default don't follow # arguments that have a default and that the KW_ONLY sentinel @@ -762,6 +766,8 @@ def _freeze(self, attributes: list[DataclassAttribute]) -> None: if sym_node is not None: var = sym_node.node if isinstance(var, Var): + if var.is_final: + continue # do not turn `Final` attrs to `@property` var.is_property = True else: var = attr.to_var(info) @@ -957,6 +963,9 @@ def dataclass_tag_callback(ctx: ClassDefContext) -> None: def dataclass_class_maker_callback(ctx: ClassDefContext) -> bool: """Hooks into the class typechecking process to add support for dataclasses.""" + if any(i.is_named_tuple for i in ctx.cls.info.mro): + ctx.api.fail("A NamedTuple cannot be a dataclass", ctx=ctx.cls.info) + return True transformer = DataclassTransformer( ctx.cls, ctx.reason, _get_transform_spec(ctx.reason), ctx.api ) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 73c5742614eef..7a58307fc5597 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -5,7 +5,7 @@ import mypy.errorcodes as codes from mypy import message_registry -from mypy.nodes import DictExpr, IntExpr, StrExpr, UnaryExpr +from mypy.nodes import DictExpr, Expression, IntExpr, StrExpr, UnaryExpr from mypy.plugin import ( AttributeContext, ClassDefContext, @@ -15,7 +15,51 @@ MethodSigContext, Plugin, ) +from mypy.plugins.attrs import ( + attr_class_maker_callback, + attr_class_makers, + attr_dataclass_makers, + attr_define_makers, + attr_frozen_makers, + attr_tag_callback, + evolve_function_sig_callback, + fields_function_sig_callback, +) from mypy.plugins.common import try_getting_str_literals +from mypy.plugins.constants import ( + ENUM_NAME_ACCESS, + ENUM_VALUE_ACCESS, + SINGLEDISPATCH_CALLABLE_CALL_METHOD, + SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD, + SINGLEDISPATCH_REGISTER_METHOD, +) +from mypy.plugins.ctypes import ( + array_constructor_callback, + array_getitem_callback, + array_iter_callback, + array_raw_callback, + array_setitem_callback, + array_value_callback, +) +from mypy.plugins.dataclasses import ( + dataclass_class_maker_callback, + dataclass_makers, + dataclass_tag_callback, + replace_function_sig_callback, +) +from mypy.plugins.enums import enum_member_callback, enum_name_callback, enum_value_callback +from mypy.plugins.functools import ( + functools_total_ordering_maker_callback, + functools_total_ordering_makers, + partial_call_callback, + partial_new_callback, +) +from mypy.plugins.singledispatch import ( + call_singledispatch_function_after_register_argument, + call_singledispatch_function_callback, + create_singledispatch_function_callback, + singledispatch_register_callback, +) from mypy.subtypes import is_subtype from mypy.typeops import is_literal_type_like, make_simplified_union from mypy.types import ( @@ -36,70 +80,61 @@ get_proper_types, ) +TD_SETDEFAULT_NAMES: Final = {n + ".setdefault" for n in TPDICT_FB_NAMES} +TD_POP_NAMES: Final = {n + ".pop" for n in TPDICT_FB_NAMES} +TD_DELITEM_NAMES: Final = {n + ".__delitem__" for n in TPDICT_FB_NAMES} + +TD_UPDATE_METHOD_NAMES: Final = ( + {n + ".update" for n in TPDICT_FB_NAMES} + | {n + ".__or__" for n in TPDICT_FB_NAMES} + | {n + ".__ror__" for n in TPDICT_FB_NAMES} + | {n + ".__ior__" for n in TPDICT_FB_NAMES} +) + class DefaultPlugin(Plugin): """Type checker plugin that is enabled by default.""" def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: - from mypy.plugins import ctypes, enums, singledispatch - if fullname == "_ctypes.Array": - return ctypes.array_constructor_callback + return array_constructor_callback elif fullname == "functools.singledispatch": - return singledispatch.create_singledispatch_function_callback + return create_singledispatch_function_callback elif fullname == "functools.partial": - import mypy.plugins.functools - - return mypy.plugins.functools.partial_new_callback + return partial_new_callback elif fullname == "enum.member": - return enums.enum_member_callback - + return enum_member_callback return None def get_function_signature_hook( self, fullname: str ) -> Callable[[FunctionSigContext], FunctionLike] | None: - from mypy.plugins import attrs, dataclasses - if fullname in ("attr.evolve", "attrs.evolve", "attr.assoc", "attrs.assoc"): - return attrs.evolve_function_sig_callback + return evolve_function_sig_callback elif fullname in ("attr.fields", "attrs.fields"): - return attrs.fields_function_sig_callback + return fields_function_sig_callback elif fullname == "dataclasses.replace": - return dataclasses.replace_function_sig_callback + return replace_function_sig_callback return None def get_method_signature_hook( self, fullname: str ) -> Callable[[MethodSigContext], FunctionLike] | None: - from mypy.plugins import ctypes, singledispatch - if fullname == "typing.Mapping.get": return typed_dict_get_signature_callback - elif fullname in {n + ".setdefault" for n in TPDICT_FB_NAMES}: + elif fullname in TD_SETDEFAULT_NAMES: return typed_dict_setdefault_signature_callback - elif fullname in {n + ".pop" for n in TPDICT_FB_NAMES}: + elif fullname in TD_POP_NAMES: return typed_dict_pop_signature_callback elif fullname == "_ctypes.Array.__setitem__": - return ctypes.array_setitem_callback - elif fullname == singledispatch.SINGLEDISPATCH_CALLABLE_CALL_METHOD: - return singledispatch.call_singledispatch_function_callback - - typed_dict_updates = set() - for n in TPDICT_FB_NAMES: - typed_dict_updates.add(n + ".update") - typed_dict_updates.add(n + ".__or__") - typed_dict_updates.add(n + ".__ror__") - typed_dict_updates.add(n + ".__ior__") - - if fullname in typed_dict_updates: + return array_setitem_callback + elif fullname == SINGLEDISPATCH_CALLABLE_CALL_METHOD: + return call_singledispatch_function_callback + elif fullname in TD_UPDATE_METHOD_NAMES: return typed_dict_update_signature_callback - return None def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: - from mypy.plugins import ctypes, singledispatch - if fullname == "typing.Mapping.get": return typed_dict_get_callback elif fullname == "builtins.int.__pow__": @@ -110,81 +145,70 @@ def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | No return int_pos_callback elif fullname in ("builtins.tuple.__mul__", "builtins.tuple.__rmul__"): return tuple_mul_callback - elif fullname in {n + ".setdefault" for n in TPDICT_FB_NAMES}: + elif fullname in TD_SETDEFAULT_NAMES: return typed_dict_setdefault_callback - elif fullname in {n + ".pop" for n in TPDICT_FB_NAMES}: + elif fullname in TD_POP_NAMES: return typed_dict_pop_callback - elif fullname in {n + ".__delitem__" for n in TPDICT_FB_NAMES}: + elif fullname in TD_DELITEM_NAMES: return typed_dict_delitem_callback elif fullname == "_ctypes.Array.__getitem__": - return ctypes.array_getitem_callback + return array_getitem_callback elif fullname == "_ctypes.Array.__iter__": - return ctypes.array_iter_callback - elif fullname == singledispatch.SINGLEDISPATCH_REGISTER_METHOD: - return singledispatch.singledispatch_register_callback - elif fullname == singledispatch.REGISTER_CALLABLE_CALL_METHOD: - return singledispatch.call_singledispatch_function_after_register_argument + return array_iter_callback + elif fullname == SINGLEDISPATCH_REGISTER_METHOD: + return singledispatch_register_callback + elif fullname == SINGLEDISPATCH_REGISTER_CALLABLE_CALL_METHOD: + return call_singledispatch_function_after_register_argument elif fullname == "functools.partial.__call__": - import mypy.plugins.functools - - return mypy.plugins.functools.partial_call_callback + return partial_call_callback return None def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: - from mypy.plugins import ctypes, enums - if fullname == "_ctypes.Array.value": - return ctypes.array_value_callback + return array_value_callback elif fullname == "_ctypes.Array.raw": - return ctypes.array_raw_callback - elif fullname in enums.ENUM_NAME_ACCESS: - return enums.enum_name_callback - elif fullname in enums.ENUM_VALUE_ACCESS: - return enums.enum_value_callback + return array_raw_callback + elif fullname in ENUM_NAME_ACCESS: + return enum_name_callback + elif fullname in ENUM_VALUE_ACCESS: + return enum_value_callback return None def get_class_decorator_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: - from mypy.plugins import attrs, dataclasses - # These dataclass and attrs hooks run in the main semantic analysis pass # and only tag known dataclasses/attrs classes, so that the second # hooks (in get_class_decorator_hook_2) can detect dataclasses/attrs classes # in the MRO. - if fullname in dataclasses.dataclass_makers: - return dataclasses.dataclass_tag_callback + if fullname in dataclass_makers: + return dataclass_tag_callback if ( - fullname in attrs.attr_class_makers - or fullname in attrs.attr_dataclass_makers - or fullname in attrs.attr_frozen_makers - or fullname in attrs.attr_define_makers + fullname in attr_class_makers + or fullname in attr_dataclass_makers + or fullname in attr_frozen_makers + or fullname in attr_define_makers ): - return attrs.attr_tag_callback - + return attr_tag_callback return None def get_class_decorator_hook_2( self, fullname: str ) -> Callable[[ClassDefContext], bool] | None: - import mypy.plugins.functools - from mypy.plugins import attrs, dataclasses - - if fullname in dataclasses.dataclass_makers: - return dataclasses.dataclass_class_maker_callback - elif fullname in mypy.plugins.functools.functools_total_ordering_makers: - return mypy.plugins.functools.functools_total_ordering_maker_callback - elif fullname in attrs.attr_class_makers: - return attrs.attr_class_maker_callback - elif fullname in attrs.attr_dataclass_makers: - return partial(attrs.attr_class_maker_callback, auto_attribs_default=True) - elif fullname in attrs.attr_frozen_makers: + if fullname in dataclass_makers: + return dataclass_class_maker_callback + elif fullname in functools_total_ordering_makers: + return functools_total_ordering_maker_callback + elif fullname in attr_class_makers: + return attr_class_maker_callback + elif fullname in attr_dataclass_makers: + return partial(attr_class_maker_callback, auto_attribs_default=True) + elif fullname in attr_frozen_makers: return partial( - attrs.attr_class_maker_callback, auto_attribs_default=None, frozen_default=True + attr_class_maker_callback, auto_attribs_default=None, frozen_default=True ) - elif fullname in attrs.attr_define_makers: + elif fullname in attr_define_makers: return partial( - attrs.attr_class_maker_callback, auto_attribs_default=None, slots_default=True + attr_class_maker_callback, auto_attribs_default=None, slots_default=True ) - return None @@ -239,30 +263,40 @@ def typed_dict_get_callback(ctx: MethodContext) -> Type: if keys is None: return ctx.default_return_type + default_type: Type + default_arg: Expression | None + if len(ctx.arg_types) <= 1 or not ctx.arg_types[1]: + default_arg = None + default_type = NoneType() + elif len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1: + default_arg = ctx.args[1][0] + default_type = ctx.arg_types[1][0] + else: + return ctx.default_return_type + output_types: list[Type] = [] for key in keys: - value_type = get_proper_type(ctx.type.items.get(key)) + value_type: Type | None = ctx.type.items.get(key) if value_type is None: return ctx.default_return_type - if len(ctx.arg_types) == 1: + if key in ctx.type.required_keys: output_types.append(value_type) - elif len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1: - default_arg = ctx.args[1][0] + else: + # HACK to deal with get(key, {}) if ( isinstance(default_arg, DictExpr) and len(default_arg.items) == 0 - and isinstance(value_type, TypedDictType) + and isinstance(vt := get_proper_type(value_type), TypedDictType) ): - # Special case '{}' as the default for a typed dict type. - output_types.append(value_type.copy_modified(required_keys=set())) + output_types.append(vt.copy_modified(required_keys=set())) else: output_types.append(value_type) - output_types.append(ctx.arg_types[1][0]) - - if len(ctx.arg_types) == 1: - output_types.append(NoneType()) + output_types.append(default_type) + # for nicer reveal_type, put default at the end, if it is present + if default_type in output_types: + output_types = [t for t in output_types if t != default_type] + [default_type] return make_simplified_union(output_types) return ctx.default_return_type @@ -304,25 +338,26 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type: and len(ctx.arg_types) >= 1 and len(ctx.arg_types[0]) == 1 ): - keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) + key_expr = ctx.args[0][0] + keys = try_getting_str_literals(key_expr, ctx.arg_types[0][0]) if keys is None: ctx.api.fail( message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - ctx.context, + key_expr, code=codes.LITERAL_REQ, ) return AnyType(TypeOfAny.from_error) value_types = [] for key in keys: - if key in ctx.type.required_keys: - ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) + 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, key_expr) value_type = ctx.type.items.get(key) if value_type: value_types.append(value_type) else: - ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_not_found(ctx.type, key, key_expr) return AnyType(TypeOfAny.from_error) if len(ctx.args[1]) == 0: @@ -363,27 +398,29 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: and len(ctx.arg_types[0]) == 1 and len(ctx.arg_types[1]) == 1 ): - keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) + key_expr = ctx.args[0][0] + keys = try_getting_str_literals(key_expr, ctx.arg_types[0][0]) if keys is None: ctx.api.fail( message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - ctx.context, + key_expr, code=codes.LITERAL_REQ, ) 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) + ctx.api.msg.readonly_keys_mutated(assigned_readonly_keys, context=key_expr) default_type = ctx.arg_types[1][0] + default_expr = ctx.args[1][0] value_types = [] for key in keys: value_type = ctx.type.items.get(key) if value_type is None: - ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_not_found(ctx.type, key, key_expr) return AnyType(TypeOfAny.from_error) # The signature_callback above can't always infer the right signature @@ -392,7 +429,7 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: # default can be assigned to all key-value pairs we're updating. if not is_subtype(default_type, value_type): ctx.api.msg.typeddict_setdefault_arguments_inconsistent( - default_type, value_type, ctx.context + default_type, value_type, default_expr ) return AnyType(TypeOfAny.from_error) @@ -409,20 +446,21 @@ def typed_dict_delitem_callback(ctx: MethodContext) -> Type: and len(ctx.arg_types) == 1 and len(ctx.arg_types[0]) == 1 ): - keys = try_getting_str_literals(ctx.args[0][0], ctx.arg_types[0][0]) + key_expr = ctx.args[0][0] + keys = try_getting_str_literals(key_expr, ctx.arg_types[0][0]) if keys is None: ctx.api.fail( message_registry.TYPEDDICT_KEY_MUST_BE_STRING_LITERAL, - ctx.context, + key_expr, code=codes.LITERAL_REQ, ) return AnyType(TypeOfAny.from_error) for key in 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) + ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, key_expr) elif key not in ctx.type.items: - ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) + ctx.api.msg.typeddict_key_not_found(ctx.type, key, key_expr) return ctx.default_return_type @@ -550,7 +588,7 @@ def tuple_mul_callback(ctx: MethodContext) -> Type: value = arg_type.last_known_value.value if isinstance(value, int): return ctx.type.copy_modified(items=ctx.type.items * value) - elif isinstance(ctx.type, LiteralType): + elif isinstance(arg_type, LiteralType): value = arg_type.value if isinstance(value, int): return ctx.type.copy_modified(items=ctx.type.items * value) diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 816241fa6e9a6..0be2e083b6ddf 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -13,14 +13,16 @@ from __future__ import annotations -from typing import Final, Iterable, Sequence, TypeVar, cast +from collections.abc import Iterable, Sequence +from typing import TypeVar, cast import mypy.plugin # To avoid circular imports. -from mypy.nodes import TypeInfo -from mypy.semanal_enum import ENUM_BASES +from mypy.checker_shared import TypeCheckerSharedApi +from mypy.nodes import TypeInfo, Var from mypy.subtypes import is_equivalent from mypy.typeops import fixup_partial_type, make_simplified_union from mypy.types import ( + ELLIPSIS_TYPE_NAMES, CallableType, Instance, LiteralType, @@ -30,13 +32,6 @@ is_named_instance, ) -ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | { - f"{prefix}._name_" for prefix in ENUM_BASES -} -ENUM_VALUE_ACCESS: Final = {f"{prefix}.value" for prefix in ENUM_BASES} | { - f"{prefix}._value_" for prefix in ENUM_BASES -} - def enum_name_callback(ctx: mypy.plugin.AttributeContext) -> Type: """This plugin refines the 'name' attribute in enums to act as if @@ -86,6 +81,19 @@ def _infer_value_type_with_auto_fallback( if proper_type is None: return None proper_type = get_proper_type(fixup_partial_type(proper_type)) + # Enums in stubs may have ... instead of actual values. If `_value_` is annotated + # (manually or inherited from IntEnum, for example), it is a more reasonable guess + # than literal ellipsis type. + if ( + _is_defined_in_stub(ctx) + and isinstance(proper_type, Instance) + and proper_type.type.fullname in ELLIPSIS_TYPE_NAMES + and isinstance(ctx.type, Instance) + ): + value_type = ctx.type.type.get("_value_") + if value_type is not None and isinstance(var := value_type.node, Var): + return var.type + return proper_type if not (isinstance(proper_type, Instance) and proper_type.type.fullname == "enum.auto"): if is_named_instance(proper_type, "enum.member") and proper_type.args: return proper_type.args[0] @@ -113,6 +121,11 @@ def _infer_value_type_with_auto_fallback( return ctx.default_attr_type +def _is_defined_in_stub(ctx: mypy.plugin.AttributeContext) -> bool: + assert isinstance(ctx.api, TypeCheckerSharedApi) + return isinstance(ctx.type, Instance) and ctx.api.is_defined_in_stub(ctx.type) + + def _implements_new(info: TypeInfo) -> bool: """Check whether __new__ comes from enum.Enum or was implemented in a subclass. In the latter case, we must infer Any as long as mypy can't infer @@ -129,9 +142,9 @@ def _implements_new(info: TypeInfo) -> bool: def enum_member_callback(ctx: mypy.plugin.FunctionContext) -> Type: - """By default `member(1)` will be infered as `member[int]`, + """By default `member(1)` will be inferred as `member[int]`, we want to improve the inference to be `Literal[1]` here.""" - if ctx.arg_types or ctx.arg_types[0]: + if ctx.arg_types and ctx.arg_types[0]: arg = get_proper_type(ctx.arg_types[0][0]) proper_return = get_proper_type(ctx.default_return_type) if ( diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 6650af637519f..c8b370f15e6d3 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -8,16 +8,31 @@ 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.erasetype import erase_typevars +from mypy.nodes import ( + ARG_POS, + ARG_STAR2, + SYMBOL_FUNCBASE_TYPES, + ArgKind, + Argument, + CallExpr, + 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, ) @@ -103,7 +118,7 @@ def _analyze_class(ctx: mypy.plugin.ClassDefContext) -> dict[str, _MethodInfo | for name in _ORDERING_METHODS: if name in cls.names and name not in comparison_methods: node = cls.names[name].node - if isinstance(node, FuncItem) and isinstance(node.type, CallableType): + if isinstance(node, SYMBOL_FUNCBASE_TYPES) and isinstance(node.type, CallableType): comparison_methods[name] = _MethodInfo(node.is_static, node.type) continue @@ -130,7 +145,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 +178,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 +198,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 +269,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 = [] @@ -234,7 +277,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: for i, actuals in enumerate(formal_to_actual): if len(bound.arg_types) == len(fn_type.arg_types): arg_type = bound.arg_types[i] - if not mypy.checker.is_valid_inferred_type(arg_type): + if not mypy.checker.is_valid_inferred_type(arg_type, ctx.api.options): arg_type = fn_type.arg_types[i] # bit of a hack else: # TODO: I assume that bound and fn_type have the same arguments. It appears this isn't @@ -259,7 +302,7 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: partial_names.append(fn_type.arg_names[i]) ret_type = bound.ret_type - if not mypy.checker.is_valid_inferred_type(ret_type): + if not mypy.checker.is_valid_inferred_type(ret_type, ctx.api.options): ret_type = fn_type.ret_type # same kind of hack as above partially_applied = fn_type.copy_modified( @@ -267,10 +310,23 @@ 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]) + # Do not leak typevars from generic functions - they cannot be usable. + # Keep them in the wrapped callable, but avoid `partial[SomeStrayTypeVar]` + erased_ret_type = erase_typevars(ret_type, [tv.id for tv in fn_type.variables]) + + ret = ctx.api.named_generic_type(PARTIAL, [erased_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 @@ -285,7 +341,8 @@ 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 @@ -303,11 +360,36 @@ def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: 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=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 f51685c80afad..872903ea6b47d 100644 --- a/mypy/plugins/proper_plugin.py +++ b/mypy/plugins/proper_plugin.py @@ -107,6 +107,8 @@ def is_special_target(right: ProperType) -> bool: "mypy.types.DeletedType", "mypy.types.RequiredType", "mypy.types.ReadOnlyType", + "mypy.types.TypeGuardedType", + "mypy.types.PlaceholderType", ): # 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/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index c5ce20233a0a4..eb2bbe133bf09 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Final, NamedTuple, Sequence, TypeVar, Union +from collections.abc import Sequence +from typing import NamedTuple, TypeVar, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.messages import format_type @@ -8,6 +9,7 @@ from mypy.options import Options from mypy.plugin import CheckerPluginInterface, FunctionContext, MethodContext, MethodSigContext from mypy.plugins.common import add_method_to_class +from mypy.plugins.constants import SINGLEDISPATCH_REGISTER_RETURN_CLASS from mypy.subtypes import is_subtype from mypy.types import ( AnyType, @@ -32,13 +34,6 @@ class RegisterCallableInfo(NamedTuple): singledispatch_obj: Instance -SINGLEDISPATCH_TYPE: Final = "functools._SingleDispatchCallable" - -SINGLEDISPATCH_REGISTER_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.register" - -SINGLEDISPATCH_CALLABLE_CALL_METHOD: Final = f"{SINGLEDISPATCH_TYPE}.__call__" - - def get_singledispatch_info(typ: Instance) -> SingledispatchTypeVars | None: if len(typ.args) == 2: return SingledispatchTypeVars(*typ.args) # type: ignore[arg-type] @@ -55,16 +50,11 @@ def get_first_arg(args: list[list[T]]) -> T | None: return None -REGISTER_RETURN_CLASS: Final = "_SingleDispatchRegisterCallable" - -REGISTER_CALLABLE_CALL_METHOD: Final = f"functools.{REGISTER_RETURN_CLASS}.__call__" - - def make_fake_register_class_instance( api: CheckerPluginInterface, type_args: Sequence[Type] ) -> Instance: - defn = ClassDef(REGISTER_RETURN_CLASS, Block([])) - defn.fullname = f"functools.{REGISTER_RETURN_CLASS}" + defn = ClassDef(SINGLEDISPATCH_REGISTER_RETURN_CLASS, Block([])) + defn.fullname = f"functools.{SINGLEDISPATCH_REGISTER_RETURN_CLASS}" info = TypeInfo(SymbolTable(), defn, "functools") obj_type = api.named_generic_type("builtins.object", []).type info.bases = [Instance(obj_type, [])] diff --git a/mypy/pyinfo.py b/mypy/pyinfo.py index ee5307cfaebbd..98350f46363ca 100644 --- a/mypy/pyinfo.py +++ b/mypy/pyinfo.py @@ -2,9 +2,9 @@ """Utilities to find the site and prefix information of a Python executable. -This file MUST remain compatible with all Python 3.8+ versions. Since we cannot make any +This file MUST remain compatible with all Python 3.9+ versions. Since we cannot make any assumptions about the Python being executed, this module should not use *any* dependencies outside -of the standard library found in Python 3.8. This file is run each mypy run, so it should be kept +of the standard library found in Python 3.9. This file is run each mypy run, so it should be kept as fast as possible. """ import sys diff --git a/mypy/reachability.py b/mypy/reachability.py index a25b9dff45813..132c269e96af2 100644 --- a/mypy/reachability.py +++ b/mypy/reachability.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Final, Tuple, TypeVar +from typing import Final, TypeVar from mypy.literals import literal from mypy.nodes import ( @@ -115,31 +115,44 @@ def infer_condition_value(expr: Expression, options: Options) -> int: MYPY_TRUE if true under mypy and false at runtime, MYPY_FALSE if false under mypy and true at runtime, else TRUTH_VALUE_UNKNOWN. """ + if isinstance(expr, UnaryExpr) and expr.op == "not": + positive = infer_condition_value(expr.expr, options) + return inverted_truth_mapping[positive] + pyversion = options.python_version name = "" - negated = False - alias = expr - if isinstance(alias, UnaryExpr): - if alias.op == "not": - expr = alias.expr - negated = True + result = TRUTH_VALUE_UNKNOWN if isinstance(expr, NameExpr): name = expr.name elif isinstance(expr, MemberExpr): name = expr.name - elif isinstance(expr, OpExpr) and expr.op in ("and", "or"): + elif isinstance(expr, OpExpr): + if expr.op not in ("or", "and"): + return TRUTH_VALUE_UNKNOWN + left = infer_condition_value(expr.left, options) - if (left in (ALWAYS_TRUE, MYPY_TRUE) and expr.op == "and") or ( - left in (ALWAYS_FALSE, MYPY_FALSE) and expr.op == "or" - ): - # Either `True and ` or `False or `: the result will - # always be the right-hand-side. - return infer_condition_value(expr.right, options) - else: - # The result will always be the left-hand-side (e.g. ALWAYS_* or - # TRUTH_VALUE_UNKNOWN). - return left + right = infer_condition_value(expr.right, options) + results = {left, right} + if expr.op == "or": + if ALWAYS_TRUE in results: + return ALWAYS_TRUE + elif MYPY_TRUE in results: + return MYPY_TRUE + elif left == right == MYPY_FALSE: + return MYPY_FALSE + elif results <= {ALWAYS_FALSE, MYPY_FALSE}: + return ALWAYS_FALSE + elif expr.op == "and": + if ALWAYS_FALSE in results: + return ALWAYS_FALSE + elif MYPY_FALSE in results: + return MYPY_FALSE + elif left == right == ALWAYS_TRUE: + return ALWAYS_TRUE + elif results <= {ALWAYS_TRUE, MYPY_TRUE}: + return MYPY_TRUE + return TRUTH_VALUE_UNKNOWN else: result = consider_sys_version_info(expr, pyversion) if result == TRUTH_VALUE_UNKNOWN: @@ -155,8 +168,6 @@ def infer_condition_value(expr: Expression, options: Options) -> int: result = ALWAYS_TRUE elif name in options.always_false: result = ALWAYS_FALSE - if negated: - result = inverted_truth_mapping[result] return result @@ -221,7 +232,7 @@ def consider_sys_platform(expr: Expression, platform: str) -> int: Return ALWAYS_TRUE, ALWAYS_FALSE, or TRUTH_VALUE_UNKNOWN. """ # Cases supported: - # - sys.platform == 'posix' + # - sys.platform == 'linux' # - sys.platform != 'win32' # - sys.platform.startswith('win') if isinstance(expr, ComparisonExpr): @@ -254,7 +265,7 @@ def consider_sys_platform(expr: Expression, platform: str) -> int: return TRUTH_VALUE_UNKNOWN -Targ = TypeVar("Targ", int, str, Tuple[int, ...]) +Targ = TypeVar("Targ", int, str, tuple[int, ...]) def fixed_comparison(left: Targ, op: str, right: Targ) -> int: diff --git a/mypy/renaming.py b/mypy/renaming.py index 8db3362059609..dff76b157acc0 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager -from typing import Final, Iterator +from typing import Final from mypy.nodes import ( AssignmentStmt, @@ -151,7 +152,21 @@ def visit_try_stmt(self, stmt: TryStmt) -> None: # type checker which allows them to be always redefined, so no need to # do renaming here. with self.enter_try(): - super().visit_try_stmt(stmt) + stmt.body.accept(self) + + for var, tp, handler in zip(stmt.vars, stmt.types, stmt.handlers): + with self.enter_block(): + # Handle except variable together with its body + if tp is not None: + tp.accept(self) + if var is not None: + self.handle_def(var) + for s in handler.body: + s.accept(self) + if stmt.else_body is not None: + stmt.else_body.accept(self) + if stmt.finally_body is not None: + stmt.finally_body.accept(self) def visit_with_stmt(self, stmt: WithStmt) -> None: for expr in stmt.expr: diff --git a/mypy/report.py b/mypy/report.py index 73942b6c5ae3e..4a0b965077f6a 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -11,8 +11,9 @@ import time import tokenize from abc import ABCMeta, abstractmethod +from collections.abc import Iterator from operator import attrgetter -from typing import Any, Callable, Dict, Final, Iterator, Tuple +from typing import Any, Callable, Final from typing_extensions import TypeAlias as _TypeAlias from urllib.request import pathname2url @@ -43,8 +44,8 @@ ] ) -ReporterClasses: _TypeAlias = Dict[ - str, Tuple[Callable[["Reports", str], "AbstractReporter"], bool] +ReporterClasses: _TypeAlias = dict[ + str, tuple[Callable[["Reports", str], "AbstractReporter"], bool] ] reporter_classes: Final[ReporterClasses] = {} @@ -140,12 +141,9 @@ def should_skip_path(path: str) -> bool: def iterate_python_lines(path: str) -> Iterator[tuple[int, str]]: """Return an iterator over (line number, line text) from a Python file.""" - try: + if not os.path.isdir(path): # can happen with namespace packages with tokenize.open(path) as input_file: yield from enumerate(input_file, 1) - except IsADirectoryError: - # can happen with namespace packages - pass class FuncCounterVisitor(TraverserVisitor): @@ -171,11 +169,10 @@ def on_file( ) -> None: # Count physical lines. This assumes the file's encoding is a # superset of ASCII (or at least uses \n in its line endings). - try: + if not os.path.isdir(tree.path): # can happen with namespace packages with open(tree.path, "rb") as f: physical_lines = len(f.readlines()) - except IsADirectoryError: - # can happen with namespace packages + else: physical_lines = 0 func_counter = FuncCounterVisitor() @@ -423,6 +420,9 @@ def on_file( type_map: dict[Expression, Type], options: Options, ) -> None: + if os.path.isdir(tree.path): # can happen with namespace packages + return + with open(tree.path) as f: tree_source = f.readlines() diff --git a/mypy/scope.py b/mypy/scope.py index 021dd9a7d8a5b..766048c41180d 100644 --- a/mypy/scope.py +++ b/mypy/scope.py @@ -5,13 +5,14 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager, nullcontext -from typing import Iterator, Optional, Tuple +from typing import Optional from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import FuncBase, TypeInfo -SavedScope: _TypeAlias = Tuple[str, Optional[TypeInfo], Optional[FuncBase]] +SavedScope: _TypeAlias = tuple[str, Optional[TypeInfo], Optional[FuncBase]] class Scope: diff --git a/mypy/semanal.py b/mypy/semanal.py index 27abf2c1dc4cc..1035efb29061c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -50,9 +50,11 @@ from __future__ import annotations +import re +from collections.abc import Collection, Iterable, Iterator from contextlib import contextmanager -from typing import Any, Callable, Collection, Final, Iterable, Iterator, List, TypeVar, cast -from typing_extensions import TypeAlias as _TypeAlias, TypeGuard +from typing import Any, Callable, Final, TypeVar, cast +from typing_extensions import TypeAlias as _TypeAlias, TypeGuard, assert_never from mypy import errorcodes as codes, message_registry from mypy.constant_fold import constant_fold_expr @@ -71,7 +73,6 @@ from mypy.nodes import ( ARG_NAMED, ARG_POS, - ARG_STAR, ARG_STAR2, CONTRAVARIANT, COVARIANT, @@ -86,6 +87,7 @@ REVEAL_LOCALS, REVEAL_TYPE, RUNTIME_PROTOCOL_DECOS, + SYMBOL_FUNCBASE_TYPES, TYPE_VAR_KIND, TYPE_VAR_TUPLE_KIND, VARIANCE_NOT_READY, @@ -97,10 +99,12 @@ AwaitExpr, Block, BreakStmt, + BytesExpr, CallExpr, CastExpr, ClassDef, ComparisonExpr, + ComplexExpr, ConditionalExpr, Context, ContinueStmt, @@ -114,6 +118,7 @@ Expression, ExpressionStmt, FakeExpression, + FloatExpr, ForStmt, FuncBase, FuncDef, @@ -126,11 +131,13 @@ ImportBase, ImportFrom, IndexExpr, + IntExpr, LambdaExpr, ListComprehension, ListExpr, Lvalue, MatchStmt, + MaybeTypeExpression, MemberExpr, MypyFile, NamedTupleExpr, @@ -167,6 +174,7 @@ TypeAliasStmt, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeInfo, TypeParam, TypeVarExpr, @@ -179,20 +187,20 @@ YieldExpr, YieldFromExpr, get_member_expr_fullname, - get_nongen_builtins, implicit_module_attrs, is_final_node, type_aliases, type_aliases_source_versions, typing_extensions_aliases, ) -from mypy.options import Options +from mypy.options import TYPE_FORM, Options from mypy.patterns import ( AsPattern, ClassPattern, MappingPattern, OrPattern, SequencePattern, + SingletonPattern, StarredPattern, ValuePattern, ) @@ -241,7 +249,6 @@ find_self_type, fix_instance, has_any_from_unimported_type, - no_subscript_builtin_alias, type_constructors, validate_instance, ) @@ -249,6 +256,8 @@ from mypy.types import ( ASSERT_TYPE_NAMES, DATACLASS_TRANSFORM_NAMES, + DEPRECATED_TYPE_NAMES, + DISJOINT_BASE_DECORATOR_NAMES, FINAL_DECORATOR_NAMES, FINAL_TYPE_NAMES, IMPORTED_REVEAL_TYPE_NAMES, @@ -260,8 +269,10 @@ TPDICT_NAMES, TYPE_ALIAS_NAMES, TYPE_CHECK_ONLY_NAMES, + TYPE_NAMES, TYPE_VAR_LIKE_NAMES, TYPED_NAMEDTUPLE_NAMES, + UNPACK_TYPE_NAMES, AnyType, CallableType, FunctionLike, @@ -287,8 +298,10 @@ UnboundType, UnionType, UnpackType, + flatten_nested_tuples, get_proper_type, get_proper_types, + has_type_vars, is_named_instance, remove_dups, type_vars_as_args, @@ -301,6 +314,13 @@ T = TypeVar("T") +# Whether to print diagnostic information for failed full parses +# in SemanticAnalyzer.try_parse_as_type_expression(). +# +# See also: misc/analyze_typeform_stats.py +DEBUG_TYPE_EXPRESSION_FULL_PARSE_FAILURES: Final = False + + FUTURE_IMPORTS: Final = { "__future__.nested_scopes": "nested_scopes", "__future__.generators": "generators", @@ -332,6 +352,22 @@ Tag: _TypeAlias = int +# Matches two words separated by whitespace, where each word lacks +# any symbols which have special meaning in a type expression. +# +# Any string literal matching this common pattern cannot be a valid +# type expression and can be ignored quickly when attempting to parse a +# string literal as a type expression. +_MULTIPLE_WORDS_NONTYPE_RE = re.compile(r'\s*[^\s.\'"|\[]+\s+[^\s.\'"|\[]') + +# Matches any valid Python identifier, including identifiers with Unicode characters. +# +# [^\d\W] = word character that is not a digit +# \w = word character +# \Z = match end of string; does not allow a trailing \n, unlike $ +_IDENTIFIER_RE = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE) + + class SemanticAnalyzer( NodeVisitor[None], SemanticAnalyzerInterface, SemanticAnalyzerPluginInterface ): @@ -415,6 +451,7 @@ def __init__( incomplete_namespaces: set[str], errors: Errors, plugin: Plugin, + import_map: dict[str, set[str]], ) -> None: """Construct semantic analyzer. @@ -447,6 +484,7 @@ def __init__( self.loop_depth = [0] self.errors = errors self.modules = modules + self.import_map = import_map self.msg = MessageBuilder(errors, modules) self.missing_modules = missing_modules self.missing_names = [set()] @@ -483,6 +521,31 @@ 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 + + self._str_type: Instance | None = None + self._function_type: Instance | None = None + self._object_type: Instance | None = None + + # TypeForm profiling counters + self.type_expression_parse_count: int = 0 # Total try_parse_as_type_expression calls + self.type_expression_full_parse_success_count: int = 0 # Successful full parses + self.type_expression_full_parse_failure_count: int = 0 # Failed full parses + + # Imports of submodules transitively visible from given module. + # This is needed to support patterns like this + # [a.py] + # import b + # import foo + # foo.bar # <- this should work even if bar is not re-exported in foo + # [b.py] + # import foo.bar + self.transitive_submodule_imports: dict[str, set[str]] = {} + # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make them properties @property @@ -510,6 +573,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) # @@ -624,6 +706,13 @@ def refresh_partial( def refresh_top_level(self, file_node: MypyFile) -> None: """Reanalyze a stale module top-level in fine-grained incremental mode.""" + if self.options.allow_redefinition_new and not self.options.local_partial_types: + n = TempNode(AnyType(TypeOfAny.special_form)) + n.line = 1 + n.column = 0 + n.end_line = 1 + n.end_column = 0 + self.fail("--local-partial-types must be enabled if using --allow-redefinition-new", n) self.recurse_into_functions = False self.add_implicit_module_attrs(file_node) for d in file_node.defs: @@ -673,7 +762,15 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None: else: inst = self.named_type_or_none("importlib.machinery.ModuleSpec") if inst is None: - if self.final_iteration: + if ( + self.final_iteration + or self.options.clone_for_module("importlib.machinery").follow_imports + == "skip" + ): + # If we are not allowed to resolve imports from `importlib.machinery`, + # ModuleSpec will not be available at any iteration. + # Use the fallback earlier. + # (see https://github.com/python/mypy/issues/18237) inst = self.named_type_or_none("builtins.object") assert inst is not None, "Cannot find builtins.object" else: @@ -763,6 +860,7 @@ def create_alias(self, tree: MypyFile, target_name: str, alias: str, name: str) alias_node = TypeAlias( target, alias, + tree.fullname, line=-1, column=-1, # there is no context no_args=True, @@ -876,7 +974,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: @@ -948,11 +1047,10 @@ def analyze_func_def(self, defn: FuncDef) -> None: if has_self_type and self.type is not None: info = self.type if info.self_type is not None: - result.variables = [info.self_type] + list(result.variables) + result.variables = (info.self_type,) + result.variables defn.type = result self.add_type_alias_deps(analyzer.aliases_used) self.check_function_signature(defn) - self.check_paramspec_definition(defn) if isinstance(defn, FuncDef): assert isinstance(defn.type, CallableType) defn.type = set_callable_name(defn.type, defn) @@ -1006,21 +1104,21 @@ def remove_unpack_kwargs(self, defn: FuncDef, typ: CallableType) -> CallableType last_type = typ.arg_types[-1] if not isinstance(last_type, UnpackType): return typ - last_type = get_proper_type(last_type.type) - if not isinstance(last_type, TypedDictType): + p_last_type = get_proper_type(last_type.type) + if not isinstance(p_last_type, TypedDictType): self.fail("Unpack item in ** argument must be a TypedDict", last_type) new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)] return typ.copy_modified(arg_types=new_arg_types) - overlap = set(typ.arg_names) & set(last_type.items) + overlap = set(typ.arg_names) & set(p_last_type.items) # It is OK for TypedDict to have a key named 'kwargs'. overlap.discard(typ.arg_names[-1]) if overlap: - overlapped = ", ".join([f'"{name}"' for name in overlap]) + overlapped = ", ".join([f'"{name}"' for name in sorted(filter(None, overlap))]) self.fail(f"Overlap between argument names and ** TypedDict items: {overlapped}", defn) new_arg_types = typ.arg_types[:-1] + [AnyType(TypeOfAny.from_error)] return typ.copy_modified(arg_types=new_arg_types) # OK, everything looks right now, mark the callable type as using unpack. - new_arg_types = typ.arg_types[:-1] + [last_type] + new_arg_types = typ.arg_types[:-1] + [p_last_type] return typ.copy_modified(arg_types=new_arg_types, unpack_kwargs=True) def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type: bool) -> None: @@ -1029,21 +1127,17 @@ def prepare_method_signature(self, func: FuncDef, info: TypeInfo, has_self_type: functype = func.type if func.name == "__new__": func.is_static = True - if not func.is_static or func.name == "__new__": + if func.has_self_or_cls_argument: if func.name in ["__init_subclass__", "__class_getitem__"]: func.is_class = True - if not func.arguments: - self.fail( - 'Method must have at least one argument. Did you forget the "self" argument?', - func, - ) - elif isinstance(functype, CallableType): + if func.arguments and isinstance(functype, CallableType): self_type = get_proper_type(functype.arg_types[0]) if isinstance(self_type, AnyType): if has_self_type: assert self.type is not None and self.type.self_type is not None leading_type: Type = self.type.self_type else: + func.is_trivial_self = True leading_type = fill_typevars(info) if func.is_class or func.name == "__new__": leading_type = self.class_type(leading_type) @@ -1076,21 +1170,7 @@ def is_expected_self_type(self, typ: Type, is_classmethod: bool) -> bool: return self.is_expected_self_type(typ.item, is_classmethod=False) if isinstance(typ, UnboundType): sym = self.lookup_qualified(typ.name, typ, suppress_errors=True) - if ( - sym is not None - and ( - sym.fullname == "typing.Type" - or ( - sym.fullname == "builtins.type" - and ( - self.is_stub_file - or self.is_future_flag_set("annotations") - or self.options.python_version >= (3, 9) - ) - ) - ) - and typ.args - ): + if sym is not None and sym.fullname in TYPE_NAMES and typ.args: return self.is_expected_self_type(typ.args[0], is_classmethod=False) return False if isinstance(typ, TypeVarType): @@ -1212,15 +1292,19 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: with self.overload_item_set(0): first_item.accept(self) + bare_setter_type = None + is_property = False if isinstance(first_item, Decorator) and first_item.func.is_property: + is_property = True # This is a property. first_item.func.is_overload = True - self.analyze_property_with_multi_part_definition(defn) - typ = function_type(first_item.func, self.named_type("builtins.function")) + bare_setter_type = self.analyze_property_with_multi_part_definition(defn) + typ = function_type(first_item.func, self.function_type()) assert isinstance(typ, CallableType) + typ.definition = first_item types = [typ] else: - # This is an a normal overload. Find the item signatures, the + # This is a normal overload. Find the item signatures, the # implementation (if outside a stub), and any missing @overload # decorators. types, impl, non_overload_indexes = self.analyze_overload_sigs_and_impl(defn) @@ -1240,8 +1324,10 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: if types and not any( # If some overload items are decorated with other decorators, then # the overload type will be determined during type checking. - isinstance(it, Decorator) and len(it.decorators) > 1 - for it in defn.items + # Note: bare @property is removed in visit_decorator(). + isinstance(it, Decorator) + and len(it.decorators) > (1 if i > 0 or not is_property else 0) + for i, it in enumerate(defn.items) ): # TODO: should we enforce decorated overloads consistency somehow? # Some existing code uses both styles: @@ -1249,6 +1335,11 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: # * Put decorator everywhere, use "bare" types in overloads. defn.type = Overloaded(types) defn.type.line = defn.line + # In addition, we can set the getter/setter type for valid properties as some + # code paths may either use the above type, or var.type etc. of the first item. + if isinstance(first_item, Decorator) and bare_setter_type: + first_item.var.type = types[0] + first_item.var.setter_type = bare_setter_type if not defn.items: # It was not a real overload after all, but function redefinition. We've @@ -1260,10 +1351,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. @@ -1299,8 +1431,9 @@ def analyze_overload_sigs_and_impl( item.accept(self) # TODO: support decorated overloaded functions properly if isinstance(item, Decorator): - callable = function_type(item.func, self.named_type("builtins.function")) + callable = function_type(item.func, self.function_type()) assert isinstance(callable, CallableType) + callable.definition = item if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators): if i == len(defn.items) - 1 and not self.is_stub_file: # Last item outside a stub is impl @@ -1369,8 +1502,15 @@ def handle_missing_overload_implementation(self, defn: OverloadedFuncDef) -> Non item.func.abstract_status = IS_ABSTRACT else: item.abstract_status = IS_ABSTRACT + elif all( + isinstance(item, Decorator) and item.func.abstract_status == IS_ABSTRACT + for item in defn.items + ): + # Since there is no implementation, it can't be called via super(). + if defn.items: + assert isinstance(defn.items[0], Decorator) + defn.items[0].func.is_trivial_body = True else: - # TODO: also allow omitting an implementation for abstract methods in ABCs? self.fail( "An overloaded function outside a stub file must have an implementation", defn, @@ -1427,39 +1567,76 @@ def process_static_or_class_method_in_overload(self, defn: OverloadedFuncDef) -> defn.is_class = class_status[0] defn.is_static = static_status[0] - def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) -> None: + def analyze_property_with_multi_part_definition( + self, defn: OverloadedFuncDef + ) -> CallableType | None: """Analyze a property defined using multiple methods (e.g., using @x.setter). Assume that the first method (@property) has already been analyzed. + Return bare setter type (without any other decorators applied), this may be used + by the caller for performance optimizations. """ defn.is_property = True items = defn.items first_item = defn.items[0] assert isinstance(first_item, Decorator) deleted_items = [] + bare_setter_type = None + func_name = first_item.func.name 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": + item.func.accept(self) + if item.decorators: + first_node = item.decorators[0] + if self._is_valid_property_decorator(first_node, func_name): + # Get abstractness from the original definition. + item.func.abstract_status = first_item.func.abstract_status + 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": - item.func.abstract_status = first_item.func.abstract_status + setter_func_type = function_type(item.func, self.function_type()) + assert isinstance(setter_func_type, CallableType) + bare_setter_type = setter_func_type + defn.setter_index = i + 1 + 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 + f'Only supported top decorators are "@{func_name}.setter" and "@{func_name}.deleter"', + first_node, ) - item.func.accept(self) else: - self.fail(f'Unexpected definition for property "{first_item.func.name}"', item) + self.fail(f'Unexpected definition for property "{func_name}"', item) deleted_items.append(i + 1) 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}" + ) + return bare_setter_type + + def _is_valid_property_decorator( + self, deco: Expression, property_name: str + ) -> TypeGuard[MemberExpr]: + if not isinstance(deco, MemberExpr): + return False + if not isinstance(deco.expr, NameExpr) or deco.expr.name != property_name: + return False + if deco.name not in {"setter", "deleter"}: + # This intentionally excludes getter. While `@prop.getter` is valid at + # runtime, that would mean replacing the already processed getter type. + # Such usage is almost definitely a mistake (except for overrides in + # subclasses but we don't support them anyway) and might be a typo + # (only one letter away from `setter`), it's likely almost never used, + # so supporting it properly won't pay off. + return False + return True + def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> None: if self.is_class_scope(): assert self.type is not None @@ -1495,7 +1672,7 @@ def analyze_function_body(self, defn: FuncItem) -> None: # The first argument of a non-static, non-class method is like 'self' # (though the name could be different), having the enclosing class's # instance type. - if is_method and (not defn.is_static or defn.name == "__new__") and defn.arguments: + if is_method and defn.has_self_or_cls_argument and defn.arguments: if not defn.is_class: defn.arguments[0].variable.is_self = True else: @@ -1530,64 +1707,6 @@ def check_function_signature(self, fdef: FuncItem) -> None: elif len(sig.arg_types) > len(fdef.arguments): self.fail("Type signature has too many arguments", fdef, blocker=True) - def check_paramspec_definition(self, defn: FuncDef) -> None: - func = defn.type - assert isinstance(func, CallableType) - - if not any(isinstance(var, ParamSpecType) for var in func.variables): - return # Function does not have param spec variables - - args = func.var_arg() - kwargs = func.kw_arg() - if args is None and kwargs is None: - return # Looks like this function does not have starred args - - args_defn_type = None - kwargs_defn_type = None - for arg_def, arg_kind in zip(defn.arguments, defn.arg_kinds): - if arg_kind == ARG_STAR: - args_defn_type = arg_def.type_annotation - elif arg_kind == ARG_STAR2: - kwargs_defn_type = arg_def.type_annotation - - # This may happen on invalid `ParamSpec` args / kwargs definition, - # type analyzer sets types of arguments to `Any`, but keeps - # definition types as `UnboundType` for now. - if not ( - (isinstance(args_defn_type, UnboundType) and args_defn_type.name.endswith(".args")) - or ( - isinstance(kwargs_defn_type, UnboundType) - and kwargs_defn_type.name.endswith(".kwargs") - ) - ): - # Looks like both `*args` and `**kwargs` are not `ParamSpec` - # It might be something else, skipping. - return - - args_type = args.typ if args is not None else None - kwargs_type = kwargs.typ if kwargs is not None else None - - if ( - not isinstance(args_type, ParamSpecType) - or not isinstance(kwargs_type, ParamSpecType) - or args_type.name != kwargs_type.name - ): - if isinstance(args_defn_type, UnboundType) and args_defn_type.name.endswith(".args"): - param_name = args_defn_type.name.split(".")[0] - elif isinstance(kwargs_defn_type, UnboundType) and kwargs_defn_type.name.endswith( - ".kwargs" - ): - param_name = kwargs_defn_type.name.split(".")[0] - else: - # Fallback for cases that probably should not ever happen: - param_name = "P" - - self.fail( - f'ParamSpec must have "*args" typed as "{param_name}.args" and "**kwargs" typed as "{param_name}.kwargs"', - func, - code=codes.VALID_TYPE, - ) - def visit_decorator(self, dec: Decorator) -> None: self.statement = dec # TODO: better don't modify them at all. @@ -1632,6 +1751,7 @@ def visit_decorator(self, dec: Decorator) -> None: "abc.abstractproperty", "functools.cached_property", "enum.property", + "types.DynamicClassAttribute", ), ): removed.append(i) @@ -1663,6 +1783,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. @@ -1720,11 +1842,12 @@ def push_type_args( return None tvs.append((p.name, tv)) - for name, tv in tvs: - if self.is_defined_type_param(name): - self.fail(f'"{name}" already defined as a type parameter', context) + if self.is_defined_type_param(p.name): + self.fail(f'"{p.name}" already defined as a type parameter', context) else: - self.add_symbol(name, tv, context, no_progress=True, type_param=True) + assert self.add_symbol( + p.name, tv, context, no_progress=True, type_param=True + ), "Type parameter should not be discarded" return tvs @@ -1754,15 +1877,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, @@ -2097,8 +2243,12 @@ def analyze_class_decorator_common( """ if refers_to_fullname(decorator, FINAL_DECORATOR_NAMES): info.is_final = True + elif refers_to_fullname(decorator, DISJOINT_BASE_DECORATOR_NAMES): + info.is_disjoint_base = 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 @@ -2187,21 +2337,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: @@ -2247,7 +2383,7 @@ def analyze_unbound_tvar(self, t: Type) -> tuple[str, TypeVarLikeExpr] | None: return self.analyze_unbound_tvar_impl(t.type, is_unpacked=True) if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) - if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + if sym and sym.fullname in UNPACK_TYPE_NAMES: inner_t = t.args[0] if isinstance(inner_t, UnboundType): return self.analyze_unbound_tvar_impl(inner_t, is_unpacked=True) @@ -2302,6 +2438,34 @@ 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) + ) + # PEP-695 type variables that are redeclared in an inner scope are warned + # about elsewhere. + if not tvar_expr.is_new_style and not self.tvar_scope.allow_binding( + tvar_expr.fullname + ): + self.fail( + message_registry.TYPE_VAR_REDECLARED_IN_NESTED_CLASS.format(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. @@ -2555,8 +2719,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) @@ -2612,7 +2775,7 @@ def infer_metaclass_and_bases_from_compat_helpers(self, defn: ClassDef) -> None: if len(metas) == 0: return if len(metas) > 1: - self.fail("Multiple metaclass definitions", defn) + self.fail("Multiple metaclass definitions", defn, code=codes.METACLASS) return defn.metaclass = metas.pop() @@ -2668,7 +2831,11 @@ def get_declared_metaclass( elif isinstance(metaclass_expr, MemberExpr): metaclass_name = get_member_expr_fullname(metaclass_expr) if metaclass_name is None: - self.fail(f'Dynamic metaclass not supported for "{name}"', metaclass_expr) + self.fail( + f'Dynamic metaclass not supported for "{name}"', + metaclass_expr, + code=codes.METACLASS, + ) return None, False, True sym = self.lookup_qualified(metaclass_name, metaclass_expr) if sym is None: @@ -2679,28 +2846,33 @@ def get_declared_metaclass( self.fail( f'Class cannot use "{sym.node.name}" as a metaclass (has type "Any")', metaclass_expr, + code=codes.METACLASS, ) return None, False, True if isinstance(sym.node, PlaceholderNode): return None, True, False # defer later in the caller # Support type aliases, like `_Meta: TypeAlias = type` + metaclass_info: Node | None = sym.node if ( isinstance(sym.node, TypeAlias) - and sym.node.no_args - and isinstance(sym.node.target, ProperType) - and isinstance(sym.node.target, Instance) + and not sym.node.python_3_12_type_alias + and not sym.node.alias_tvars ): - metaclass_info: Node | None = sym.node.target.type - else: - metaclass_info = sym.node + target = get_proper_type(sym.node.target) + if isinstance(target, Instance): + metaclass_info = target.type if not isinstance(metaclass_info, TypeInfo) or metaclass_info.tuple_type is not None: - self.fail(f'Invalid metaclass "{metaclass_name}"', metaclass_expr) + self.fail( + f'Invalid metaclass "{metaclass_name}"', metaclass_expr, code=codes.METACLASS + ) return None, False, False if not metaclass_info.is_metaclass(): self.fail( - 'Metaclasses not inheriting from "type" are not supported', metaclass_expr + 'Metaclasses not inheriting from "type" are not supported', + metaclass_expr, + code=codes.METACLASS, ) return None, False, False inst = fill_typevars(metaclass_info) @@ -2933,7 +3105,9 @@ def report_missing_module_attribute( message = ( f'Module "{import_id}" does not explicitly export attribute "{source_id}"' ) - else: + elif not ( + self.options.ignore_errors or self.cur_mod_node.path in self.errors.ignored_files + ): alternatives = set(module.names.keys()).difference({source_id}) matches = best_matches(source_id, alternatives, n=3) if matches: @@ -3021,10 +3195,12 @@ def visit_import_all(self, i: ImportAll) -> None: # namespace is incomplete. self.mark_incomplete("*", i) for name, node in m.names.items(): + if node.no_serialize: + # This is either internal or generated symbol, skip it to avoid problems + # like accidental name conflicts or invalid cross-references. + continue fullname = i_id + "." + name self.set_future_import_flags(fullname) - if node is None: - continue # if '__all__' exists, all nodes not included have had module_public set to # False, and we can skip checking '_' because it's been explicitly included. if node.module_public and (not name.startswith("_") or "__all__" in m.names): @@ -3151,6 +3327,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.store_final_status(s) self.check_classvar(s) self.process_type_annotation(s) + self.analyze_rvalue_as_type_form(s) self.apply_dynamic_class_hook(s) if not s.type: self.process_module_assignment(s.lvalues, s.rvalue, s) @@ -3378,8 +3555,9 @@ def record_special_form_lvalue(self, s: AssignmentStmt) -> None: def analyze_enum_assign(self, s: AssignmentStmt) -> bool: """Check if s defines an Enum.""" if isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.analyzed, EnumCallExpr): - # Already analyzed enum -- nothing to do here. - return True + # This is an analyzed enum definition. + # It is valid iff it can be stored correctly, failures were already reported. + return self._is_single_name_assignment(s) return self.enum_call_analyzer.process_enum_call(s, self.is_func_scope()) def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: @@ -3388,7 +3566,9 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: if s.rvalue.analyzed.info.tuple_type and not has_placeholder( s.rvalue.analyzed.info.tuple_type ): - return True # This is a valid and analyzed named tuple definition, nothing to do here. + # This is an analyzed named tuple definition. + # It is valid iff it can be stored correctly, failures were already reported. + return self._is_single_name_assignment(s) if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], (NameExpr, MemberExpr)): return False lvalue = s.lvalues[0] @@ -3429,8 +3609,9 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: if s.rvalue.analyzed.info.typeddict_type and not has_placeholder( s.rvalue.analyzed.info.typeddict_type ): - # This is a valid and analyzed typed dict definition, nothing to do here. - return True + # This is an analyzed typed dict definition. + # It is valid iff it can be stored correctly, failures were already reported. + return self._is_single_name_assignment(s) if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], (NameExpr, MemberExpr)): return False lvalue = s.lvalues[0] @@ -3454,6 +3635,9 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: self.setup_alias_type_vars(defn) return True + def _is_single_name_assignment(self, s: AssignmentStmt) -> bool: + return len(s.lvalues) == 1 and isinstance(s.lvalues[0], NameExpr) + 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 @@ -3479,6 +3663,10 @@ def analyze_lvalues(self, s: AssignmentStmt) -> None: has_explicit_value=has_explicit_value, ) + def analyze_rvalue_as_type_form(self, s: AssignmentStmt) -> None: + if TYPE_FORM in self.options.enable_incomplete_feature: + self.try_parse_as_type_expression(s.rvalue) + def apply_dynamic_class_hook(self, s: AssignmentStmt) -> None: if not isinstance(s.rvalue, CallExpr): return @@ -3534,13 +3722,22 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: invalid_bare_final = False if not s.unanalyzed_type.args: s.type = None - if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + if ( + isinstance(s.rvalue, TempNode) + and s.rvalue.no_rhs + # Filter duplicate errors, we already reported this: + and not (self.type and self.type.is_named_tuple) + ): invalid_bare_final = True self.fail("Type in Final[...] can only be omitted if there is an initializer", s) else: s.type = s.unanalyzed_type.args[0] - if s.type is not None and self.is_classvar(s.type): + if ( + s.type is not None + and self.options.python_version < (3, 13) + and self.is_classvar(s.type) + ): self.fail("Variable should not be annotated with both ClassVar and Final", s) return False @@ -3641,9 +3838,9 @@ def store_final_status(self, s: AssignmentStmt) -> None: cur_node = self.type.names.get(lval.name, None) if cur_node and isinstance(cur_node.node, Var) and cur_node.node.is_final: assert self.function_stack - top_function = self.function_stack[-1] + current_function = self.function_stack[-1] if ( - top_function.name == "__init__" + current_function.name == "__init__" and cur_node.node.final_unset_in_class and not cur_node.node.final_set_in_init and not (isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs) @@ -3750,16 +3947,15 @@ def analyze_alias( declared_type_vars: TypeVarLikeList | None = None, all_declared_type_params_names: list[str] | None = None, python_3_12_type_alias: bool = False, - ) -> tuple[Type | None, list[TypeVarLikeType], set[str], list[str], bool]: + ) -> tuple[Type | None, list[TypeVarLikeType], set[str], bool]: """Check if 'rvalue' is a valid type allowed for aliasing (e.g. not a type variable). - If yes, return the corresponding type, a list of - qualified type variable names for generic aliases, a set of names the alias depends on, - and a list of type variables if the alias is generic. - A schematic example for the dependencies: + If yes, return the corresponding type, a list of type variables for generic aliases, + a set of names the alias depends on, and True if the original type has empty tuple index. + An example for the dependencies: A = int B = str - analyze_alias(Dict[A, B])[2] == {'__main__.A', '__main__.B'} + analyze_alias(dict[A, B])[2] == {'__main__.A', '__main__.B'} """ dynamic = bool(self.function_stack and self.function_stack[-1].is_dynamic()) global_scope = not self.type and not self.function_stack @@ -3771,27 +3967,13 @@ def analyze_alias( self.fail( "Invalid type alias: expression is not a valid type", rvalue, code=codes.VALID_TYPE ) - return None, [], set(), [], False + return None, [], set(), False found_type_vars = self.find_type_var_likes(typ) - 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(): @@ -3803,6 +3985,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, @@ -3822,9 +4005,8 @@ def analyze_alias( variadic = True new_tvar_defs.append(td) - qualified_tvars = [node.fullname for _name, node in alias_type_vars] empty_tuple_index = typ.empty_tuple_index if isinstance(typ, UnboundType) else False - return analyzed, new_tvar_defs, depends_on, qualified_tvars, empty_tuple_index + return analyzed, new_tvar_defs, depends_on, empty_tuple_index def is_pep_613(self, s: AssignmentStmt) -> bool: if s.unanalyzed_type is not None and isinstance(s.unanalyzed_type, UnboundType): @@ -3889,7 +4071,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: if isinstance(existing.node, TypeAlias) and not s.is_alias_def: self.fail( 'Cannot assign multiple types to name "{}"' - ' without an explicit "Type[...]" annotation'.format(lvalue.name), + ' without an explicit "type[...]" annotation'.format(lvalue.name), lvalue, ) return False @@ -3919,11 +4101,10 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: res = NoneType() alias_tvars: list[TypeVarLikeType] = [] depends_on: set[str] = set() - qualified_tvars: list[str] = [] empty_tuple_index = False else: tag = self.track_incomplete_refs() - res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( + res, alias_tvars, depends_on, empty_tuple_index = self.analyze_alias( lvalue.name, rvalue, allow_placeholder=True, @@ -3947,19 +4128,15 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: self.mark_incomplete(lvalue.name, rvalue, becomes_typeinfo=True) return True self.add_type_alias_deps(depends_on) - # In addition to the aliases used, we add deps on unbound - # type variables, since they are erased from target type. - self.add_type_alias_deps(qualified_tvars) - # The above are only direct deps on other aliases. - # For subscripted aliases, type deps from expansion are added in deps.py - # (because the type is stored). check_for_explicit_any(res, self.options, self.is_typeshed_stub_file, self.msg, context=s) # 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) + # 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. @@ -3981,6 +4158,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: alias_node = TypeAlias( res, self.qualified_name(lvalue.name), + self.cur_mod_id, s.line, s.column, alias_tvars=alias_tvars, @@ -4013,6 +4191,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 @@ -4048,15 +4228,19 @@ def check_type_alias_type_call(self, rvalue: Expression, *, name: str) -> TypeGu names.append("typing.TypeAliasType") if not refers_to_fullname(rvalue.callee, tuple(names)): return False + if not self.check_typevarlike_name(rvalue, name, rvalue): + return False + if rvalue.arg_kinds.count(ARG_POS) != 2: + return False - return self.check_typevarlike_name(rvalue, name, rvalue) + return True def analyze_type_alias_type_params( self, rvalue: CallExpr ) -> tuple[TypeVarLikeList, list[str]]: """Analyze type_params of TypeAliasType. - Returns declared unbound type variable expressions and a list of all decalred type + Returns declared unbound type variable expressions and a list of all declared type variable names for error reporting. """ if "type_params" in rvalue.arg_names: @@ -4108,7 +4292,7 @@ def analyze_type_alias_type_params( base, code=codes.TYPE_VAR, ) - if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"): + if sym and sym.fullname in UNPACK_TYPE_NAMES: self.note( "Don't Unpack type variables in type_params", base, code=codes.TYPE_VAR ) @@ -4130,7 +4314,16 @@ def disable_invalid_recursive_aliases( ) -> None: """Prohibit and fix recursive type aliases that are invalid/unsupported.""" messages = [] - if is_invalid_recursive_alias({current_node}, current_node.target): + if ( + isinstance(current_node.target, TypeAliasType) + and current_node.target.alias is current_node + ): + # We want to have consistent error messages, but not calling name_not_defined(), + # since it will do a bunch of unrelated things we don't want here. + messages.append( + f'Cannot resolve name "{current_node.name}" (possible cyclic definition)' + ) + elif is_invalid_recursive_alias({current_node}, current_node.target): target = ( "tuple" if isinstance(get_proper_type(current_node.target), TupleType) else "union" ) @@ -4153,6 +4346,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. @@ -4163,6 +4357,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" @@ -4173,6 +4368,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) @@ -4199,6 +4395,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. @@ -4235,9 +4432,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: @@ -4250,8 +4456,10 @@ def analyze_name_lvalue( else: lvalue.fullname = lvalue.name if self.is_func_scope(): - if unmangle(name) == "_": + if unmangle(name) == "_" and not self.options.allow_redefinition_new: # Special case for assignment to local named '_': always infer 'Any'. + # This isn't needed with --allow-redefinition-new, since arbitrary + # types can be assigned to '_' anyway. typ = AnyType(TypeOfAny.special_form) self.store_declared_types(lvalue, typ) if is_final and self.is_final_redefinition(kind, name): @@ -4289,7 +4497,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 @@ -4304,10 +4517,11 @@ def make_name_lvalue_var( if kind != LDEF: v._fullname = self.qualified_name(name) else: - # fullanme should never stay None + # fullname should never stay None 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( @@ -4419,6 +4633,9 @@ def analyze_member_lvalue( lval.node = v # TODO: should we also set lval.kind = MDEF? self.type.names[lval.name] = SymbolTableNode(MDEF, v, implicit=True) + for func in self.scope.functions: + if isinstance(func, FuncDef): + func.has_self_attr_def = True self.check_lvalue_validity(lval.node, lval) def is_self_member_ref(self, memberexpr: MemberExpr) -> bool: @@ -4555,6 +4772,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) @@ -4688,7 +4939,7 @@ def get_typevarlike_argument( ) if analyzed is None: # Type variables are special: we need to place them in the symbol table - # soon, even if upper bound is not ready yet. Otherwise avoiding + # soon, even if upper bound is not ready yet. Otherwise, avoiding # a "deadlock" in this common pattern would be tricky: # T = TypeVar('T', bound=Custom[Any]) # class Custom(Generic[T]): @@ -4762,23 +5013,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. @@ -4839,12 +5074,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) @@ -4913,7 +5143,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)) @@ -4926,20 +5160,13 @@ def check_classvar(self, s: AssignmentStmt) -> None: return if not s.type or not self.is_classvar(s.type): return + assert isinstance(s.type, UnboundType) if self.is_class_scope() and isinstance(lvalue, NameExpr): node = lvalue.node if isinstance(node, Var): node.is_classvar = True analyzed = self.anal_type(s.type) assert self.type is not None - if analyzed is not None and set(get_type_vars(analyzed)) & set( - self.type.defn.type_vars - ): - # This means that we have a type var defined inside of a ClassVar. - # This is not allowed by PEP526. - # See https://github.com/python/mypy/issues/11538 - - self.fail(message_registry.CLASS_VAR_WITH_TYPEVARS, s) if ( analyzed is not None and self.type.self_type in get_type_vars(analyzed) @@ -4950,6 +5177,12 @@ def check_classvar(self, s: AssignmentStmt) -> None: # In case of member access, report error only when assigning to self # Other kinds of member assignments should be already reported self.fail_invalid_classvar(lvalue) + if not s.type.args: + if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + if self.options.disallow_any_generics: + self.fail("ClassVar without type argument becomes Any", s, code=codes.TYPE_ARG) + return + s.type = None def is_classvar(self, typ: Type) -> bool: if not isinstance(typ, UnboundType): @@ -4989,7 +5222,7 @@ def process_module_assignment( # with unpacking assignment like `x, y = a, b`. Mypy didn't # understand our all(isinstance(...)), so cast them as TupleExpr # so mypy knows it is safe to access their .items attribute. - seq_lvals = cast(List[TupleExpr], lvals) + seq_lvals = cast(list[TupleExpr], lvals) # given an assignment like: # (x, y) = (m, n) = (a, b) # we now have: @@ -5156,11 +5389,17 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> None: s.expr.accept(self) def visit_return_stmt(self, s: ReturnStmt) -> None: + old = self.statement 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) + if TYPE_FORM in self.options.enable_incomplete_feature: + self.try_parse_as_type_expression(s.expr) + self.statement = old def visit_raise_stmt(self, s: RaiseStmt) -> None: self.statement = s @@ -5192,7 +5431,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) @@ -5205,7 +5445,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) @@ -5216,20 +5456,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 @@ -5250,7 +5494,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: @@ -5387,7 +5632,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: return tag = self.track_incomplete_refs() - res, alias_tvars, depends_on, qualified_tvars, empty_tuple_index = self.analyze_alias( + res, alias_tvars, depends_on, empty_tuple_index = self.analyze_alias( s.name.name, s.value.expr(), allow_placeholder=True, @@ -5414,13 +5659,15 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: self.mark_incomplete(s.name.name, s.value, becomes_typeinfo=True) return + # Now go through all new variables and temporary replace all tvars that still + # refer to some placeholders. We defer the whole alias and will revisit it again, + # as well as all its dependents. + for i, tv in enumerate(alias_tvars): + if has_placeholder(tv): + self.mark_incomplete(s.name.name, s.value, becomes_typeinfo=True) + alias_tvars[i] = self._trivial_typevarlike_like(tv) + self.add_type_alias_deps(depends_on) - # In addition to the aliases used, we add deps on unbound - # type variables, since they are erased from target type. - self.add_type_alias_deps(qualified_tvars) - # The above are only direct deps on other aliases. - # For subscripted aliases, type deps from expansion are added in deps.py - # (because the type is stored). check_for_explicit_any( res, self.options, self.is_typeshed_stub_file, self.msg, context=s ) @@ -5431,11 +5678,12 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: 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: + if isinstance(res, ProperType) and isinstance(res, Instance): fix_instance(res, self.fail, self.note, disallow_any=False, options=self.options) alias_node = TypeAlias( res, self.qualified_name(s.name.name), + self.cur_mod_id, s.line, s.column, alias_tvars=alias_tvars, @@ -5443,6 +5691,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 @@ -5451,12 +5700,17 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: ): updated = False if isinstance(existing.node, TypeAlias): - if existing.node.target != res: + if ( + existing.node.target != res + or existing.node.alias_tvars != alias_node.alias_tvars + ): # Copy expansion to the existing alias, this matches how we update base classes # for a TypeInfo _in place_ if there are nested placeholders. existing.node.target = res existing.node.alias_tvars = alias_tvars 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 @@ -5479,6 +5733,46 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: finally: self.pop_type_args(s.type_args) + def _trivial_typevarlike_like(self, tv: TypeVarLikeType) -> TypeVarLikeType: + object_type = self.named_type("builtins.object") + if isinstance(tv, TypeVarType): + return TypeVarType( + tv.name, + tv.fullname, + tv.id, + values=[], + upper_bound=object_type, + default=AnyType(TypeOfAny.from_omitted_generics), + variance=tv.variance, + line=tv.line, + column=tv.column, + ) + elif isinstance(tv, TypeVarTupleType): + tuple_type = self.named_type("builtins.tuple", [object_type]) + return TypeVarTupleType( + tv.name, + tv.fullname, + tv.id, + upper_bound=tuple_type, + tuple_fallback=tuple_type, + default=AnyType(TypeOfAny.from_omitted_generics), + line=tv.line, + column=tv.column, + ) + elif isinstance(tv, ParamSpecType): + return ParamSpecType( + tv.name, + tv.fullname, + tv.id, + flavor=tv.flavor, + upper_bound=object_type, + default=AnyType(TypeOfAny.from_omitted_generics), + line=tv.line, + column=tv.column, + ) + else: + assert False, f"Unknown TypeVarLike: {tv!r}" + # # Expressions # @@ -5601,7 +5895,7 @@ def visit_call_expr(self, expr: CallExpr) -> None: reveal_type_node = self.lookup("reveal_type", expr, suppress_errors=True) if ( reveal_type_node - and isinstance(reveal_type_node.node, FuncBase) + and isinstance(reveal_type_node.node, SYMBOL_FUNCBASE_TYPES) and reveal_type_node.fullname in IMPORTED_REVEAL_TYPE_NAMES ): reveal_imported = True @@ -5660,7 +5954,11 @@ def visit_call_expr(self, expr: CallExpr) -> None: expr.analyzed = PromoteExpr(target) expr.analyzed.line = expr.line expr.analyzed.accept(self) - elif refers_to_fullname(expr.callee, "builtins.dict"): + elif refers_to_fullname(expr.callee, "builtins.dict") and not ( + isinstance(expr.callee, RefExpr) + and isinstance(expr.callee.node, TypeAlias) + and not expr.callee.node.no_args + ): expr.analyzed = self.translate_dict_call(expr) elif refers_to_fullname(expr.callee, "builtins.divmod"): if not self.check_fixed_args(expr, 2, "divmod"): @@ -5674,10 +5972,31 @@ def visit_call_expr(self, expr: CallExpr) -> None: with self.allow_unbound_tvars_set(): for a in expr.args: a.accept(self) + elif refers_to_fullname(expr.callee, ("typing.TypeForm", "typing_extensions.TypeForm")): + # Special form TypeForm(...). + if not self.check_fixed_args(expr, 1, "TypeForm"): + return + # Translate first argument to an unanalyzed type. + try: + typ = self.expr_to_unanalyzed_type(expr.args[0]) + except TypeTranslationError: + self.fail("TypeForm argument is not a type", expr) + # Suppress future error: "" not callable + expr.analyzed = CastExpr(expr.args[0], AnyType(TypeOfAny.from_error)) + return + # Piggyback TypeFormExpr object to the CallExpr object; it takes + # precedence over the CallExpr semantics. + expr.analyzed = TypeFormExpr(typ) + expr.analyzed.line = expr.line + expr.analyzed.column = expr.column + expr.analyzed.accept(self) else: # Normal call expression. + calculate_type_forms = TYPE_FORM in self.options.enable_incomplete_feature for a in expr.args: a.accept(self) + if calculate_type_forms: + self.try_parse_as_type_expression(a) if ( isinstance(expr.callee, MemberExpr) @@ -5747,6 +6066,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None: if isinstance(sym.node, PlaceholderNode): self.process_placeholder(expr.name, "attribute", expr) return + self.record_imported_symbol(sym) expr.kind = sym.kind expr.fullname = sym.fullname or "" expr.node = sym.node @@ -5777,8 +6097,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None: if type_info: n = type_info.names.get(expr.name) if n is not None and isinstance(n.node, (MypyFile, TypeInfo, TypeAlias)): - if not n: - return + self.record_imported_symbol(n) expr.kind = n.kind expr.fullname = n.fullname or "" expr.node = n.node @@ -5834,30 +6153,6 @@ def analyze_type_application(self, expr: IndexExpr) -> None: expr.analyzed = TypeApplication(base, types) expr.analyzed.line = expr.line expr.analyzed.column = expr.column - # Types list, dict, set are not subscriptable, prohibit this if - # subscripted either via type alias... - if isinstance(base, RefExpr) and isinstance(base.node, TypeAlias): - alias = base.node - target = get_proper_type(alias.target) - if isinstance(target, Instance): - name = target.type.fullname - if ( - alias.no_args - and name # this avoids bogus errors for already reported aliases - in get_nongen_builtins(self.options.python_version) - and not self.is_stub_file - and not alias.normalized - ): - self.fail(no_subscript_builtin_alias(name, propose_alt=False), expr) - # ...or directly. - else: - n = self.lookup_type_node(base) - if ( - n - and n.fullname in get_nongen_builtins(self.options.python_version) - and not self.is_stub_file - ): - self.fail(no_subscript_builtin_alias(n.fullname, propose_alt=False), expr) def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: """Analyze type arguments (index) in a type application. @@ -5924,6 +6219,10 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: return None types.append(analyzed) + if allow_unpack: + # need to flatten away harmless unpacks like Unpack[tuple[int]] + flattened_items = flatten_nested_tuples(types) + types = self.type_analyzer().check_unpacks_in_list(flattened_items) if has_param_spec and num_args == 1 and types: first_arg = get_proper_type(types[0]) single_any = len(types) == 1 and isinstance(first_arg, AnyType) @@ -5946,6 +6245,11 @@ def visit_cast_expr(self, expr: CastExpr) -> None: if analyzed is not None: expr.type = analyzed + def visit_type_form_expr(self, expr: TypeFormExpr) -> None: + analyzed = self.anal_type(expr.type) + if analyzed is not None: + expr.type = analyzed + def visit_assert_type_expr(self, expr: AssertTypeExpr) -> None: expr.expr.accept(self) analyzed = self.anal_type(expr.type) @@ -6025,7 +6329,8 @@ def analyze_comp_for_2(self, expr: GeneratorExpr | DictionaryComprehension) -> N def visit_lambda_expr(self, expr: LambdaExpr) -> None: self.analyze_arg_initializers(expr) - self.analyze_function_body(expr) + with self.inside_except_star_block_set(False, entering_loop=False): + self.analyze_function_body(expr) def visit_conditional_expr(self, expr: ConditionalExpr) -> None: expr.if_expr.accept(self) @@ -6059,7 +6364,7 @@ def visit_yield_expr(self, e: YieldExpr) -> None: def visit_await_expr(self, expr: AwaitExpr) -> None: if not self.is_func_scope() or not self.function_stack: # We check both because is_function_scope() returns True inside comprehensions. - # This is not a blocker, because some enviroments (like ipython) + # This is not a blocker, because some environments (like ipython) # support top level awaits. self.fail('"await" outside function', expr, serious=True, code=codes.TOP_LEVEL_AWAIT) elif not self.function_stack[-1].is_coroutine: @@ -6117,6 +6422,55 @@ def visit_class_pattern(self, p: ClassPattern) -> None: def lookup( self, name: str, ctx: Context, suppress_errors: bool = False + ) -> SymbolTableNode | None: + node = self._lookup(name, ctx, suppress_errors) + if node is not None: + # This call is unfortunate from performance point of view, but + # needed for rare cases like e.g. testIncrementalChangingAlias. + self.record_imported_symbol(node) + return node + + def record_imported_symbol(self, sym: SymbolTableNode) -> None: + """If the symbol was not defined in current module, add its module to module_refs.""" + if sym.kind == LDEF or sym.node is None: + return + node = sym.node + if isinstance(node, PlaceholderNode) or not node.fullname: + # This node is not ready yet. + return + if node.fullname.startswith(("builtins.", "typing.")): + # Skip dependencies on builtins/typing. + return + # Modules, classes, and type aliases store defining module directly. + if isinstance(node, MypyFile): + fullname = node.fullname + elif isinstance(node, TypeInfo): + fullname = node.module_name + elif isinstance(node, TypeAlias): + fullname = node.module + elif isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)): + # For functions/variables infer defining module from enclosing class. + info = node.var.info if isinstance(node, Decorator) else node.info + if info: + fullname = info.module_name + else: + # global function/variable + fullname = node.fullname.rsplit(".", maxsplit=1)[0] + else: + # Some nodes (currently only TypeVarLikeExpr subclasses) don't store + # module fullname explicitly, infer it from the node fullname iteratively. + # TODO: this is not 100% robust for type variables nested within a class + # with a name that matches name of a submodule. + fullname = node.fullname.rsplit(".", maxsplit=1)[0] + if fullname == self.cur_mod_id: + return + while "." in fullname and fullname not in self.modules: + fullname = fullname.rsplit(".", maxsplit=1)[0] + if fullname != self.cur_mod_id: + self.cur_mod_node.module_refs.add(fullname) + + def _lookup( + self, name: str, ctx: Context, suppress_errors: bool = False ) -> SymbolTableNode | None: """Look up an unqualified (no dots) name in all active namespaces. @@ -6206,12 +6560,26 @@ class C: if self.statement is None: # Assume it's fine -- don't have enough context to check return True - return ( + if ( node is None or self.is_textually_before_statement(node) or not self.is_defined_in_current_module(node.fullname) - or isinstance(node, (TypeInfo, TypeAlias)) - or (isinstance(node, PlaceholderNode) and node.becomes_typeinfo) + ): + return True + if self.is_type_like(node): + # Allow forward references to classes/type aliases (see docstring), but + # a forward reference should never shadow an existing regular reference. + if node.name not in self.globals: + return True + global_node = self.globals[node.name] + if not self.is_textually_before_class(global_node.node): + return True + return not self.is_type_like(global_node.node) + return False + + def is_type_like(self, node: SymbolNode | None) -> bool: + return isinstance(node, (TypeInfo, TypeAlias)) or ( + isinstance(node, PlaceholderNode) and node.becomes_typeinfo ) def is_textually_before_statement(self, node: SymbolNode) -> bool: @@ -6233,6 +6601,13 @@ def is_textually_before_statement(self, node: SymbolNode) -> bool: else: return line_diff > 0 + def is_textually_before_class(self, node: SymbolNode | None) -> bool: + """Similar to above, but check if a node is defined before current class.""" + assert self.type is not None + if node is None: + return False + return node.line < self.type.defn.line + def is_overloaded_item(self, node: SymbolNode, statement: Statement) -> bool: """Check whether the function belongs to the overloaded variants""" if isinstance(node, OverloadedFuncDef) and isinstance(statement, FuncDef): @@ -6304,6 +6679,8 @@ def lookup_qualified( self.name_not_defined(name, ctx, namespace=namespace) return None sym = nextsym + if sym is not None: + self.record_imported_symbol(sym) return sym def lookup_type_node(self, expr: Expression) -> SymbolTableNode | None: @@ -6326,7 +6703,7 @@ def get_module_symbol(self, node: MypyFile, name: str) -> SymbolTableNode | None sym = names.get(name) if not sym: fullname = module + "." + name - if fullname in self.modules: + if fullname in self.modules and self.is_visible_import(module, fullname): sym = SymbolTableNode(GDEF, self.modules[fullname]) elif self.is_incomplete_namespace(module): self.record_incomplete_ref() @@ -6345,6 +6722,40 @@ def get_module_symbol(self, node: MypyFile, name: str) -> SymbolTableNode | None sym = None return sym + def is_visible_import(self, base_id: str, id: str) -> bool: + if id in self.import_map[self.cur_mod_id]: + # Fast path: module is imported locally. + return True + if base_id not in self.transitive_submodule_imports: + # This is a performance optimization for a common pattern. If one module + # in a codebase uses import numpy as np; np.foo.bar, then it is likely that + # other modules use similar pattern as well. So we pre-compute transitive + # dependencies for np, to avoid possible duplicate work in the future. + self.add_transitive_submodule_imports(base_id) + if self.cur_mod_id not in self.transitive_submodule_imports: + self.add_transitive_submodule_imports(self.cur_mod_id) + return id in self.transitive_submodule_imports[self.cur_mod_id] + + def add_transitive_submodule_imports(self, mod_id: str) -> None: + if mod_id not in self.import_map: + return + todo = self.import_map[mod_id] + seen = {mod_id} + result = {mod_id} + while todo: + dep = todo.pop() + if dep in seen: + continue + seen.add(dep) + if "." in dep: + result.add(dep) + if dep in self.transitive_submodule_imports: + result |= self.transitive_submodule_imports[dep] + continue + if dep in self.import_map: + todo |= self.import_map[dep] + self.transitive_submodule_imports[mod_id] = result + def is_missing_module(self, module: str) -> bool: return module in self.missing_modules @@ -6405,30 +6816,67 @@ 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") + if self._object_type is None: + self._object_type = self.named_type("builtins.object") + return self._object_type def str_type(self) -> Instance: - return self.named_type("builtins.str") + if self._str_type is None: + self._str_type = self.named_type("builtins.str") + return self._str_type + + def function_type(self) -> Instance: + if self._function_type is None: + self._function_type = self.named_type("builtins.function") + return self._function_type def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance: sym = self.lookup_fully_qualified(fullname) assert sym, "Internal error: attempted to construct unknown type" node = sym.node - assert isinstance(node, TypeInfo) + assert isinstance(node, TypeInfo), node if args: # TODO: assert len(args) == len(node.defn.type_vars) return Instance(node, args) @@ -6560,6 +7008,7 @@ def add_symbol_table_node( else: # see note in docstring describing None contexts self.defer() + if ( existing is not None and context is not None @@ -6579,7 +7028,9 @@ def add_symbol_table_node( self.add_redefinition(names, name, symbol) if not (isinstance(new, (FuncDef, Decorator)) and self.set_original_def(old, new)): self.name_already_defined(name, context, existing) - elif name not in self.missing_names[-1] and "*" not in self.missing_names[-1]: + elif type_param or ( + name not in self.missing_names[-1] and "*" not in self.missing_names[-1] + ): names[name] = symbol if not no_progress: self.progress = True @@ -7087,7 +7538,15 @@ def fail( if code is None: code = msg.code msg = msg.value - self.errors.report(ctx.line, ctx.column, msg, blocker=blocker, code=code) + self.errors.report( + ctx.line, + ctx.column, + msg, + blocker=blocker, + code=code, + end_line=ctx.end_line, + end_column=ctx.end_column, + ) def note(self, msg: str, ctx: Context, code: ErrorCode | None = None) -> None: if not self.in_checked_function(): @@ -7170,10 +7629,12 @@ def type_analyzer( allow_unbound_tvars: bool = False, allow_placeholder: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, allow_type_any: bool = False, ) -> TypeAnalyser: if tvar_scope is None: @@ -7183,15 +7644,18 @@ 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_typed_dict_special_forms=allow_typed_dict_special_forms, + allow_final=allow_final, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, prohibit_self_type=prohibit_self_type, + prohibit_special_class_field_types=prohibit_special_class_field_types, allow_type_any=allow_type_any, ) tpan.in_dynamic_func = bool(self.function_stack and self.function_stack[-1].is_dynamic()) @@ -7212,10 +7676,12 @@ def anal_type( allow_unbound_tvars: bool = False, allow_placeholder: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, allow_type_any: bool = False, ) -> Type | None: """Semantically analyze a type. @@ -7247,10 +7713,12 @@ def anal_type( allow_tuple_literal=allow_tuple_literal, allow_placeholder=allow_placeholder, allow_typed_dict_special_forms=allow_typed_dict_special_forms, + allow_final=allow_final, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, report_invalid_types=report_invalid_types, prohibit_self_type=prohibit_self_type, + prohibit_special_class_field_types=prohibit_special_class_field_types, allow_type_any=allow_type_any, ) tag = self.track_incomplete_refs() @@ -7296,8 +7764,6 @@ def add_type_alias_deps( If `target` is None, then the target node used will be the current scope. """ if not aliases_used: - # A basic optimization to avoid adding targets with no dependencies to - # the `alias_deps` dict. return if target is None: target = self.scope.current_target() @@ -7381,6 +7847,223 @@ def parse_dataclass_transform_field_specifiers(self, arg: Expression) -> tuple[s names.append(specifier.fullname) return tuple(names) + # leafs + def visit_int_expr(self, o: IntExpr, /) -> None: + return None + + def visit_str_expr(self, o: StrExpr, /) -> None: + return None + + def visit_bytes_expr(self, o: BytesExpr, /) -> None: + return None + + def visit_float_expr(self, o: FloatExpr, /) -> None: + return None + + def visit_complex_expr(self, o: ComplexExpr, /) -> None: + return None + + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: + return None + + def visit_temp_node(self, o: TempNode, /) -> None: + return None + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: + return None + + def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: + """Try to parse a value Expression as a type expression. + If success then annotate the Expression with the type that it spells. + If fails then emit no errors and take no further action. + + A value expression that is parsable as a type expression may be used + where a TypeForm is expected to represent the spelled type. + + Unlike ExpressionChecker.try_parse_as_type_expression() + (used in the later TypeChecker pass), this function can recognize + ALL kinds of type expressions, including type expressions containing + string annotations. + + If the provided Expression will be parsable later in + ExpressionChecker.try_parse_as_type_expression(), this function will + skip parsing the Expression to improve performance, because the later + function is called many fewer times (i.e. only lazily in a rare TypeForm + type context) than this function is called (i.e. eagerly for EVERY + expression in certain syntactic positions). + """ + # Count every call to this method for profiling + self.type_expression_parse_count += 1 + + # Bail ASAP if the Expression matches a common pattern that cannot possibly + # be a valid type expression, because this function is called very frequently + if not isinstance(maybe_type_expr, MaybeTypeExpression): + return + # Check types in order from most common to least common, for best performance + if isinstance(maybe_type_expr, (NameExpr, MemberExpr)): + # Defer parsing to the later TypeChecker pass, + # and only lazily in contexts where a TypeForm is expected + return + elif isinstance(maybe_type_expr, StrExpr): + str_value = maybe_type_expr.value # cache + # Filter out string literals with common patterns that could not + # possibly be in a type expression + if _MULTIPLE_WORDS_NONTYPE_RE.match(str_value): + # A common pattern in string literals containing a sentence. + # But cannot be a type expression. + maybe_type_expr.as_type = None + return + # Filter out string literals which look like an identifier but + # cannot be a type expression, for a few common reasons + if _IDENTIFIER_RE.fullmatch(str_value): + sym = self.lookup(str_value, UnboundType(str_value), suppress_errors=True) + if sym is None: + # Does not refer to anything in the local symbol table + maybe_type_expr.as_type = None + return + else: # sym is not None + node = sym.node # cache + if isinstance(node, PlaceholderNode) and not node.becomes_typeinfo: + # Either: + # 1. f'Cannot resolve name "{t.name}" (possible cyclic definition)' + # 2. Reference to an unknown placeholder node. + maybe_type_expr.as_type = None + return + unbound_tvar_or_paramspec = ( + isinstance(node, (TypeVarExpr, TypeVarTupleExpr, ParamSpecExpr)) + and self.tvar_scope.get_binding(sym) is None + ) + if unbound_tvar_or_paramspec: + # Either: + # 1. unbound_tvar: 'Type variable "{}" is unbound' [codes.VALID_TYPE] + # 2. unbound_paramspec: f'ParamSpec "{name}" is unbound' [codes.VALID_TYPE] + maybe_type_expr.as_type = None + return + else: # does not look like an identifier + if '"' in str_value or "'" in str_value: + # Only valid inside a Literal[...] type + if "[" not in str_value: + # Cannot be a Literal[...] type + maybe_type_expr.as_type = None + return + elif str_value == "": + # Empty string is not a valid type + maybe_type_expr.as_type = None + return + elif isinstance(maybe_type_expr, IndexExpr): + if isinstance(maybe_type_expr.base, NameExpr): + if isinstance( + maybe_type_expr.base.node, Var + ) and not self.var_is_typing_special_form(maybe_type_expr.base.node): + # Leftmost part of IndexExpr refers to a Var. Not a valid type. + maybe_type_expr.as_type = None + return + elif isinstance(maybe_type_expr.base, MemberExpr): + next_leftmost = maybe_type_expr.base + while True: + leftmost = next_leftmost.expr + if not isinstance(leftmost, MemberExpr): + break + next_leftmost = leftmost + if isinstance(leftmost, NameExpr): + if isinstance(leftmost.node, Var) and not self.var_is_typing_special_form( + leftmost.node + ): + # Leftmost part of IndexExpr refers to a Var. Not a valid type. + maybe_type_expr.as_type = None + return + else: + # Leftmost part of IndexExpr is not a NameExpr. Not a valid type. + maybe_type_expr.as_type = None + return + else: + # IndexExpr base is neither a NameExpr nor MemberExpr. Not a valid type. + maybe_type_expr.as_type = None + return + elif isinstance(maybe_type_expr, OpExpr): + if maybe_type_expr.op != "|": + # Binary operators other than '|' never spell a valid type + maybe_type_expr.as_type = None + return + else: + assert_never(maybe_type_expr) + + with self.isolated_error_analysis(): + try: + t = self.expr_to_analyzed_type(maybe_type_expr) + if self.errors.is_errors(): + t = None + except TypeTranslationError: + # Not a type expression + t = None + + if DEBUG_TYPE_EXPRESSION_FULL_PARSE_FAILURES and t is None: + original_flushed_files = set(self.errors.flushed_files) # save + try: + errors = self.errors.new_messages() # capture + finally: + self.errors.flushed_files = original_flushed_files # restore + + print( + f"SA.try_parse_as_type_expression: Full parse failure: {maybe_type_expr}, errors={errors!r}" + ) + + # Count full parse attempts for profiling + if t is not None: + self.type_expression_full_parse_success_count += 1 + else: + self.type_expression_full_parse_failure_count += 1 + + maybe_type_expr.as_type = t + + @staticmethod + def var_is_typing_special_form(var: Var) -> bool: + return var.fullname.startswith("typing") and var.fullname in [ + "typing.Annotated", + "typing_extensions.Annotated", + "typing.Callable", + "typing.Literal", + "typing_extensions.Literal", + "typing.Optional", + "typing.TypeGuard", + "typing_extensions.TypeGuard", + "typing.TypeIs", + "typing_extensions.TypeIs", + "typing.Union", + ] + + @contextmanager + def isolated_error_analysis(self) -> Iterator[None]: + """ + Context manager for performing error analysis that should not + affect the main SemanticAnalyzer state. + + Upon entering this context, `self.errors` will start empty. + Within this context, you can analyze expressions for errors. + Upon exiting this context, the original `self.errors` will be restored, + and any errors collected during the analysis will be discarded. + """ + # Save state + original_errors = self.errors + original_num_incomplete_refs = self.num_incomplete_refs + original_progress = self.progress + original_deferred = self.deferred + original_deferral_debug_context_len = len(self.deferral_debug_context) + + self.errors = Errors(Options()) + try: + yield + finally: + # Restore state + self.errors = original_errors + self.num_incomplete_refs = original_num_incomplete_refs + self.progress = original_progress + self.deferred = original_deferred + del self.deferral_debug_context[original_deferral_debug_context_len:] + def replace_implicit_first_type(sig: FunctionLike, new: Type) -> FunctionLike: if isinstance(sig, CallableType): @@ -7404,7 +8087,7 @@ def refers_to_fullname(node: Expression, fullnames: str | tuple[str, ...]) -> bo return False if node.fullname in fullnames: return True - if isinstance(node.node, TypeAlias): + if isinstance(node.node, TypeAlias) and not node.node.python_3_12_type_alias: return is_named_instance(node.node.target, fullnames) return False 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_infer.py b/mypy/semanal_infer.py index a146b56dc2d36..89a073cdad473 100644 --- a/mypy/semanal_infer.py +++ b/mypy/semanal_infer.py @@ -31,6 +31,7 @@ def infer_decorator_signature_if_simple( """ if dec.var.is_property: # Decorators are expected to have a callable type (it's a little odd). + # TODO: this may result in wrong type if @property is applied to decorated method. if dec.func.type is None: dec.var.type = CallableType( [AnyType(TypeOfAny.special_form)], @@ -47,6 +48,8 @@ def infer_decorator_signature_if_simple( for expr in dec.decorators: preserve_type = False if isinstance(expr, RefExpr) and isinstance(expr.node, FuncDef): + if expr.fullname == "typing.no_type_check": + return if expr.node.type and is_identity_signature(expr.node.type): preserve_type = True if not preserve_type: diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 09a1223be6aaa..b2c43e6becb8b 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -26,8 +26,10 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import nullcontext -from typing import TYPE_CHECKING, Callable, Final, List, Optional, Tuple, Union +from itertools import groupby +from typing import TYPE_CHECKING, Callable, Final, Optional, Union from typing_extensions import TypeAlias as _TypeAlias import mypy.build @@ -59,7 +61,7 @@ from mypy.build import Graph, State -Patches: _TypeAlias = List[Tuple[int, Callable[[], None]]] +Patches: _TypeAlias = list[tuple[int, Callable[[], None]]] # If we perform this many iterations, raise an exception since we are likely stuck. @@ -105,6 +107,17 @@ def semantic_analysis_for_scc(graph: Graph, scc: list[str], errors: Errors) -> N if "builtins" in scc: cleanup_builtin_scc(graph["builtins"]) + # Report TypeForm profiling stats + if len(scc) >= 1: + # Get manager from any state in the SCC (they all share the same manager) + manager = graph[scc[0]].manager + analyzer = manager.semantic_analyzer + manager.add_stats( + type_expression_parse_count=analyzer.type_expression_parse_count, + type_expression_full_parse_success_count=analyzer.type_expression_full_parse_success_count, + type_expression_full_parse_failure_count=analyzer.type_expression_full_parse_failure_count, + ) + def cleanup_builtin_scc(state: State) -> None: """Remove imported names from builtins namespace. @@ -179,7 +192,7 @@ def process_top_levels(graph: Graph, scc: list[str], patches: Patches) -> None: # Reverse order of the scc so the first modules in the original list will be # be processed first. This helps with performance. - scc = list(reversed(scc)) + scc = list(reversed(scc)) # noqa: FURB187 intentional copy # Initialize ASTs and symbol tables. for id in scc: @@ -232,26 +245,66 @@ def process_top_levels(graph: Graph, scc: list[str], patches: Patches) -> None: final_iteration = not any_progress +def order_by_subclassing(targets: list[FullTargetInfo]) -> Iterator[FullTargetInfo]: + """Make sure that superclass methods are always processed before subclass methods. + + This algorithm is not very optimal, but it is simple and should work well for lists + that are already almost correctly ordered. + """ + + # First, group the targets by their TypeInfo (since targets are sorted by line, + # we know that each TypeInfo will appear as group key only once). + grouped = [(k, list(g)) for k, g in groupby(targets, key=lambda x: x[3])] + remaining_infos = {info for info, _ in grouped if info is not None} + + next_group = 0 + while grouped: + if next_group >= len(grouped): + # This should never happen, if there is an MRO cycle, it should be reported + # and fixed during top-level processing. + raise ValueError("Cannot order method targets by MRO") + next_info, group = grouped[next_group] + if next_info is None: + # Trivial case, not methods but functions, process them straight away. + yield from group + grouped.pop(next_group) + continue + if any(parent in remaining_infos for parent in next_info.mro[1:]): + # We cannot process this method group yet, try a next one. + next_group += 1 + continue + yield from group + grouped.pop(next_group) + remaining_infos.discard(next_info) + # Each time after processing a method group we should retry from start, + # since there may be some groups that are not blocked on parents anymore. + next_group = 0 + + def process_functions(graph: Graph, scc: list[str], patches: Patches) -> None: # Process functions. + all_targets = [] for module in scc: tree = graph[module].tree assert tree is not None - analyzer = graph[module].manager.semantic_analyzer # In principle, functions can be processed in arbitrary order, # but _methods_ must be processed in the order they are defined, # because some features (most notably partial types) depend on # order of definitions on self. # # There can be multiple generated methods per line. Use target - # name as the second sort key to get a repeatable sort order on - # Python 3.5, which doesn't preserve dictionary order. + # name as the second sort key to get a repeatable sort order. targets = sorted(get_all_leaf_targets(tree), key=lambda x: (x[1].line, x[0])) - for target, node, active_type in targets: - assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator)) - process_top_level_function( - analyzer, graph[module], module, target, node, active_type, patches - ) + all_targets.extend( + [(module, target, node, active_type) for target, node, active_type in targets] + ) + + for module, target, node, active_type in order_by_subclassing(all_targets): + analyzer = graph[module].manager.semantic_analyzer + assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator)), node + process_top_level_function( + analyzer, graph[module], module, target, node, active_type, patches + ) def process_top_level_function( @@ -304,10 +357,15 @@ def process_top_level_function( analyzer.saved_locals.clear() -TargetInfo: _TypeAlias = Tuple[ +TargetInfo: _TypeAlias = tuple[ str, Union[MypyFile, FuncDef, OverloadedFuncDef, Decorator], Optional[TypeInfo] ] +# Same as above but includes module as first item. +FullTargetInfo: _TypeAlias = tuple[ + str, str, Union[MypyFile, FuncDef, OverloadedFuncDef, Decorator], Optional[TypeInfo] +] + def get_all_leaf_targets(file: MypyFile) -> list[TargetInfo]: """Return all leaf targets in a symbol table (module-level and methods).""" @@ -342,6 +400,7 @@ def semantic_analyze_target( analyzer.global_decls = [set()] analyzer.nonlocal_decls = [set()] analyzer.globals = tree.names + analyzer.imports = set() analyzer.progress = False with state.wrap_context(check_blockers=False): refresh_node = node diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 768dd265b3383..f27c89e34fdf8 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -6,8 +6,9 @@ from __future__ import annotations import keyword +from collections.abc import Container, Iterator, Mapping from contextlib import contextmanager -from typing import Container, Final, Iterator, List, Mapping, cast +from typing import Final, cast from mypy.errorcodes import ARG_TYPE, ErrorCode from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type @@ -33,6 +34,7 @@ NamedTupleExpr, NameExpr, PassStmt, + PlaceholderNode, RefExpr, Statement, StrExpr, @@ -191,12 +193,13 @@ def check_namedtuple_classdef( stmt.type, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="NamedTuple item type", + prohibit_special_class_field_types="NamedTuple", ) if analyzed is None: # Something is incomplete. We need to defer this named tuple. return None types.append(analyzed) - # ...despite possible minor failures that allow further analyzis. + # ...despite possible minor failures that allow further analysis. if name.startswith("_"): self.fail( f"NamedTuple field name cannot start with an underscore: {name}", stmt @@ -380,8 +383,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, ) @@ -484,6 +486,7 @@ def parse_namedtuple_fields_with_types( type, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="NamedTuple item type", + prohibit_special_class_field_types="NamedTuple", ) # Workaround #4987 and avoid introducing a bogus UnboundType if isinstance(analyzed, UnboundType): @@ -603,8 +606,8 @@ def add_method( items = [arg.variable.name for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in types - signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) - signature.variables = [self_type] + signature = CallableType(cast(list[Type], types), arg_kinds, items, ret, function_type) + signature.variables = (self_type,) func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod @@ -695,10 +698,14 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]: if isinstance(sym.node, (FuncBase, Decorator)) and not sym.plugin_generated: # Keep user-defined methods as is. continue - # Keep existing (user-provided) definitions under mangled names, so they - # get semantically analyzed. - r_key = get_unique_redefinition_name(key, named_tuple_info.names) - named_tuple_info.names[r_key] = sym + # Do not retain placeholders - we'll get back here if they cease to + # be placeholders later. If we keep placeholders alive, they may never + # be reached again, making it to cacheable symtable. + if not isinstance(sym.node, PlaceholderNode): + # Keep existing (user-provided) definitions under mangled names, so they + # get semantically analyzed. + r_key = get_unique_redefinition_name(key, named_tuple_info.names) + named_tuple_info.names[r_key] = sym named_tuple_info.names[key] = value # Helpers diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index c9c0c46f7aeec..0c717b5d9a0e7 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -174,7 +174,7 @@ def analyze_newtype_declaration(self, s: AssignmentStmt) -> tuple[str | None, Ca def check_newtype_args( self, name: str, call: CallExpr, context: Context ) -> tuple[Type | None, bool]: - """Ananlyze base type in NewType call. + """Analyze base type in NewType call. Return a tuple (type, should defer). """ diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index aaa01969217ae..266fd236a01f6 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -74,6 +74,9 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - if last.end_line is not None: # We are on a Python version recent enough to support end lines. self.skipped_lines |= set(range(next_def.line, last.end_line + 1)) + file.imports = [ + i for i in file.imports if (i.line, i.column) <= (defn.line, defn.column) + ] del file.defs[i + 1 :] break file.skipped_lines = self.skipped_lines diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index cb0bdebab724f..c49b13d08f451 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -3,8 +3,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import Callable, Final, overload -from typing_extensions import Literal, Protocol +from typing import Callable, Final, Literal, Protocol, overload from mypy_extensions import trait @@ -47,6 +46,7 @@ TypeVarLikeType, TypeVarTupleType, UnpackType, + flatten_nested_tuples, get_proper_type, ) @@ -76,11 +76,11 @@ def lookup_qualified( raise NotImplementedError @abstractmethod - def lookup_fully_qualified(self, name: str) -> SymbolTableNode: + def lookup_fully_qualified(self, fullname: str, /) -> SymbolTableNode: raise NotImplementedError @abstractmethod - def lookup_fully_qualified_or_none(self, name: str) -> SymbolTableNode | None: + def lookup_fully_qualified_or_none(self, fullname: str, /) -> SymbolTableNode | None: raise NotImplementedError @abstractmethod @@ -176,7 +176,8 @@ def accept(self, node: Node) -> None: @abstractmethod def anal_type( self, - t: Type, + typ: Type, + /, *, tvar_scope: TypeVarLikeScope | None = None, allow_tuple_literal: bool = False, @@ -185,6 +186,7 @@ def anal_type( allow_placeholder: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, ) -> Type | None: raise NotImplementedError @@ -197,11 +199,11 @@ def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance, line: in raise NotImplementedError @abstractmethod - def schedule_patch(self, priority: int, fn: Callable[[], None]) -> None: + def schedule_patch(self, priority: int, patch: Callable[[], None]) -> None: raise NotImplementedError @abstractmethod - def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> bool: + def add_symbol_table_node(self, name: str, symbol: SymbolTableNode) -> bool: """Add node to the current symbol table.""" raise NotImplementedError @@ -241,7 +243,7 @@ def parse_bool(self, expr: Expression) -> bool | None: raise NotImplementedError @abstractmethod - def qualified_name(self, n: str) -> str: + def qualified_name(self, name: str) -> str: raise NotImplementedError @property @@ -289,7 +291,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None: fallback = typ.partial_fallback assert fallback.type.fullname == "builtins.tuple" items = [] - for item in typ.items: + for item in flatten_nested_tuples(typ.items): # TODO: this duplicates some logic in typeops.tuple_fallback(). if isinstance(item, UnpackType): unpacked_type = get_proper_type(item.type) @@ -301,14 +303,16 @@ def calculate_tuple_fallback(typ: TupleType) -> None: ): items.append(unpacked_type.args[0]) else: - raise NotImplementedError + # This is called before semanal_typeargs.py fixes broken unpacks, + # where the error should also be generated. + items.append(AnyType(TypeOfAny.from_error)) else: items.append(item) fallback.args = (make_simplified_union(items),) class _NamedTypeCallback(Protocol): - def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> Instance: ... + def __call__(self, fullname: str, args: list[Type] | None = None) -> Instance: ... def paramspec_args( @@ -451,7 +455,7 @@ def require_bool_literal_argument( api: SemanticAnalyzerInterface | SemanticAnalyzerPluginInterface, expression: Expression, name: str, - default: Literal[True] | Literal[False], + default: Literal[True, False], ) -> bool: ... diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 646bb28a3b6e5..9d1ce1fd60806 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -83,12 +83,11 @@ def visit_block(self, o: Block) -> None: def visit_type_alias_type(self, t: TypeAliasType) -> None: super().visit_type_alias_type(t) - if t in self.seen_aliases: - # Avoid infinite recursion on recursive type aliases. - # Note: it is fine to skip the aliases we have already seen in non-recursive - # types, since errors there have already been reported. - return - self.seen_aliases.add(t) + if t.is_recursive: + if t in self.seen_aliases: + # Avoid infinite recursion on recursive type aliases. + return + self.seen_aliases.add(t) assert t.alias is not None, f"Unfixed type alias {t.type_ref}" is_error, is_invalid = self.validate_args( t.alias.name, tuple(t.args), t.alias.alias_tvars, t @@ -101,7 +100,12 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: 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. - get_proper_type(t).accept(self) + if t.args: + # Since we always allow unbounded type variables in alias definitions, we need + # to verify the arguments satisfy the upper bounds of the expansion as well. + get_proper_type(t).accept(self) + if t.is_recursive: + self.seen_aliases.discard(t) def visit_tuple_type(self, t: TupleType) -> None: t.items = flatten_nested_tuples(t.items) @@ -147,18 +151,19 @@ def validate_args( is_error = False is_invalid = False - for (i, arg), tvar in zip(enumerate(args), type_vars): + for arg, tvar in zip(args, type_vars): + context = ctx if arg.line < 0 else arg if isinstance(tvar, TypeVarType): if isinstance(arg, ParamSpecType): is_invalid = True self.fail( INVALID_PARAM_SPEC_LOCATION.format(format_type(arg, self.options)), - ctx, + context, code=codes.VALID_TYPE, ) self.note( INVALID_PARAM_SPEC_LOCATION_NOTE.format(arg.name), - ctx, + context, code=codes.VALID_TYPE, ) continue @@ -167,28 +172,30 @@ def validate_args( self.fail( f"Cannot use {format_type(arg, self.options)} for regular type variable," " only for ParamSpec", - ctx, + context, code=codes.VALID_TYPE, ) continue + if self.in_type_alias_expr and isinstance(arg, TypeVarType): + # Type aliases are allowed to use unconstrained type variables + # error will be checked at substitution point. + continue if tvar.values: if isinstance(arg, TypeVarType): - if self.in_type_alias_expr: - # Type aliases are allowed to use unconstrained type variables - # error will be checked at substitution point. - continue arg_values = arg.values if not arg_values: is_error = True self.fail( message_registry.INVALID_TYPEVAR_AS_TYPEARG.format(arg.name, name), - ctx, + context, code=codes.TYPE_VAR, ) continue else: arg_values = [arg] - if self.check_type_var_values(name, arg_values, tvar.name, tvar.values, ctx): + if self.check_type_var_values( + name, arg_values, tvar.name, tvar.values, context + ): is_error = True # Check against upper bound. Since it's object the vast majority of the time, # add fast path to avoid a potentially slow subtype check. @@ -198,10 +205,6 @@ def validate_args( and upper_bound.type.fullname == "builtins.object" ) if not object_upper_bound and not is_subtype(arg, upper_bound): - if self.in_type_alias_expr and isinstance(arg, TypeVarType): - # Type aliases are allowed to use unconstrained type variables - # error will be checked at substitution point. - continue is_error = True self.fail( message_registry.INVALID_TYPEVAR_ARG_BOUND.format( @@ -209,7 +212,7 @@ def validate_args( name, format_type(upper_bound, self.options), ), - ctx, + context, code=codes.TYPE_VAR, ) elif isinstance(tvar, ParamSpecType): @@ -220,7 +223,7 @@ def validate_args( self.fail( "Can only replace ParamSpec with a parameter types list or" f" another ParamSpec, got {format_type(arg, self.options)}", - ctx, + context, code=codes.VALID_TYPE, ) if is_invalid: @@ -251,6 +254,10 @@ def visit_unpack_type(self, typ: UnpackType) -> None: def check_type_var_values( self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context ) -> bool: + if self.in_type_alias_expr: + # See testValidTypeAliasValues - we do not enforce typevar compatibility + # at the definition site. We check instantiation validity later. + return False is_error = False for actual in get_proper_types(actuals): # We skip UnboundType here, since they may appear in defn.bases, diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index d081898bf010c..8bf073d30f710 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Collection from typing import Final from mypy import errorcodes as codes, message_registry @@ -29,6 +30,7 @@ StrExpr, TempNode, TupleExpr, + TypeAlias, TypedDictExpr, TypeInfo, ) @@ -49,6 +51,7 @@ TypedDictType, TypeOfAny, TypeVarLikeType, + get_proper_type, ) TPDICT_CLASS_ERROR: Final = ( @@ -97,21 +100,23 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N existing_info = None if isinstance(defn.analyzed, TypedDictExpr): existing_info = defn.analyzed.info + + field_types: dict[str, Type] | None if ( len(defn.base_type_exprs) == 1 and isinstance(defn.base_type_exprs[0], RefExpr) and defn.base_type_exprs[0].fullname in TPDICT_NAMES ): # Building a new TypedDict - fields, types, statements, required_keys, readonly_keys = ( + field_types, statements, required_keys, readonly_keys = ( self.analyze_typeddict_classdef_fields(defn) ) - if fields is None: + if field_types 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, readonly_keys, defn.line, existing_info + defn.name, field_types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -134,46 +139,39 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N typeddict_bases_set.add("TypedDict") else: self.fail('Duplicate base class "TypedDict"', defn) - elif isinstance(expr, RefExpr) and self.is_typeddict(expr): - assert expr.fullname - if expr.fullname not in typeddict_bases_set: - typeddict_bases_set.add(expr.fullname) - typeddict_bases.append(expr) - else: - assert isinstance(expr.node, TypeInfo) - self.fail(f'Duplicate base class "{expr.node.name}"', defn) - elif isinstance(expr, IndexExpr) and self.is_typeddict(expr.base): - assert isinstance(expr.base, RefExpr) - assert expr.base.fullname - if expr.base.fullname not in typeddict_bases_set: - typeddict_bases_set.add(expr.base.fullname) + elif ( + isinstance(expr, RefExpr) + and self.is_typeddict(expr) + or isinstance(expr, IndexExpr) + and self.is_typeddict(expr.base) + ): + info = self._parse_typeddict_base(expr, defn) + if info.fullname not in typeddict_bases_set: + typeddict_bases_set.add(info.fullname) typeddict_bases.append(expr) else: - assert isinstance(expr.base.node, TypeInfo) - self.fail(f'Duplicate base class "{expr.base.node.name}"', defn) + self.fail(f'Duplicate base class "{info.name}"', defn) else: self.fail("All bases of a new TypedDict must be TypedDict types", defn) - keys: list[str] = [] - types = [] + field_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, readonly_keys, defn + base, field_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) + (new_field_types, new_statements, new_required_keys, new_readonly_keys) = ( + self.analyze_typeddict_classdef_fields(defn, oldfields=field_types) ) - if new_keys is None: + if new_field_types is None: return True, None # Defer - keys.extend(new_keys) - types.extend(new_types) + field_types.update(new_field_types) required_keys.update(new_required_keys) readonly_keys.update(new_readonly_keys) info = self.build_typeddict_typeinfo( - defn.name, keys, types, required_keys, readonly_keys, defn.line, existing_info + defn.name, field_types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -184,28 +182,18 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N def add_keys_and_types_from_base( self, base: Expression, - keys: list[str], - types: list[Type], + field_types: dict[str, Type], required_keys: set[str], readonly_keys: set[str], ctx: Context, ) -> None: + info = self._parse_typeddict_base(base, ctx) base_args: list[Type] = [] - if isinstance(base, RefExpr): - assert isinstance(base.node, TypeInfo) - info = base.node - elif isinstance(base, IndexExpr): - assert isinstance(base.base, RefExpr) - assert isinstance(base.base.node, TypeInfo) - info = base.base.node + if isinstance(base, IndexExpr): args = self.analyze_base_args(base, ctx) if args is None: return base_args = args - else: - assert isinstance(base, CallExpr) - assert isinstance(base.analyzed, TypedDictExpr) - info = base.analyzed.info assert info.typeddict_type is not None base_typed_dict = info.typeddict_type @@ -224,13 +212,33 @@ def add_keys_and_types_from_base( with state.strict_optional_set(self.options.strict_optional): valid_items = self.map_items_to_base(valid_items, tvars, base_args) for key in base_items: - if key in keys: + if key in field_types: self.fail(TYPEDDICT_OVERRIDE_MERGE.format(key), ctx) - keys.extend(valid_items.keys()) - types.extend(valid_items.values()) + + field_types.update(valid_items) required_keys.update(base_typed_dict.required_keys) readonly_keys.update(base_typed_dict.readonly_keys) + def _parse_typeddict_base(self, base: Expression, ctx: Context) -> TypeInfo: + if isinstance(base, RefExpr): + if isinstance(base.node, TypeInfo): + return base.node + elif isinstance(base.node, TypeAlias): + # Only old TypeAlias / plain assignment, PEP695 `type` stmt + # cannot be used as a base class + target = get_proper_type(base.node.target) + assert isinstance(target, TypedDictType) + return target.fallback.type + else: + assert False + elif isinstance(base, IndexExpr): + assert isinstance(base.base, RefExpr) + return self._parse_typeddict_base(base.base, ctx) + else: + assert isinstance(base, CallExpr) + assert isinstance(base.analyzed, TypedDictExpr) + return base.analyzed.info + def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None: """Analyze arguments of base type expressions as types. @@ -280,23 +288,34 @@ def map_items_to_base( return mapped_items def analyze_typeddict_classdef_fields( - self, defn: ClassDef, oldfields: list[str] | None = None - ) -> tuple[list[str] | None, list[Type], list[Statement], set[str], set[str]]: + self, defn: ClassDef, oldfields: Collection[str] | None = None + ) -> tuple[dict[str, Type] | None, 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, if given. Return tuple with these items: - * List of keys (or None if found an incomplete reference --> deferral) - * List of types for each key + * Dict of key -> type (or None if found an incomplete reference -> deferral) * List of statements from defn.defs.body that are legally allowed to be a part of a TypedDict definition * Set of required keys """ - fields: list[str] = [] - types: list[Type] = [] + fields: dict[str, Type] = {} + readonly_keys = set[str]() + required_keys = set[str]() statements: list[Statement] = [] + + total: bool | None = True + for key in defn.keywords: + if key == "total": + total = require_bool_literal_argument( + self.api, defn.keywords["total"], "total", True + ) + continue + for_function = ' for "__init_subclass__" of "TypedDict"' + self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) + for stmt in defn.defs.body: if not isinstance(stmt, AssignmentStmt): # Still allow pass or ... (for empty TypedDict's) and docstrings @@ -320,50 +339,41 @@ def analyze_typeddict_classdef_fields( self.fail(f'Duplicate TypedDict key "{name}"', stmt) continue # Append stmt, name, and type in this case... - fields.append(name) statements.append(stmt) + + field_type: Type if stmt.unanalyzed_type is None: - types.append(AnyType(TypeOfAny.unannotated)) + field_type = AnyType(TypeOfAny.unannotated) else: analyzed = self.api.anal_type( stmt.unanalyzed_type, allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", + prohibit_special_class_field_types="TypedDict", ) if analyzed is None: - return None, [], [], set(), set() # Need to defer - types.append(analyzed) + return None, [], set(), set() # Need to defer + field_type = analyzed if not has_placeholder(analyzed): stmt.type = self.extract_meta_info(analyzed, stmt)[0] + + field_type, required, readonly = self.extract_meta_info(field_type) + fields[name] = field_type + + if (total or required is True) and required is not False: + required_keys.add(name) + if readonly: + readonly_keys.add(name) + # ...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) elif not isinstance(stmt.rvalue, TempNode): # x: int assigns rvalue to TempNode(AnyType()) self.fail("Right hand side values are not supported in TypedDict", stmt) - total: bool | None = True - if "total" in defn.keywords: - total = require_bool_literal_argument(self.api, defn.keywords["total"], "total", True) - if defn.keywords and defn.keywords.keys() != {"total"}: - for_function = ' for "__init_subclass__" of "TypedDict"' - for key in defn.keywords: - if key == "total": - continue - self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) - 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 + return fields, statements, required_keys, readonly_keys def extract_meta_info( self, typ: Type, context: Context | None = None @@ -432,7 +442,7 @@ def check_typeddict( name += "@" + str(call.line) else: name = var_name = "TypedDict@" + str(call.line) - info = self.build_typeddict_typeinfo(name, [], [], set(), 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( @@ -472,7 +482,12 @@ def check_typeddict( if isinstance(node.analyzed, TypedDictExpr): existing_info = node.analyzed.info info = self.build_typeddict_typeinfo( - name, items, types, required_keys, readonly_keys, call.line, existing_info + name, + dict(zip(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. @@ -520,7 +535,7 @@ def parse_typeddict_args( return "", [], [], True, [], False dictexpr = args[1] tvar_defs = self.api.get_and_bind_all_tvars([t for k, t in dictexpr.items]) - res = self.parse_typeddict_fields_with_types(dictexpr.items, call) + res = self.parse_typeddict_fields_with_types(dictexpr.items) if res is None: # One of the types is not ready, defer. return None @@ -529,7 +544,7 @@ def parse_typeddict_args( return args[0].value, items, types, total, tvar_defs, ok def parse_typeddict_fields_with_types( - self, dict_items: list[tuple[Expression | None, Expression]], context: Context + self, dict_items: list[tuple[Expression | None, Expression]] ) -> tuple[list[str], list[Type], bool] | None: """Parse typed dict items passed as pairs (name expression, type expression). @@ -561,6 +576,7 @@ def parse_typeddict_fields_with_types( allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", + prohibit_special_class_field_types="TypedDict", ) if analyzed is None: return None @@ -576,8 +592,7 @@ def fail_typeddict_arg( def build_typeddict_typeinfo( self, name: str, - items: list[str], - types: list[Type], + item_types: dict[str, Type], required_keys: set[str], readonly_keys: set[str], line: int, @@ -591,9 +606,7 @@ 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, readonly_keys, fallback - ) + typeddict_type = TypedDictType(item_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 @@ -604,10 +617,11 @@ def build_typeddict_typeinfo( # Helpers def is_typeddict(self, expr: Expression) -> bool: - return ( - isinstance(expr, RefExpr) - and isinstance(expr.node, TypeInfo) + return isinstance(expr, RefExpr) and ( + isinstance(expr.node, TypeInfo) and expr.node.typeddict_type is not None + or isinstance(expr.node, TypeAlias) + and isinstance(get_proper_type(expr.node.target), TypedDictType) ) def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None: diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index fc868d288b4df..15d472b648866 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -52,14 +52,15 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' from __future__ import annotations -from typing import Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Union from typing_extensions import TypeAlias as _TypeAlias from mypy.expandtype import expand_type from mypy.nodes import ( + SYMBOL_FUNCBASE_TYPES, UNBOUND_IMPORTED, Decorator, - FuncBase, FuncDef, FuncItem, MypyFile, @@ -114,10 +115,10 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' # Type snapshots are strict, they must be hashable and ordered (e.g. for Unions). Primitive: _TypeAlias = Union[str, float, int, bool] # float is for Literal[3.14] support. -SnapshotItem: _TypeAlias = Tuple[Union[Primitive, "SnapshotItem"], ...] +SnapshotItem: _TypeAlias = tuple[Union[Primitive, "SnapshotItem"], ...] # Symbol snapshots can be more lenient. -SymbolSnapshot: _TypeAlias = Tuple[object, ...] +SymbolSnapshot: _TypeAlias = tuple[object, ...] def compare_symbol_table_snapshots( @@ -219,7 +220,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 @@ -231,19 +234,33 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb The representation is nested tuples and dicts. Only externally visible attributes are included. """ - if isinstance(node, FuncBase): + if isinstance(node, SYMBOL_FUNCBASE_TYPES): # TODO: info if node.type: - signature = snapshot_type(node.type) + signature: tuple[object, ...] = snapshot_type(node.type) else: signature = snapshot_untyped_signature(node) impl: FuncDef | None = None if isinstance(node, FuncDef): impl = node - elif isinstance(node, OverloadedFuncDef) and node.impl: + elif node.impl: impl = node.impl.func if isinstance(node.impl, Decorator) else node.impl + setter_type = None + if isinstance(node, OverloadedFuncDef) and node.items: + first_item = node.items[0] + if isinstance(first_item, Decorator) and first_item.func.is_property: + setter_type = snapshot_optional_type(first_item.var.setter_type) is_trivial_body = impl.is_trivial_body if impl else False dataclass_transform_spec = find_dataclass_transform_spec(node) + + deprecated: str | list[str | None] | None = None + if isinstance(node, FuncDef): + deprecated = node.deprecated + elif isinstance(node, OverloadedFuncDef): + deprecated = [node.deprecated] + [ + i.func.deprecated for i in node.items if isinstance(i, Decorator) + ] + return ( "Func", common, @@ -254,6 +271,8 @@ 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, + deprecated, + setter_type, # multi-part properties are stored as OverloadedFuncDef ) elif isinstance(node, Var): return ("Var", common, snapshot_optional_type(node.type), node.is_final) @@ -300,6 +319,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) @@ -415,6 +435,7 @@ def visit_param_spec(self, typ: ParamSpecType) -> SnapshotItem: typ.flavor, snapshot_type(typ.upper_bound), snapshot_type(typ.default), + snapshot_type(typ.prefix), ) def visit_type_var_tuple(self, typ: TypeVarTupleType) -> SnapshotItem: @@ -449,6 +470,7 @@ def visit_callable_type(self, typ: CallableType) -> SnapshotItem: typ.is_type_obj(), typ.is_ellipsis_args, snapshot_types(typ.variables), + typ.is_bound, ) def normalize_callable_variables(self, typ: CallableType) -> CallableType: @@ -497,7 +519,7 @@ def visit_partial_type(self, typ: PartialType) -> SnapshotItem: raise RuntimeError def visit_type_type(self, typ: TypeType) -> SnapshotItem: - return ("TypeType", snapshot_type(typ.item)) + return ("TypeType", snapshot_type(typ.item), typ.is_type_form) def visit_type_alias_type(self, typ: TypeAliasType) -> SnapshotItem: assert typ.alias is not None diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 174c2922c7676..56f2f935481c5 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -51,6 +51,7 @@ from mypy.nodes import ( MDEF, + SYMBOL_NODE_EXPRESSION_TYPES, AssertTypeExpr, AssignmentStmt, Block, @@ -74,6 +75,7 @@ SymbolTable, TypeAlias, TypedDictExpr, + TypeFormExpr, TypeInfo, Var, ) @@ -160,7 +162,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 @@ -290,6 +292,10 @@ def visit_cast_expr(self, node: CastExpr) -> None: super().visit_cast_expr(node) self.fixup_type(node.type) + def visit_type_form_expr(self, node: TypeFormExpr) -> None: + super().visit_type_form_expr(node) + self.fixup_type(node.type) + def visit_assert_type_expr(self, node: AssertTypeExpr) -> None: super().visit_assert_type_expr(node) self.fixup_type(node.type) @@ -301,7 +307,7 @@ def visit_super_expr(self, node: SuperExpr) -> None: def visit_call_expr(self, node: CallExpr) -> None: super().visit_call_expr(node) - if isinstance(node.analyzed, SymbolNode): + if isinstance(node.analyzed, SYMBOL_NODE_EXPRESSION_TYPES): node.analyzed = self.fixup(node.analyzed) def visit_newtype_expr(self, node: NewTypeExpr) -> None: @@ -330,6 +336,7 @@ def visit_enum_call_expr(self, node: EnumCallExpr) -> None: def visit_var(self, node: Var) -> None: node.info = self.fixup(node.info) self.fixup_type(node.type) + self.fixup_type(node.setter_type) super().visit_var(node) def visit_type_alias(self, node: TypeAlias) -> None: @@ -343,13 +350,11 @@ def visit_type_alias(self, node: TypeAlias) -> None: def fixup(self, node: SN) -> SN: if node in self.replacements: new = self.replacements[node] - skip_slots: tuple[str, ...] = () if isinstance(node, TypeInfo) and isinstance(new, TypeInfo): # Special case: special_alias is not exposed in symbol tables, but may appear # in external types (e.g. named tuples), so we need to update it manually. - skip_slots = ("special_alias",) replace_object_state(new.special_alias, node.special_alias) - replace_object_state(new, node, skip_slots=skip_slots) + replace_object_state(new, node, skip_slots=_get_ignored_slots(new)) return cast(SN, new) return node @@ -489,6 +494,7 @@ def visit_type_var(self, typ: TypeVarType) -> None: def visit_param_spec(self, typ: ParamSpecType) -> None: typ.upper_bound.accept(self) typ.default.accept(self) + typ.prefix.accept(self) def visit_type_var_tuple(self, typ: TypeVarTupleType) -> None: typ.upper_bound.accept(self) @@ -554,9 +560,16 @@ def replace_nodes_in_symbol_table( if node.node in replacements: new = replacements[node.node] old = node.node - # Needed for TypeInfo, see comment in fixup() above. - replace_object_state(new, old, skip_slots=("special_alias",)) + replace_object_state(new, old, skip_slots=_get_ignored_slots(new)) node.node = new if isinstance(node.node, (Var, TypeAlias)): # Handle them here just in case these aren't exposed through the AST. node.node.accept(NodeReplaceVisitor(replacements)) + + +def _get_ignored_slots(node: SymbolNode) -> tuple[str, ...]: + if isinstance(node, OverloadedFuncDef): + return ("setter",) + if isinstance(node, TypeInfo): + return ("special_alias",) + return () diff --git a/mypy/server/aststrip.py b/mypy/server/aststrip.py index 05af6a3d53a11..27c1c4a0eedbb 100644 --- a/mypy/server/aststrip.py +++ b/mypy/server/aststrip.py @@ -33,8 +33,8 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager, nullcontext -from typing import Dict, Iterator, Tuple from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import ( @@ -68,7 +68,7 @@ from mypy.types import CallableType from mypy.typestate import type_state -SavedAttributes: _TypeAlias = Dict[Tuple[ClassDef, str], SymbolTableNode] +SavedAttributes: _TypeAlias = dict[tuple[ClassDef, str], SymbolTableNode] def strip_target( @@ -165,7 +165,7 @@ def visit_func_def(self, node: FuncDef) -> None: # in order to get the state exactly as it was before semantic analysis. # See also #4814. assert isinstance(node.type, CallableType) - node.type.variables = [] + node.type.variables = () with self.enter_method(node.info) if node.info else nullcontext(): super().visit_func_def(node) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index 9ed2d4549629c..ba622329665ea 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. @@ -82,12 +82,12 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a from __future__ import annotations from collections import defaultdict -from typing import List from mypy.nodes import ( GDEF, LDEF, MDEF, + SYMBOL_FUNCBASE_TYPES, AssertTypeExpr, AssignmentStmt, AwaitExpr, @@ -125,6 +125,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a TypeAliasExpr, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeInfo, TypeVarExpr, UnaryExpr, @@ -227,14 +228,17 @@ def __init__( self.scope = Scope() self.type_map = type_map # This attribute holds a mapping from target to names of type aliases - # it depends on. These need to be processed specially, since they are - # only present in expanded form in symbol tables. For example, after: - # A = List[int] + # it depends on. These need to be processed specially, since they may + # appear in expanded form in symbol tables, because of a get_proper_type() + # somewhere. For example, after: + # A = int # x: A - # The module symbol table will just have a Var `x` with type `List[int]`, - # and the dependency of `x` on `A` is lost. Therefore the alias dependencies + # the module symbol table will just have a Var `x` with type `int`, + # and the dependency of `x` on `A` is lost. Therefore, the alias dependencies # are preserved at alias expansion points in `semanal.py`, stored as an attribute # on MypyFile, and then passed here. + # TODO: fine-grained is more susceptible to this partially because we are reckless + # about get_proper_type() in *this specific file*. self.alias_deps = alias_deps self.map: dict[str, set[str]] = {} self.is_class = False @@ -502,7 +506,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: if isinstance(rvalue.callee.node, TypeInfo): # use actual __init__ as a dependency source init = rvalue.callee.node.get("__init__") - if init and isinstance(init.node, FuncBase): + if init and isinstance(init.node, SYMBOL_FUNCBASE_TYPES): fname = init.node.fullname else: fname = rvalue.callee.fullname @@ -763,6 +767,10 @@ def visit_cast_expr(self, e: CastExpr) -> None: super().visit_cast_expr(e) self.add_type_dependencies(e.type) + def visit_type_form_expr(self, e: TypeFormExpr) -> None: + super().visit_type_form_expr(e) + self.add_type_dependencies(e.type) + def visit_assert_type_expr(self, e: AssertTypeExpr) -> None: super().visit_assert_type_expr(e) self.add_type_dependencies(e.type) @@ -947,7 +955,7 @@ def get_type_triggers( return typ.accept(TypeTriggersVisitor(use_logical_deps, seen_aliases)) -class TypeTriggersVisitor(TypeVisitor[List[str]]): +class TypeTriggersVisitor(TypeVisitor[list[str]]): def __init__( self, use_logical_deps: bool, seen_aliases: set[TypeAliasType] | None = None ) -> None: @@ -979,8 +987,6 @@ def visit_type_alias_type(self, typ: TypeAliasType) -> list[str]: triggers = [trigger] for arg in typ.args: triggers.extend(self.get_type_triggers(arg)) - # TODO: Now that type aliases are its own kind of types we can simplify - # the logic to rely on intermediate dependencies (like for instance types). triggers.extend(self.get_type_triggers(typ.alias.target)) return triggers @@ -1037,10 +1043,8 @@ def visit_type_var(self, typ: TypeVarType) -> list[str]: triggers = [] if typ.fullname: triggers.append(make_trigger(typ.fullname)) - if typ.upper_bound: - triggers.extend(self.get_type_triggers(typ.upper_bound)) - if typ.default: - triggers.extend(self.get_type_triggers(typ.default)) + triggers.extend(self.get_type_triggers(typ.upper_bound)) + triggers.extend(self.get_type_triggers(typ.default)) for val in typ.values: triggers.extend(self.get_type_triggers(val)) return triggers @@ -1049,22 +1053,17 @@ def visit_param_spec(self, typ: ParamSpecType) -> list[str]: triggers = [] if typ.fullname: triggers.append(make_trigger(typ.fullname)) - if typ.upper_bound: - triggers.extend(self.get_type_triggers(typ.upper_bound)) - if typ.default: - triggers.extend(self.get_type_triggers(typ.default)) triggers.extend(self.get_type_triggers(typ.upper_bound)) + triggers.extend(self.get_type_triggers(typ.default)) + triggers.extend(self.get_type_triggers(typ.prefix)) return triggers def visit_type_var_tuple(self, typ: TypeVarTupleType) -> list[str]: triggers = [] if typ.fullname: triggers.append(make_trigger(typ.fullname)) - if typ.upper_bound: - triggers.extend(self.get_type_triggers(typ.upper_bound)) - if typ.default: - triggers.extend(self.get_type_triggers(typ.default)) triggers.extend(self.get_type_triggers(typ.upper_bound)) + triggers.extend(self.get_type_triggers(typ.default)) return triggers def visit_unpack_type(self, typ: UnpackType) -> list[str]: diff --git a/mypy/server/mergecheck.py b/mypy/server/mergecheck.py index 6f044a5ea8b94..11e00213d05a7 100644 --- a/mypy/server/mergecheck.py +++ b/mypy/server/mergecheck.py @@ -26,10 +26,11 @@ def check_consistency(o: object) -> None: continue fn = sym.fullname - # Skip None names, since they are ambiguous. + # Skip None and empty names, since they are ambiguous. # TODO: Everything should have a proper full name? - if fn is None: + if not fn: continue + # Skip stuff that should be expected to have duplicate names if isinstance(sym, (Var, Decorator)): continue @@ -37,7 +38,7 @@ def check_consistency(o: object) -> None: continue if fn not in m: - m[sym.fullname] = sym + m[fn] = sym continue # We have trouble and need to decide what to do about it. diff --git a/mypy/server/objgraph.py b/mypy/server/objgraph.py index a13fd84129340..e5096d5befa34 100644 --- a/mypy/server/objgraph.py +++ b/mypy/server/objgraph.py @@ -4,8 +4,8 @@ import types import weakref -from collections.abc import Iterable -from typing import Final, Iterator, Mapping +from collections.abc import Iterable, Iterator, Mapping +from typing import Final method_descriptor_type: Final = type(object.__dir__) method_wrapper_type: Final = type(object().__ne__) diff --git a/mypy/server/subexpr.py b/mypy/server/subexpr.py index c94db44445dc3..013b936e8b7c9 100644 --- a/mypy/server/subexpr.py +++ b/mypy/server/subexpr.py @@ -28,6 +28,7 @@ StarExpr, TupleExpr, TypeApplication, + TypeFormExpr, UnaryExpr, YieldExpr, YieldFromExpr, @@ -122,6 +123,10 @@ def visit_cast_expr(self, e: CastExpr) -> None: self.add(e) super().visit_cast_expr(e) + def visit_type_form_expr(self, e: TypeFormExpr) -> None: + self.add(e) + super().visit_type_form_expr(e) + def visit_assert_type_expr(self, e: AssertTypeExpr) -> None: self.add(e) super().visit_assert_type_expr(e) diff --git a/mypy/server/update.py b/mypy/server/update.py index 0cc7a2229514d..86ccb57c2a3f0 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -118,7 +118,8 @@ import re import sys import time -from typing import Callable, Final, NamedTuple, Sequence, Union +from collections.abc import Sequence +from typing import Callable, Final, NamedTuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.build import ( @@ -146,11 +147,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 +159,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 +404,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 @@ -630,6 +631,8 @@ def restore(ids: list[str]) -> None: # Find any other modules brought in by imports. changed_modules = [(st.id, st.xpath) for st in new_modules] + for m in new_modules: + manager.import_map[m.id] = set(m.dependencies + m.suppressed) # If there are multiple modules to process, only process one of them and return # the remaining ones to the caller. @@ -667,6 +670,8 @@ def restore(ids: list[str]) -> None: state.type_check_first_pass() state.type_check_second_pass() state.detect_possibly_undefined_vars() + state.generate_unused_ignore_notes() + state.generate_ignore_without_code_notes() t2 = time.time() state.finish_passes() t3 = time.time() @@ -1022,10 +1027,12 @@ def key(node: FineGrainedDeferredNode) -> int: # We seem to need additional passes in fine-grained incremental mode. checker.pass_num = 0 checker.last_pass = 3 - more = checker.check_second_pass(nodes) + # It is tricky to reliably invalidate constructor cache in fine-grained increments. + # See PR 19514 description for details. + more = checker.check_second_pass(nodes, allow_constructor_cache=False) while more: more = False - if graph[module_id].type_checker().check_second_pass(): + if graph[module_id].type_checker().check_second_pass(allow_constructor_cache=False): more = True if manager.options.export_types: @@ -1059,8 +1066,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/sharedparse.py b/mypy/sharedparse.py index ef2e4f7206649..71d1dee8f7d6e 100644 --- a/mypy/sharedparse.py +++ b/mypy/sharedparse.py @@ -10,6 +10,7 @@ "__call__", "__complex__", "__contains__", + "__buffer__", "__del__", "__delattr__", "__delitem__", @@ -31,6 +32,7 @@ "__new__", "__oct__", "__pos__", + "__release_buffer__", "__repr__", "__reversed__", "__setattr__", diff --git a/mypy/solve.py b/mypy/solve.py index 8a1495a9a246b..fbbcac2520ad0 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -3,13 +3,13 @@ from __future__ import annotations from collections import defaultdict -from typing import Iterable, Sequence +from collections.abc import Iterable, Sequence from typing_extensions import TypeAlias as _TypeAlias from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op from mypy.expandtype import expand_type from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort -from mypy.join import join_types +from mypy.join import join_type_list from mypy.meet import meet_type_list, meet_types from mypy.subtypes import is_subtype from mypy.typeops import get_all_type_vars @@ -139,7 +139,7 @@ def solve_with_dependent( * Find dependencies between type variables, group them in SCCs, and sort topologically * Check that all SCC are intrinsically linear, we can't solve (express) T <: List[T] * Variables in leaf SCCs that don't have constant bounds are free (choose one per SCC) - * Solve constraints iteratively starting from leafs, updating bounds after each step. + * Solve constraints iteratively starting from leaves, updating bounds after each step. """ graph, lowers, uppers = transitive_closure(vars, constraints) @@ -247,10 +247,18 @@ def solve_iteratively( return solutions +def _join_sorted_key(t: Type) -> int: + t = get_proper_type(t) + if isinstance(t, UnionType): + return -2 + if isinstance(t, NoneType): + return -1 + return 0 + + def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None: """Solve constraints by finding by using meets of upper bounds, and joins of lower bounds.""" - bottom: Type | None = None - top: Type | None = None + candidate: Type | None = None # Filter out previous results of failed inference, they will only spoil the current pass... @@ -262,24 +270,33 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None: uppers = new_uppers # ...unless this is the only information we have, then we just pass it on. + lowers = list(lowers) if not uppers and not lowers: candidate = UninhabitedType() candidate.ambiguous = True return candidate + bottom: Type | None = None + top: Type | None = None + # Process each bound separately, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint references. - for target in lowers: - if bottom is None: - bottom = target - else: - if type_state.infer_unions: - # This deviates from the general mypy semantics because - # recursive types are union-heavy in 95% of cases. - bottom = UnionType.make_union([bottom, target]) - else: - bottom = join_types(bottom, target) + if type_state.infer_unions and lowers: + # This deviates from the general mypy semantics because + # recursive types are union-heavy in 95% of cases. + # Retain `None` when no bottoms were provided to avoid bogus `Never` inference. + bottom = UnionType.make_union(lowers) + else: + # The order of lowers is non-deterministic. + # We attempt to sort lowers because joins are non-associative. For instance: + # join(join(int, str), int | str) == join(object, int | str) == object + # join(int, join(str, int | str)) == join(int, int | str) == int | str + # Note that joins in theory should be commutative, but in practice some bugs mean this is + # also a source of non-deterministic type checking results. + sorted_lowers = sorted(lowers, key=_join_sorted_key) + if sorted_lowers: + bottom = join_type_list(sorted_lowers) for target in uppers: if top is None: @@ -350,7 +367,7 @@ def test(x: U) -> U: ... # For convenience with current type application machinery, we use a stable # choice that prefers the original type variables (not polymorphic ones) in SCC. - best = sorted(scc, key=lambda x: (x.id not in original_vars, x.id.raw_id))[0] + best = min(scc, key=lambda x: (x.id not in original_vars, x.id.raw_id)) if isinstance(best, TypeVarType): return best.copy_modified(values=values, upper_bound=common_upper_bound) if is_trivial_bound(common_upper_bound_p, allow_tuple=True): diff --git a/mypy/state.py b/mypy/state.py index cd3a360dd15f3..a3055bf6b2085 100644 --- a/mypy/state.py +++ b/mypy/state.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Iterator from contextlib import contextmanager -from typing import Final, Iterator +from typing import Final # These are global mutable state. Don't add anything here unless there's a very # good reason. @@ -24,5 +25,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 9c69a245741b9..e3499d2345635 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -4,8 +4,9 @@ import os from collections import Counter +from collections.abc import Iterator from contextlib import contextmanager -from typing import Final, Iterator +from typing import Final from mypy import nodes from mypy.argmap import map_formals_to_actuals @@ -42,6 +43,7 @@ YieldFromExpr, ) from mypy.traverser import TraverserVisitor +from mypy.type_visitor import ANY_STRATEGY, BoolTypeQuery from mypy.typeanal import collect_all_inner_types from mypy.types import ( AnyType, @@ -51,7 +53,6 @@ TupleType, Type, TypeOfAny, - TypeQuery, TypeVarType, get_proper_type, get_proper_types, @@ -452,9 +453,9 @@ def is_imprecise(t: Type) -> bool: return t.accept(HasAnyQuery()) -class HasAnyQuery(TypeQuery[bool]): +class HasAnyQuery(BoolTypeQuery): def __init__(self) -> None: - super().__init__(any) + super().__init__(ANY_STRATEGY) def visit_any(self, t: AnyType) -> bool: return not is_special_form_any(t) diff --git a/mypy/strconv.py b/mypy/strconv.py index a96a27c45d758..168a8bcffdc7e 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -4,7 +4,8 @@ import os import re -from typing import TYPE_CHECKING, Any, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any import mypy.nodes from mypy.options import Options @@ -112,7 +113,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) @@ -212,6 +213,12 @@ def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl) -> str: def visit_decorator(self, o: mypy.nodes.Decorator) -> str: return self.dump([o.var, o.decorators, o.func], o) + def visit_type_alias(self, o: mypy.nodes.TypeAlias, /) -> str: + return self.dump([o.name, o.target, o.alias_tvars, o.no_args], o) + + def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode, /) -> str: + return self.dump([o.fullname], o) + # Statements def visit_block(self, o: mypy.nodes.Block) -> str: @@ -349,6 +356,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 @@ -385,7 +394,9 @@ def visit_name_expr(self, o: mypy.nodes.NameExpr) -> str: o.name, o.kind, o.fullname, o.is_inferred_def or o.is_special_form, o.node ) if isinstance(o.node, mypy.nodes.Var) and o.node.is_final: - pretty += f" = {o.node.final_value}" + final_value = o.node.final_value + if final_value is not None: + pretty += f" = {o.node.final_value}" return short_type(o) + "(" + pretty + ")" def pretty_name( @@ -461,6 +472,9 @@ def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> str: def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> str: return self.dump([o.expr, o.type], o) + def visit_type_form_expr(self, o: mypy.nodes.TypeFormExpr) -> str: + return self.dump([o.type], o) + def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> str: return self.dump([o.expr, o.type], o) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 928d024514f30..89db6cb3378fd 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -11,16 +11,17 @@ import keyword import re import tokenize -from typing import Any, Final, MutableMapping, MutableSequence, NamedTuple, Sequence, Tuple +from collections.abc import MutableMapping, MutableSequence, Sequence +from typing import Any, Final, NamedTuple from typing_extensions import TypeAlias as _TypeAlias import mypy.util # Type alias for signatures strings in format ('func_name', '(arg, opt_arg=False)'). -Sig: _TypeAlias = Tuple[str, str] +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 +77,8 @@ class FunctionSig(NamedTuple): name: str args: list[ArgSig] ret_type: str | None + type_args: str = "" # TODO implement in stubgenc and remove the default + docstring: str | None = None def is_special_method(self) -> bool: return bool( @@ -108,6 +111,7 @@ def format_sig( is_async: bool = False, any_val: str | None = None, docstring: str | None = None, + include_docstrings: bool = False, ) -> str: args: list[str] = [] for arg in self.args: @@ -141,11 +145,12 @@ 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 - ) - if docstring: - suffix = f"\n{indent} {mypy.util.quote_docstring(docstring)}" + sig = f"{indent}{prefix}def {self.name}{self.type_args}({', '.join(args)}){retfield}:" + # if this object has a docstring it's probably produced by a SignatureGenerator, so it + # takes precedence over the passed docstring, which acts as a fallback. + doc = (self.docstring or docstring) if include_docstrings else None + if doc: + suffix = f"\n{indent} {mypy.util.quote_docstring(doc)}" else: suffix = " ..." return f"{sig}{suffix}" @@ -175,6 +180,8 @@ def __init__(self, function_name: str) -> None: self.ret_type = "Any" self.found = False self.args: list[ArgSig] = [] + self.pos_only: int | None = None + self.keyword_only: int | None = None # Valid signatures found so far. self.signatures: list[FunctionSig] = [] @@ -252,15 +259,34 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.arg_type = self.accumulator self.state.pop() elif self.state[-1] == STATE_ARGUMENT_LIST: - self.arg_name = self.accumulator - if not ( - token.string == ")" and self.accumulator.strip() == "" - ) and not _ARG_NAME_RE.match(self.arg_name): - # Invalid argument name. - self.reset() - return + if self.accumulator == "*": + if self.keyword_only is not None: + # Error condition: cannot have * twice + self.reset() + return + self.keyword_only = len(self.args) + self.accumulator = "" + else: + if self.accumulator.startswith("*"): + self.keyword_only = len(self.args) + 1 + self.arg_name = self.accumulator + if not ( + token.string == ")" and self.accumulator.strip() == "" + ) and not _ARG_NAME_RE.match(self.arg_name): + # Invalid argument name. + self.reset() + return if token.string == ")": + if ( + self.state[-1] == STATE_ARGUMENT_LIST + and self.keyword_only is not None + and self.keyword_only == len(self.args) + and not self.arg_name + ): + # Error condition: * must be followed by arguments + self.reset() + return self.state.pop() # arg_name is empty when there are no args. e.g. func() @@ -280,6 +306,22 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.arg_type = None self.arg_default = None self.accumulator = "" + elif ( + token.type == tokenize.OP + and token.string == "/" + and self.state[-1] == STATE_ARGUMENT_LIST + ): + if token.string == "/": + if self.pos_only is not None or self.keyword_only is not None or not self.args: + # Error cases: + # - / shows up more than once + # - / shows up after * + # - / shows up before any arguments + self.reset() + return + self.pos_only = len(self.args) + self.state.append(STATE_ARGUMENT_TYPE) + self.accumulator = "" elif token.type == tokenize.OP and token.string == "->" and self.state[-1] == STATE_INIT: self.accumulator = "" diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 02c0c1e58ab5e..60fbd7f43c0fd 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -47,7 +47,8 @@ import os.path import sys import traceback -from typing import Final, Iterable, Iterator +from collections.abc import Iterable, Iterator +from typing import Final import mypy.build import mypy.mixedtraverser @@ -77,40 +78,51 @@ Block, BytesExpr, CallExpr, + CastExpr, ClassDef, ComparisonExpr, ComplexExpr, + ConditionalExpr, Decorator, DictExpr, + DictionaryComprehension, EllipsisExpr, Expression, ExpressionStmt, FloatExpr, FuncBase, FuncDef, + GeneratorExpr, IfStmt, Import, ImportAll, ImportFrom, IndexExpr, IntExpr, + LambdaExpr, + ListComprehension, ListExpr, MemberExpr, MypyFile, NameExpr, OpExpr, OverloadedFuncDef, + SetComprehension, SetExpr, + SliceExpr, StarExpr, Statement, StrExpr, TempNode, TupleExpr, + TypeAliasStmt, TypeInfo, UnaryExpr, Var, ) from mypy.options import Options as MypyOptions +from mypy.plugins.dataclasses import DATACLASS_FIELD_SPECIFIERS +from mypy.semanal_shared import find_dataclass_transform_spec from mypy.sharedparse import MAGIC_METHODS_POS_ARGS_ONLY from mypy.stubdoc import ArgSig, FunctionSig from mypy.stubgenc import InspectionStubGenerator, generate_stub_for_c_module @@ -137,8 +149,10 @@ has_yield_from_expression, ) from mypy.types import ( + DATACLASS_TRANSFORM_NAMES, OVERLOAD_NAMES, TPDICT_NAMES, + TYPE_VAR_LIKE_NAMES, TYPED_NAMEDTUPLE_NAMES, AnyType, CallableType, @@ -338,15 +352,19 @@ def visit_index_expr(self, node: IndexExpr) -> str: base = node.base.accept(self) index = node.index.accept(self) if len(index) > 2 and index.startswith("(") and index.endswith(")"): - index = index[1:-1] + index = index[1:-1].rstrip(",") return f"{base}[{index}]" def visit_tuple_expr(self, node: TupleExpr) -> str: - return f"({', '.join(n.accept(self) for n in node.items)})" + suffix = "," if len(node.items) == 1 else "" + return f"({', '.join(n.accept(self) for n in node.items)}{suffix})" def visit_list_expr(self, node: ListExpr) -> str: return f"[{', '.join(n.accept(self) for n in node.items)}]" + def visit_set_expr(self, node: SetExpr) -> str: + return f"{{{', '.join(n.accept(self) for n in node.items)}}}" + def visit_dict_expr(self, o: DictExpr) -> str: dict_items = [] for key, value in o.items: @@ -361,9 +379,50 @@ def visit_ellipsis(self, node: EllipsisExpr) -> str: def visit_op_expr(self, o: OpExpr) -> str: return f"{o.left.accept(self)} {o.op} {o.right.accept(self)}" + def visit_unary_expr(self, o: UnaryExpr, /) -> str: + return f"{o.op}{o.expr.accept(self)}" + + def visit_slice_expr(self, o: SliceExpr, /) -> str: + blocks = [ + o.begin_index.accept(self) if o.begin_index is not None else "", + o.end_index.accept(self) if o.end_index is not None else "", + ] + if o.stride is not None: + blocks.append(o.stride.accept(self)) + return ":".join(blocks) + def visit_star_expr(self, o: StarExpr) -> str: return f"*{o.expr.accept(self)}" + def visit_lambda_expr(self, o: LambdaExpr) -> str: + # TODO: Required for among other things dataclass.field default_factory + return self.stubgen.add_name("_typeshed.Incomplete") + + def _visit_unsupported_expr(self, o: object) -> str: + # Something we do not understand. + return self.stubgen.add_name("_typeshed.Incomplete") + + def visit_comparison_expr(self, o: ComparisonExpr) -> str: + return self._visit_unsupported_expr(o) + + def visit_cast_expr(self, o: CastExpr) -> str: + return self._visit_unsupported_expr(o) + + def visit_conditional_expr(self, o: ConditionalExpr) -> str: + return self._visit_unsupported_expr(o) + + def visit_list_comprehension(self, o: ListComprehension) -> str: + return self._visit_unsupported_expr(o) + + def visit_set_comprehension(self, o: SetComprehension) -> str: + return self._visit_unsupported_expr(o) + + def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> str: + return self._visit_unsupported_expr(o) + + def visit_generator_expr(self, o: GeneratorExpr) -> str: + return self._visit_unsupported_expr(o) + def find_defined_names(file: MypyFile) -> set[str]: finder = DefinitionFinder() @@ -398,6 +457,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() @@ -468,13 +530,18 @@ 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 self.method_names: set[str] = set() self.processing_enum = False self.processing_dataclass = False + self.dataclass_field_specifier: tuple[str, ...] = () + + @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 @@ -507,7 +574,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] = [] @@ -544,7 +612,7 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: default = "..." if arg_.initializer: if not typename: - typename = self.get_str_type_of_node(arg_.initializer, True, False) + typename = self.get_str_type_of_node(arg_.initializer, can_be_incomplete=False) potential_default, valid = self.get_str_default_of_node(arg_.initializer) if valid and len(potential_default) <= 200: default = potential_default @@ -565,6 +633,11 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: new_args = infer_method_arg_types( ctx.name, ctx.class_info.self_var, [arg.name for arg in args] ) + + if ctx.name == "__exit__": + self.import_tracker.add_import("types") + self.import_tracker.require_name("types") + if new_args is not None: args = new_args @@ -624,8 +697,8 @@ def visit_func_def(self, o: FuncDef) -> None: is_dataclass_generated = ( self.analyzed and self.processing_dataclass and o.info.names[o.name].plugin_generated ) - if is_dataclass_generated and o.name != "__init__": - # Skip methods generated by the @dataclass decorator (except for __init__) + if is_dataclass_generated: + # Skip methods generated by the @dataclass decorator return if ( self.is_private_name(o.name, o.fullname) @@ -638,20 +711,22 @@ def visit_func_def(self, o: FuncDef) -> None: self.add("\n") if not self.is_top_level(): self_inits = find_self_initializers(o) - for init, value in self_inits: + for init, value, annotation in self_inits: if init in self.method_names: # Can't have both an attribute and a method/property with the same name. continue - init_code = self.get_init(init, value) + init_code = self.get_init(init, value, annotation) 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 @@ -689,10 +764,13 @@ def process_decorator(self, o: Decorator) -> None: """ o.func.is_overload = False for decorator in o.original_decorators: - if not isinstance(decorator, (NameExpr, MemberExpr)): + d = decorator + if isinstance(d, CallExpr): + d = d.callee + if not isinstance(d, (NameExpr, MemberExpr)): continue - qualname = get_qualified_name(decorator) - fullname = self.get_fullname(decorator) + qualname = get_qualified_name(d) + fullname = self.get_fullname(d) if fullname in ( "builtins.property", "builtins.staticmethod", @@ -727,6 +805,12 @@ def process_decorator(self, o: Decorator) -> None: o.func.is_overload = True elif qualname.endswith((".setter", ".deleter")): self.add_decorator(qualname, require_name=False) + elif fullname in DATACLASS_TRANSFORM_NAMES: + p = AliasPrinter(self) + self._decorators.append(f"@{decorator.accept(p)}") + elif isinstance(decorator, (NameExpr, MemberExpr)): + p = AliasPrinter(self) + self._decorators.append(f"@{decorator.accept(p)}") def get_fullname(self, expr: Expression) -> str: """Return the expression's full name.""" @@ -741,7 +825,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: @@ -765,13 +849,17 @@ 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) self.add(f"{self._indent}{docstring}\n") n = len(self._output) self._vars.append([]) + if self.analyzed and (spec := find_dataclass_transform_spec(o)): + self.processing_dataclass = True + self.dataclass_field_specifier = spec.field_specifiers super().visit_class_def(o) self.dedent() self._vars.pop() @@ -786,8 +874,9 @@ def visit_class_def(self, o: ClassDef) -> None: self._state = CLASS self.method_names = set() self.processing_dataclass = False + self.dataclass_field_specifier = () + 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.""" @@ -841,6 +930,9 @@ def get_class_decorators(self, cdef: ClassDef) -> list[str]: decorators.append(d.accept(p)) self.import_tracker.require_name(get_qualified_name(d)) self.processing_dataclass = True + if self.is_dataclass_transform(d): + decorators.append(d.accept(p)) + self.import_tracker.require_name(get_qualified_name(d)) return decorators def is_dataclass(self, expr: Expression) -> bool: @@ -848,6 +940,17 @@ def is_dataclass(self, expr: Expression) -> bool: expr = expr.callee return self.get_fullname(expr) == "dataclasses.dataclass" + def is_dataclass_transform(self, expr: Expression) -> bool: + if isinstance(expr, CallExpr): + expr = expr.callee + if self.get_fullname(expr) in DATACLASS_TRANSFORM_NAMES: + return True + if (spec := find_dataclass_transform_spec(expr)) is not None: + self.processing_dataclass = True + self.dataclass_field_specifier = spec.field_specifiers + return True + return False + def visit_block(self, o: Block) -> None: # Unreachable statements may be partially uninitialized and that may # cause trouble. @@ -869,13 +972,20 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: continue if ( isinstance(lvalue, NameExpr) - and not self.is_private_name(lvalue.name) - # it is never an alias with explicit annotation - and not o.unanalyzed_type and self.is_alias_expression(o.rvalue) + and not self.is_private_name(lvalue.name) ): - self.process_typealias(lvalue, o.rvalue) - continue + is_explicit_type_alias = ( + o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" + ) + if is_explicit_type_alias: + self.process_typealias(lvalue, o.rvalue, is_explicit_type_alias=True) + continue + + if not o.unanalyzed_type: + self.process_typealias(lvalue, o.rvalue) + continue + if isinstance(lvalue, (TupleExpr, ListExpr)): items = lvalue.items if isinstance(o.unanalyzed_type, TupleType): # type: ignore[misc] @@ -1040,14 +1150,7 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: or module alias. """ # Assignment of TypeVar(...) and other typevar-likes are passed through - if isinstance(expr, CallExpr) and self.get_fullname(expr.callee) in ( - "typing.TypeVar", - "typing_extensions.TypeVar", - "typing.ParamSpec", - "typing_extensions.ParamSpec", - "typing.TypeVarTuple", - "typing_extensions.TypeVarTuple", - ): + if isinstance(expr, CallExpr) and self.get_fullname(expr.callee) in TYPE_VAR_LIKE_NAMES: return True elif isinstance(expr, EllipsisExpr): return not top_level @@ -1095,12 +1198,28 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: else: return False - def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None: + def process_typealias( + self, lvalue: NameExpr, rvalue: Expression, is_explicit_type_alias: bool = False + ) -> None: p = AliasPrinter(self) - self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") + if is_explicit_type_alias: + self.import_tracker.require_name("TypeAlias") + self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n") + else: + self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") 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] @@ -1213,8 +1332,14 @@ def get_assign_initializer(self, rvalue: Expression) -> str: and not isinstance(rvalue, TempNode) ): return " = ..." - if self.processing_dataclass and not (isinstance(rvalue, TempNode) and rvalue.no_rhs): - return " = ..." + if self.processing_dataclass: + if isinstance(rvalue, CallExpr): + fullname = self.get_fullname(rvalue.callee) + if fullname in (self.dataclass_field_specifier or DATACLASS_FIELD_SPECIFIERS): + p = AliasPrinter(self) + return f" = {rvalue.accept(p)}" + if not (isinstance(rvalue, TempNode) and rvalue.no_rhs): + return " = ..." # TODO: support other possible cases, where initializer is important # By default, no initializer is required: @@ -1232,9 +1357,7 @@ def is_private_member(self, fullname: str) -> bool: parts = fullname.split(".") return any(self.is_private_name(part) for part in parts) - def get_str_type_of_node( - self, rvalue: Expression, can_infer_optional: bool = False, can_be_any: bool = True - ) -> str: + def get_str_type_of_node(self, rvalue: Expression, *, can_be_incomplete: bool = True) -> str: rvalue = self.maybe_unwrap_unary_expr(rvalue) if isinstance(rvalue, IntExpr): @@ -1254,9 +1377,7 @@ def get_str_type_of_node( return "complex" if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"): return "bool" - if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None": - return f"{self.add_name('_typeshed.Incomplete')} | None" - if can_be_any: + if can_be_incomplete: return self.add_name("_typeshed.Incomplete") else: return "" @@ -1391,7 +1512,7 @@ def find_method_names(defs: list[Statement]) -> set[str]: class SelfTraverser(mypy.traverser.TraverserVisitor): def __init__(self) -> None: - self.results: list[tuple[str, Expression]] = [] + self.results: list[tuple[str, Expression, Type | None]] = [] def visit_assignment_stmt(self, o: AssignmentStmt) -> None: lvalue = o.lvalues[0] @@ -1400,10 +1521,10 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: and isinstance(lvalue.expr, NameExpr) and lvalue.expr.name == "self" ): - self.results.append((lvalue.name, o.rvalue)) + self.results.append((lvalue.name, o.rvalue, o.unanalyzed_type)) -def find_self_initializers(fdef: FuncBase) -> list[tuple[str, Expression]]: +def find_self_initializers(fdef: FuncBase) -> list[tuple[str, Expression, Type | None]]: """Find attribute initializers in a method. Return a list of pairs (attribute name, r.h.s. expression). @@ -1444,9 +1565,7 @@ def is_blacklisted_path(path: str) -> bool: def normalize_path_separators(path: str) -> str: - if sys.platform == "win32": - return path.replace("\\", "/") - return path + return path.replace("\\", "/") if sys.platform == "win32" else path def collect_build_targets( @@ -1502,7 +1621,7 @@ def find_module_paths_using_imports( except CantImport as e: tb = traceback.format_exc() if verbose: - sys.stdout.write(tb) + sys.stderr.write(tb) if not quiet: report_missing(mod, e.message, tb) continue @@ -1777,7 +1896,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", diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 7ab500b4fe120..e0e063927aadb 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -6,13 +6,15 @@ from __future__ import annotations +import enum import glob import importlib import inspect import keyword import os.path +from collections.abc import Mapping from types import FunctionType, ModuleType -from typing import Any, Callable, Mapping +from typing import Any, Callable from mypy.fastparse import parse_type_comment from mypy.moduleinspect import is_c_module @@ -36,6 +38,7 @@ infer_method_arg_types, infer_method_ret_type, ) +from mypy.util import quote_docstring class ExternalSignatureGenerator(SignatureGenerator): @@ -202,7 +205,7 @@ def _from_sigs(cls, sigs: list[FunctionSig], is_abstract: bool = False) -> CFunc sigs[0].name, "\n".join(sig.format_sig()[:-4] for sig in sigs), is_abstract ) - def __get__(self) -> None: + def __get__(self) -> None: # noqa: PLE0302 """ This exists to make this object look like a method descriptor and thus return true for CStubGenerator.ismethod() @@ -210,6 +213,9 @@ def __get__(self) -> None: pass +_Missing = enum.Enum("_Missing", "VALUE") + + class InspectionStubGenerator(BaseStubGenerator): """Stub generator that does not parse code. @@ -241,7 +247,7 @@ def __init__( self.module_name = module_name if self.is_c_module: # Add additional implicit imports. - # C-extensions are given more lattitude since they do not import the typing module. + # C-extensions are given more latitude since they do not import the typing module. self.known_imports.update( { "typing": [ @@ -309,14 +315,14 @@ def get_annotation(key: str) -> str | None: # Add the arguments to the signature def add_args( - args: list[str], get_default_value: Callable[[int, str], object | None] + args: list[str], get_default_value: Callable[[int, str], object | _Missing] ) -> 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 default_value is not _Missing.VALUE: if arg in annotations: - argtype = annotations[arg] + argtype = get_annotation(arg) else: argtype = self.get_type_annotation(default_value) if argtype == "None": @@ -329,26 +335,26 @@ def add_args( else: arglist.append(ArgSig(arg, get_annotation(arg), default=False)) - def get_pos_default(i: int, _arg: str) -> Any | None: + def get_pos_default(i: int, _arg: str) -> Any | _Missing: if defaults and i >= len(args) - len(defaults): return defaults[i - (len(args) - len(defaults))] else: - return None + return _Missing.VALUE 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 "*" + # if we have keyword only args, then we need to add "*" elif kwonlyargs: arglist.append(ArgSig("*")) - def get_kw_default(_i: int, arg: str) -> Any | None: - if kwonlydefaults: - return kwonlydefaults.get(arg) + def get_kw_default(_i: int, arg: str) -> Any | _Missing: + if kwonlydefaults and arg in kwonlydefaults: + return kwonlydefaults[arg] else: - return None + return _Missing.VALUE add_args(kwonlyargs, get_kw_default) @@ -644,8 +650,7 @@ def generate_function_stub( if inferred[0].args and inferred[0].args[0].name == "cls": decorators.append("@classmethod") - if docstring: - docstring = self._indent_docstring(docstring) + docstring = self._indent_docstring(ctx.docstring) if ctx.docstring else None output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring)) self._fix_iter(ctx, inferred, output) @@ -749,9 +754,14 @@ def generate_property_stub( ) else: # regular property if readonly: + docstring = self._indent_docstring(ctx.docstring) if ctx.docstring else None ro_properties.append(f"{self._indent}@property") - sig = FunctionSig(name, [ArgSig("self")], inferred_type) - ro_properties.append(sig.format_sig(indent=self._indent)) + sig = FunctionSig(name, [ArgSig("self")], inferred_type, docstring=docstring) + ro_properties.append( + sig.format_sig( + indent=self._indent, include_docstrings=self._include_docstrings + ) + ) else: if inferred_type is None: inferred_type = self.add_name("_typeshed.Incomplete") @@ -760,11 +770,15 @@ def generate_property_stub( def get_type_fullname(self, typ: type) -> str: """Given a type, return a string representation""" - if typ is Any: # type: ignore[comparison-overlap] + if typ is Any: return "Any" typename = getattr(typ, "__qualname__", typ.__name__) module_name = self.get_obj_module(typ) - assert module_name is not None, typ + if module_name is None: + # This should not normally happen, but some types may resist our + # introspection attempts too hard. See + # https://github.com/python/mypy/issues/19031 + return "_typeshed.Incomplete" if module_name != "builtins": typename = f"{module_name}.{typename}" return typename @@ -787,7 +801,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 @@ -808,7 +824,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 @@ -843,7 +861,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)) @@ -862,8 +880,17 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> bases_str = "(%s)" % ", ".join(bases) else: bases_str = "" - if types or static_properties or rw_properties or methods or ro_properties: + + if class_info.docstring and self._include_docstrings: + doc = quote_docstring(self._indent_docstring(class_info.docstring)) + doc = f" {self._indent}{doc}" + docstring = doc.splitlines(keepends=False) + else: + docstring = [] + + if docstring or types or static_properties or rw_properties or methods or ro_properties: output.append(f"{self._indent}class {class_name}{bases_str}:") + output.extend(docstring) for line in types: if ( output @@ -873,14 +900,10 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> ): output.append("") output.append(line) - for line in static_properties: - output.append(line) - for line in rw_properties: - output.append(line) - for line in methods: - output.append(line) - for line in ro_properties: - output.append(line) + output.extend(static_properties) + output.extend(rw_properties) + output.extend(methods) + output.extend(ro_properties) else: output.append(f"{self._indent}class {class_name}{bases_str}: ...") diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 9d8dfbe43f37a..42e53ba21c847 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -1,61 +1,63 @@ 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", "cachetools": "types-cachetools", "click_spinner": "types-click-spinner", - "contextvars": "types-contextvars", "croniter": "types-croniter", - "dataclasses": "types-dataclasses", "dateparser": "types-dateparser", "dateutil": "types-python-dateutil", "decorator": "types-decorator", "deprecated": "types-Deprecated", "docutils": "types-docutils", "first": "types-first", - "gflags": "types-python-gflags", - "google.protobuf": "types-protobuf", "markdown": "types-Markdown", "mock": "types-mock", - "OpenSSL": "types-pyOpenSSL", "paramiko": "types-paramiko", - "pkg_resources": "types-setuptools", "polib": "types-polib", "pycurl": "types-pycurl", "pymysql": "types-PyMySQL", "pyrfc3339": "types-pyRFC3339", - "python2": "types-six", "pytz": "types-pytz", - "pyVmomi": "types-pyvmomi", - "redis": "types-redis", "requests": "types-requests", "retry": "types-retry", "simplejson": "types-simplejson", "singledispatch": "types-singledispatch", "six": "types-six", - "slugify": "types-python-slugify", "tabulate": "types-tabulate", "toml": "types-toml", - "typed_ast": "types-typed-ast", - "tzlocal": "types-tzlocal", "ujson": "types-ujson", "waitress": "types-waitress", "yaml": "types-PyYAML", @@ -66,108 +68,251 @@ 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 = { - "MySQLdb": "types-mysqlclient", - "PIL": "types-Pillow", - "PyInstaller": "types-pyinstaller", - "Xlib": "types-python-xlib", +non_bundled_packages_flat: dict[str, str] = { + "_cffi_backend": "types-cffi", + "_jsonnet": "types-jsonnet", + "_win32typing": "types-pywin32", + "antlr4": "types-antlr4-python3-runtime", + "assertpy": "types-assertpy", + "auth0": "types-auth0-python", + "authlib": "types-Authlib", "aws_xray_sdk": "types-aws-xray-sdk", - "babel": "types-babel", - "backports.ssl_match_hostname": "types-backports.ssl_match_hostname", + "binaryornot": "types-binaryornot", + "boltons": "types-boltons", "braintree": "types-braintree", - "bs4": "types-beautifulsoup4", "bugbear": "types-flake8-bugbear", - "caldav": "types-caldav", + "capturer": "types-capturer", "cffi": "types-cffi", + "channels": "types-channels", "chevron": "types-chevron", + "click_default_group": "types-click-default-group", + "click_log": "types-click-log", + "click_shell": "types-click-shell", + "click_web": "types-click-web", "colorama": "types-colorama", - "commonmark": "types-commonmark", + "commctrl": "types-pywin32", "consolemenu": "types-console-menu", + "convertdate": "types-convertdate", + "cronlog": "types-python-crontab", "crontab": "types-python-crontab", - "d3dshot": "types-D3DShot", + "crontabs": "types-python-crontab", + "dateparser_data": "types-dateparser", + "dde": "types-pywin32", + "defusedxml": "types-defusedxml", + "dirhash": "types-dirhash", + "django_filters": "types-django-filter", + "docker": "types-docker", "dockerfile_parse": "types-dockerfile-parse", - "docopt": "types-docopt", "editdistance": "types-editdistance", "entrypoints": "types-entrypoints", + "exifread": "types-ExifRead", + "fanstatic": "types-fanstatic", "farmhash": "types-pyfarmhash", - "flake8_2020": "types-flake8-2020", "flake8_builtins": "types-flake8-builtins", "flake8_docstrings": "types-flake8-docstrings", - "flake8_plugin_utils": "types-flake8-plugin-utils", "flake8_rst_docstrings": "types-flake8-rst-docstrings", "flake8_simplify": "types-flake8-simplify", "flake8_typing_imports": "types-flake8-typing-imports", + "flake8": "types-flake8", "flask_cors": "types-Flask-Cors", "flask_migrate": "types-Flask-Migrate", + "flask_socketio": "types-Flask-SocketIO", "fpdf": "types-fpdf2", "gdb": "types-gdb", - "google.cloud.ndb": "types-google-cloud-ndb", + "geopandas": "types-geopandas", + "gevent": "types-gevent", + "greenlet": "types-greenlet", + "grpc_channelz": "types-grpcio-channelz", + "grpc_health": "types-grpcio-health-checking", + "grpc_reflection": "types-grpcio-reflection", + "grpc_status": "types-grpcio-status", + "grpc": "types-grpcio", "hdbcli": "types-hdbcli", + "hnswlib": "types-hnswlib", "html5lib": "types-html5lib", "httplib2": "types-httplib2", - "humanfriendly": "types-humanfriendly", - "invoke": "types-invoke", + "hvac": "types-hvac", + "ibm_db": "types-ibm-db", + "icalendar": "types-icalendar", + "import_export": "types-django-import-export", + "inifile": "types-inifile", + "isapi": "types-pywin32", "jack": "types-JACK-Client", + "jenkins": "types-python-jenkins", + "Jetson": "types-Jetson.GPIO", + "jks": "types-pyjks", "jmespath": "types-jmespath", "jose": "types-python-jose", "jsonschema": "types-jsonschema", + "jwcrypto": "types-jwcrypto", "keyboard": "types-keyboard", "ldap3": "types-ldap3", + "lunardate": "types-lunardate", + "lupa": "types-lupa", + "lzstring": "types-lzstring", + "m3u8": "types-m3u8", + "management": "types-django-import-export", + "mmapfile": "types-pywin32", + "mmsystem": "types-pywin32", + "mypy_extensions": "types-mypy-extensions", + "MySQLdb": "types-mysqlclient", + "nanoid": "types-nanoid", + "nanoleafapi": "types-nanoleafapi", + "netaddr": "types-netaddr", + "netifaces": "types-netifaces", + "networkx": "types-networkx", "nmap": "types-python-nmap", + "ntsecuritycon": "types-pywin32", "oauthlib": "types-oauthlib", + "objgraph": "types-objgraph", + "odbc": "types-pywin32", + "olefile": "types-olefile", "openpyxl": "types-openpyxl", "opentracing": "types-opentracing", - "paho.mqtt": "types-paho-mqtt", "parsimonious": "types-parsimonious", "passlib": "types-passlib", "passpy": "types-passpy", "peewee": "types-peewee", "pep8ext_naming": "types-pep8-naming", - "playsound": "types-playsound", + "perfmon": "types-pywin32", + "pexpect": "types-pexpect", + "playhouse": "types-peewee", + "pony": "types-pony", + "portpicker": "types-portpicker", "psutil": "types-psutil", "psycopg2": "types-psycopg2", + "pyasn1": "types-pyasn1", "pyaudio": "types-pyaudio", "pyautogui": "types-PyAutoGUI", "pycocotools": "types-pycocotools", "pyflakes": "types-pyflakes", "pygments": "types-Pygments", "pyi_splash": "types-pyinstaller", + "PyInstaller": "types-pyinstaller", + "pyluach": "types-pyluach", + "pymeeus": "types-PyMeeus", "pynput": "types-pynput", - "pythoncom": "types-pywin32", - "pythonwin": "types-pywin32", + "pyperclip": "types-pyperclip", "pyscreeze": "types-PyScreeze", "pysftp": "types-pysftp", "pytest_lazyfixture": "types-pytest-lazy-fixture", + "python_http_client": "types-python-http-client", + "pythoncom": "types-pywin32", + "pythonwin": "types-pywin32", "pywintypes": "types-pywin32", + "qrbill": "types-qrbill", + "qrcode": "types-qrcode", + "ratelimit": "types-ratelimit", "regex": "types-regex", + "regutil": "types-pywin32", + "reportlab": "types-reportlab", + "requests_oauthlib": "types-requests-oauthlib", + "rfc3339_validator": "types-rfc3339-validator", + "RPi": "types-RPi.GPIO", + "s2clientprotocol": "types-s2clientprotocol", + "sass": "types-libsass", + "sassutils": "types-libsass", + "seaborn": "types-seaborn", "send2trash": "types-Send2Trash", + "serial": "types-pyserial", + "servicemanager": "types-pywin32", + "setuptools": "types-setuptools", + "shapely": "types-shapely", "slumber": "types-slumber", - "stdlib_list": "types-stdlib-list", - "stripe": "types-stripe", + "socks": "types-PySocks", + "sockshandler": "types-PySocks", + "sspicon": "types-pywin32", + "str2bool": "types-str2bool", + "tensorflow": "types-tensorflow", + "tgcrypto": "types-TgCrypto", + "timer": "types-pywin32", "toposort": "types-toposort", "tqdm": "types-tqdm", - "tree_sitter": "types-tree-sitter", - "tree_sitter_languages": "types-tree-sitter-languages", + "translationstring": "types-translationstring", "ttkthemes": "types-ttkthemes", + "unidiff": "types-unidiff", + "untangle": "types-untangle", + "usersettings": "types-usersettings", + "uwsgi": "types-uWSGI", + "uwsgidecorators": "types-uWSGI", "vobject": "types-vobject", + "watchpoints": "types-watchpoints", + "webob": "types-WebOb", "whatthepatch": "types-whatthepatch", + "win2kras": "types-pywin32", "win32": "types-pywin32", "win32api": "types-pywin32", - "win32con": "types-pywin32", + "win32clipboard": "types-pywin32", "win32com": "types-pywin32", "win32comext": "types-pywin32", + "win32con": "types-pywin32", + "win32console": "types-pywin32", + "win32cred": "types-pywin32", + "win32crypt": "types-pywin32", + "win32cryptcon": "types-pywin32", + "win32event": "types-pywin32", + "win32evtlog": "types-pywin32", + "win32evtlogutil": "types-pywin32", + "win32file": "types-pywin32", + "win32gui_struct": "types-pywin32", "win32gui": "types-pywin32", + "win32help": "types-pywin32", + "win32inet": "types-pywin32", + "win32inetcon": "types-pywin32", + "win32job": "types-pywin32", + "win32lz": "types-pywin32", + "win32net": "types-pywin32", + "win32netcon": "types-pywin32", + "win32pdh": "types-pywin32", + "win32pdhquery": "types-pywin32", + "win32pipe": "types-pywin32", + "win32print": "types-pywin32", + "win32process": "types-pywin32", + "win32profile": "types-pywin32", + "win32ras": "types-pywin32", + "win32security": "types-pywin32", + "win32service": "types-pywin32", + "win32serviceutil": "types-pywin32", + "win32timezone": "types-pywin32", + "win32trace": "types-pywin32", + "win32transaction": "types-pywin32", + "win32ts": "types-pywin32", + "win32ui": "types-pywin32", + "win32uiole": "types-pywin32", + "win32verstamp": "types-pywin32", + "win32wnet": "types-pywin32", + "winerror": "types-pywin32", + "winioctlcon": "types-pywin32", + "winnt": "types-pywin32", + "winperf": "types-pywin32", + "winxpgui": "types-pywin32", + "winxptheme": "types-pywin32", + "workalendar": "types-workalendar", + "wtforms": "types-WTForms", + "wurlitzer": "types-wurlitzer", + "xdg": "types-pyxdg", + "xdgenvpy": "types-xdgenvpy", + "Xlib": "types-python-xlib", + "xlrd": "types-xlrd", "xmltodict": "types-xmltodict", + "yt_dlp": "types-yt-dlp", + "zstd": "types-zstd", "zxcvbn": "types-zxcvbn", # Stub packages that are not from typeshed # Since these can be installed automatically via --install-types, we have a high trust bar # for additions here "pandas": "pandas-stubs", # https://github.com/pandas-dev/pandas-stubs "lxml": "lxml-stubs", # https://github.com/lxml/lxml-stubs + "scipy": "scipy-stubs", # https://github.com/scipy/scipy-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 c54f83f33b007..ada56a2489fe1 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -17,6 +17,7 @@ import os import pkgutil import re +import struct import symtable import sys import traceback @@ -25,13 +26,17 @@ import typing_extensions import warnings from collections import defaultdict +from collections.abc import Iterator, Set as AbstractSet from contextlib import redirect_stderr, redirect_stdout from functools import singledispatch from pathlib import Path -from typing import AbstractSet, Any, Generic, Iterator, TypeVar, Union +from typing import Any, Final, Generic, TypeVar, Union from typing_extensions import get_origin, is_typeddict import mypy.build +import mypy.checkexpr +import mypy.checkmember +import mypy.erasetype import mypy.modulefinder import mypy.nodes import mypy.state @@ -51,7 +56,7 @@ def __repr__(self) -> str: return "MISSING" -MISSING: typing_extensions.Final = Missing() +MISSING: Final = Missing() T = TypeVar("T") MaybeMissing: typing_extensions.TypeAlias = Union[T, Missing] @@ -64,10 +69,10 @@ def __repr__(self) -> str: return "" -UNREPRESENTABLE: typing_extensions.Final = Unrepresentable() +UNREPRESENTABLE: Final = Unrepresentable() -_formatter: typing_extensions.Final = FancyFormatter(sys.stdout, sys.stderr, False) +_formatter: Final = FancyFormatter(sys.stdout, sys.stderr, False) def _style(message: str, **kwargs: Any) -> str: @@ -133,7 +138,12 @@ 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 is_disjoint_base_related(self) -> bool: + """Whether or not the error is related to @disjoint_base.""" + # TODO: This is hacky, use error codes or something more resilient + return "@disjoint_base" in self.message def get_description(self, concise: bool = False) -> str: """Returns a description of the error. @@ -337,7 +347,8 @@ def verify_mypyfile( yield Error(object_path, "is not present at runtime", stub, runtime) return if not isinstance(runtime, types.ModuleType): - yield Error(object_path, "is not a module", stub, runtime) + # Can possibly happen: + yield Error(object_path, "is not a module", stub, runtime) # type: ignore[unreachable] return runtime_all_as_set: set[str] | None @@ -348,6 +359,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 @@ -462,6 +475,98 @@ class SubClass(runtime): # type: ignore[misc] ) +SIZEOF_PYOBJECT = struct.calcsize("P") + + +def _shape_differs(t1: type[object], t2: type[object]) -> bool: + """Check whether two types differ in shape. + + Mirrors the shape_differs() function in typeobject.c in CPython.""" + if sys.version_info >= (3, 12): + return t1.__basicsize__ != t2.__basicsize__ or t1.__itemsize__ != t2.__itemsize__ + else: + # CPython had more complicated logic before 3.12: + # https://github.com/python/cpython/blob/f3c6f882cddc8dc30320d2e73edf019e201394fc/Objects/typeobject.c#L2224 + # We attempt to mirror it here well enough to support the most common cases. + if t1.__itemsize__ or t2.__itemsize__: + return t1.__basicsize__ != t2.__basicsize__ or t1.__itemsize__ != t2.__itemsize__ + t_size = t1.__basicsize__ + if not t2.__weakrefoffset__ and t1.__weakrefoffset__ + SIZEOF_PYOBJECT == t_size: + t_size -= SIZEOF_PYOBJECT + if not t2.__dictoffset__ and t1.__dictoffset__ + SIZEOF_PYOBJECT == t_size: + t_size -= SIZEOF_PYOBJECT + if not t2.__weakrefoffset__ and t2.__weakrefoffset__ == t_size: + t_size -= SIZEOF_PYOBJECT + return t_size != t2.__basicsize__ + + +def _is_disjoint_base(typ: type[object]) -> bool: + """Return whether a type is a disjoint base at runtime, mirroring CPython's logic in typeobject.c. + + See PEP 800.""" + if typ is object: + return True + base = typ.__base__ + assert base is not None, f"Type {typ} has no base" + return _shape_differs(typ, base) + + +def _verify_disjoint_base( + stub: nodes.TypeInfo, runtime: type[object], object_path: list[str] +) -> Iterator[Error]: + is_disjoint_runtime = _is_disjoint_base(runtime) + # Don't complain about missing @disjoint_base if there are __slots__, because + # in that case we can infer that it's a disjoint base. + if ( + is_disjoint_runtime + and not stub.is_disjoint_base + and not runtime.__dict__.get("__slots__") + and not stub.is_final + and not (stub.is_enum and stub.enum_members) + ): + yield Error( + object_path, + "is a disjoint base at runtime, but isn't marked with @disjoint_base in the stub", + stub, + runtime, + stub_desc=repr(stub), + ) + elif stub.is_disjoint_base: + if not is_disjoint_runtime: + yield Error( + object_path, + "is marked with @disjoint_base in the stub, but isn't a disjoint base at runtime", + stub, + runtime, + stub_desc=repr(stub), + ) + if runtime.__dict__.get("__slots__"): + yield Error( + object_path, + "is marked as @disjoint_base, but also has slots; add __slots__ instead", + stub, + runtime, + stub_desc=repr(stub), + ) + elif stub.is_final: + yield Error( + object_path, + "is marked as @disjoint_base, but also marked as @final; remove @disjoint_base", + stub, + runtime, + stub_desc=repr(stub), + ) + elif stub.is_enum and stub.enum_members: + yield Error( + object_path, + "is marked as @disjoint_base, but is an enum with members, which is implicitly final; " + "remove @disjoint_base", + stub, + runtime, + stub_desc=repr(stub), + ) + + def _verify_metaclass( stub: nodes.TypeInfo, runtime: type[Any], object_path: list[str], *, is_runtime_typeddict: bool ) -> Iterator[Error]: @@ -502,9 +607,13 @@ def _verify_metaclass( @verify.register(nodes.TypeInfo) def verify_typeinfo( - stub: nodes.TypeInfo, runtime: MaybeMissing[type[Any]], object_path: list[str] + stub: nodes.TypeInfo, + runtime: MaybeMissing[type[Any]], + object_path: list[str], + *, + is_alias_target: bool = False, ) -> Iterator[Error]: - if stub.is_type_check_only: + if stub.is_type_check_only and not is_alias_target: # This type only exists in stubs, we only check that the runtime part # is missing. Other checks are not required. if not isinstance(runtime, Missing): @@ -521,10 +630,12 @@ def verify_typeinfo( yield Error(object_path, "is not present at runtime", stub, runtime, stub_desc=repr(stub)) return if not isinstance(runtime, type): - yield Error(object_path, "is not a type", stub, runtime, stub_desc=repr(stub)) + # Yes, some runtime objects can be not types, no way to tell mypy about that. + yield Error(object_path, "is not a type", stub, runtime, stub_desc=repr(stub)) # type: ignore[unreachable] return yield from _verify_final(stub, runtime, object_path) + yield from _verify_disjoint_base(stub, runtime, object_path) is_runtime_typeddict = stub.typeddict_type is not None and is_typeddict(runtime) yield from _verify_metaclass( stub, runtime, object_path, is_runtime_typeddict=is_runtime_typeddict @@ -566,6 +677,27 @@ def verify_typeinfo( # Catch all exceptions in case the runtime raises an unexpected exception # from __getattr__ or similar. continue + + # If it came from the metaclass, consider the runtime_attr to be MISSING + # for a more accurate message + if ( + runtime_attr is not MISSING + and type(runtime) is not runtime + and getattr(runtime_attr, "__objclass__", None) is type(runtime) + ): + runtime_attr = MISSING + + # __setattr__ and __delattr__ on object are a special case, + # so if we only have these methods inherited from there, pretend that + # we don't have them. See python/typeshed#7385. + if ( + entry in ("__setattr__", "__delattr__") + and runtime_attr is not MISSING + and runtime is not object + and getattr(runtime_attr, "__objclass__", None) is object + ): + runtime_attr = MISSING + # Do not error for an object missing from the stub # If the runtime object is a types.WrapperDescriptorType object # and has a non-special dunder name. @@ -628,10 +760,10 @@ def _verify_arg_name( if is_dunder(function_name, exclude_special=True): return - def strip_prefix(s: str, prefix: str) -> str: - return s[len(prefix) :] if s.startswith(prefix) else s - - if strip_prefix(stub_arg.variable.name, "__") == runtime_arg.name: + if ( + stub_arg.variable.name == runtime_arg.name + or stub_arg.variable.name.removeprefix("__") == runtime_arg.name + ): return nonspecific_names = {"object", "args"} @@ -652,8 +784,8 @@ def names_approx_match(a: str, b: str) -> bool: if stub_arg.variable.name == "_self": return yield ( - f'stub argument "{stub_arg.variable.name}" ' - f'differs from runtime argument "{runtime_arg.name}"' + f'stub parameter "{stub_arg.variable.name}" ' + f'differs from runtime parameter "{runtime_arg.name}"' ) @@ -661,14 +793,18 @@ def _verify_arg_default_value( stub_arg: nodes.Argument, runtime_arg: inspect.Parameter ) -> Iterator[str]: """Checks whether argument default values are compatible.""" - if runtime_arg.default != inspect.Parameter.empty: + if runtime_arg.default is not inspect.Parameter.empty: if stub_arg.kind.is_required(): yield ( - f'runtime argument "{runtime_arg.name}" ' - "has a default value but stub argument does not" + f'runtime parameter "{runtime_arg.name}" ' + "has a default value but stub parameter does not" ) else: - runtime_type = get_mypy_type_of_runtime_value(runtime_arg.default) + type_context = stub_arg.variable.type + runtime_type = get_mypy_type_of_runtime_value( + runtime_arg.default, type_context=type_context + ) + # Fallback to the type annotation type if var type is missing. The type annotation # is an UnboundType, but I don't know enough to know what the pros and cons here are. # UnboundTypes have ugly question marks following them, so default to var type. @@ -686,9 +822,9 @@ def _verify_arg_default_value( and not is_subtype_helper(runtime_type, stub_type) ): yield ( - f'runtime argument "{runtime_arg.name}" ' + f'runtime parameter "{runtime_arg.name}" ' f"has a default value of type {runtime_type}, " - f"which is incompatible with stub argument type {stub_type}" + f"which is incompatible with stub parameter type {stub_type}" ) if stub_arg.initializer is not None: stub_default = evaluate_expression(stub_arg.initializer) @@ -696,23 +832,31 @@ def _verify_arg_default_value( stub_default is not UNKNOWN and stub_default is not ... and runtime_arg.default is not UNREPRESENTABLE - and ( - 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 - ) ): - yield ( - f'runtime argument "{runtime_arg.name}" ' - f"has a default value of {runtime_arg.default!r}, " - f"which is different from stub argument default {stub_default!r}" - ) + defaults_match = True + # We want the types to match exactly, e.g. in case the stub has + # True and the runtime has 1 (or vice versa). + if type(stub_default) is not type(runtime_arg.default): + defaults_match = False + else: + try: + defaults_match = bool(stub_default == runtime_arg.default) + except Exception: + # Exception can be raised in bool dunder method (e.g. numpy arrays) + # At this point, consider the default to be different, it is probably + # too complex to put in a stub anyway. + defaults_match = False + if not defaults_match: + yield ( + f'runtime parameter "{runtime_arg.name}" ' + f"has a default value of {runtime_arg.default!r}, " + f"which is different from stub parameter default {stub_default!r}" + ) else: if stub_arg.kind.is_optional(): yield ( - f'stub argument "{stub_arg.variable.name}" has a default value ' - f"but runtime argument does not" + f'stub parameter "{stub_arg.variable.name}" has a default value ' + f"but runtime parameter does not" ) @@ -749,7 +893,7 @@ def get_type(arg: Any) -> str | None: def has_default(arg: Any) -> bool: if isinstance(arg, inspect.Parameter): - return bool(arg.default != inspect.Parameter.empty) + return arg.default is not inspect.Parameter.empty if isinstance(arg, nodes.Argument): return arg.kind.is_optional() raise AssertionError @@ -822,21 +966,36 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg # For most dunder methods, just assume all args are positional-only assume_positional_only = is_dunder(stub.name, exclude_special=True) - all_args: dict[str, list[tuple[nodes.Argument, int]]] = {} + is_arg_pos_only: defaultdict[str, set[bool]] = defaultdict(set) for func in map(_resolve_funcitem_from_decorator, stub.items): - assert func is not None + assert func is not None, f"Failed to resolve decorated overload of {stub.fullname!r}" args = maybe_strip_cls(stub.name, func.arguments) for index, arg in enumerate(args): - # For positional-only args, we allow overloads to have different names for the same - # argument. To accomplish this, we just make up a fake index-based name. - name = ( - f"__{index}" - if arg.variable.name.startswith("__") + if ( + arg.variable.name.startswith("__") or arg.pos_only or assume_positional_only or arg.variable.name.strip("_") == "self" - else arg.variable.name - ) + or (index == 0 and arg.variable.name.strip("_") == "cls") + ): + is_arg_pos_only[arg.variable.name].add(True) + else: + is_arg_pos_only[arg.variable.name].add(False) + + all_args: dict[str, list[tuple[nodes.Argument, int]]] = {} + for func in map(_resolve_funcitem_from_decorator, stub.items): + assert func is not None, f"Failed to resolve decorated overload of {stub.fullname!r}" + args = maybe_strip_cls(stub.name, func.arguments) + for index, arg in enumerate(args): + # For positional-only args, we allow overloads to have different names for the same + # argument. To accomplish this, we just make up a fake index-based name. + # We can only use the index-based name if the argument is always + # positional only. Sometimes overloads have an arg as positional-only + # in some but not all branches of the overload. + name = arg.variable.name + if is_arg_pos_only[name] == {True}: + name = f"__{index}" + all_args.setdefault(name, []).append((arg, index)) def get_position(arg_name: str) -> int: @@ -894,7 +1053,10 @@ def get_kind(arg_name: str) -> nodes.ArgKind: def _verify_signature( - stub: Signature[nodes.Argument], runtime: Signature[inspect.Parameter], function_name: str + stub: Signature[nodes.Argument], + runtime: Signature[inspect.Parameter], + function_name: str, + warn_runtime_is_object_init: bool = False, ) -> Iterator[str]: # Check positional arguments match up for stub_arg, runtime_arg in zip(stub.pos, runtime.pos): @@ -905,21 +1067,24 @@ def _verify_signature( and not stub_arg.pos_only and not stub_arg.variable.name.startswith("__") and stub_arg.variable.name.strip("_") != "self" + and stub_arg.variable.name.strip("_") != "cls" and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods ): 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'stub parameter "{stub_arg.variable.name}" should be positional-only ' + f'(add "/", e.g. "{runtime_arg.name}, /")' ) if ( runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY and (stub_arg.pos_only or stub_arg.variable.name.startswith("__")) + and not runtime_arg.name.startswith("__") and stub_arg.variable.name.strip("_") != "self" + and stub_arg.variable.name.strip("_") != "cls" and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods ): yield ( - f'stub argument "{stub_arg.variable.name}" should be positional or keyword ' - "(remove leading double underscore)" + f'stub parameter "{stub_arg.variable.name}" should be positional or keyword ' + '(remove "/")' ) # Check unmatched positional args @@ -933,28 +1098,30 @@ def _verify_signature( # If the variable is in runtime.kwonly, it's just mislabelled as not a # keyword-only argument if stub_arg.variable.name not in runtime.kwonly: - msg = f'runtime does not have argument "{stub_arg.variable.name}"' + msg = f'runtime does not have parameter "{stub_arg.variable.name}"' if runtime.varkw is not None: msg += ". Maybe you forgot to make it keyword-only in the stub?" + elif warn_runtime_is_object_init: + msg += ". You may need to write stubs for __new__ instead of __init__." yield msg else: - yield f'stub argument "{stub_arg.variable.name}" is not keyword-only' + yield f'stub parameter "{stub_arg.variable.name}" is not keyword-only' if stub.varpos is not None: - yield f'runtime does not have *args argument "{stub.varpos.variable.name}"' + yield f'runtime does not have *args parameter "{stub.varpos.variable.name}"' elif len(stub.pos) < len(runtime.pos): for runtime_arg in runtime.pos[len(stub.pos) :]: if runtime_arg.name not in stub.kwonly: if not _is_private_parameter(runtime_arg): - yield f'stub does not have argument "{runtime_arg.name}"' + yield f'stub does not have parameter "{runtime_arg.name}"' else: - yield f'runtime argument "{runtime_arg.name}" is not keyword-only' + yield f'runtime parameter "{runtime_arg.name}" is not keyword-only' # Checks involving *args if len(stub.pos) <= len(runtime.pos) or runtime.varpos is None: if stub.varpos is None and runtime.varpos is not None: - yield f'stub does not have *args argument "{runtime.varpos.name}"' + yield f'stub does not have *args parameter "{runtime.varpos.name}"' if stub.varpos is not None and runtime.varpos is None: - yield f'runtime does not have *args argument "{stub.varpos.variable.name}"' + yield f'runtime does not have *args parameter "{stub.varpos.variable.name}"' # Check keyword-only args for arg in sorted(set(stub.kwonly) & set(runtime.kwonly)): @@ -973,9 +1140,13 @@ def _verify_signature( if arg in {runtime_arg.name for runtime_arg in runtime.pos}: # Don't report this if we've reported it before if arg not in {runtime_arg.name for runtime_arg in runtime.pos[len(stub.pos) :]}: - yield f'runtime argument "{arg}" is not keyword-only' + yield f'runtime parameter "{arg}" is not keyword-only' else: - yield f'runtime does not have argument "{arg}"' + msg = f'runtime does not have parameter "{arg}"' + if warn_runtime_is_object_init: + msg += ". You may need to write stubs for __new__ instead of __init__." + yield msg + for arg in sorted(set(runtime.kwonly) - set(stub.kwonly)): if arg in {stub_arg.variable.name for stub_arg in stub.pos}: # Don't report this if we've reported it before @@ -983,10 +1154,10 @@ def _verify_signature( runtime.varpos is None and arg in {stub_arg.variable.name for stub_arg in stub.pos[len(runtime.pos) :]} ): - yield f'stub argument "{arg}" is not keyword-only' + yield f'stub parameter "{arg}" is not keyword-only' else: if not _is_private_parameter(runtime.kwonly[arg]): - yield f'stub does not have argument "{arg}"' + yield f'stub does not have parameter "{arg}"' # Checks involving **kwargs if stub.varkw is None and runtime.varkw is not None: @@ -996,9 +1167,9 @@ def _verify_signature( stub_pos_names = {stub_arg.variable.name for stub_arg in stub.pos} # Ideally we'd do a strict subset check, but in practice the errors from that aren't useful if not set(runtime.kwonly).issubset(set(stub.kwonly) | stub_pos_names): - yield f'stub does not have **kwargs argument "{runtime.varkw.name}"' + yield f'stub does not have **kwargs parameter "{runtime.varkw.name}"' if stub.varkw is not None and runtime.varkw is None: - yield f'runtime does not have **kwargs argument "{stub.varkw.variable.name}"' + yield f'runtime does not have **kwargs parameter "{stub.varkw.variable.name}"' def _is_private_parameter(arg: inspect.Parameter) -> bool: @@ -1061,7 +1232,12 @@ def verify_funcitem( if not signature: return - for message in _verify_signature(stub_sig, runtime_sig, function_name=stub.name): + for message in _verify_signature( + stub_sig, + runtime_sig, + function_name=stub.name, + warn_runtime_is_object_init=runtime is object.__init__, + ): yield Error( object_path, "is inconsistent, " + message, @@ -1072,9 +1248,11 @@ def verify_funcitem( @verify.register(Missing) -def verify_none( +def verify_missing( stub: Missing, runtime: MaybeMissing[Any], object_path: list[str] ) -> Iterator[Error]: + if runtime is MISSING: + return yield Error(object_path, "is not present in stub", stub, runtime) @@ -1095,7 +1273,8 @@ def verify_var( ): yield Error(object_path, "is read-only at runtime but not in the stub", stub, runtime) - runtime_type = get_mypy_type_of_runtime_value(runtime) + runtime_type = get_mypy_type_of_runtime_value(runtime, type_context=stub.type) + note = "" if ( runtime_type is not None and stub.type is not None @@ -1108,17 +1287,28 @@ def verify_var( runtime_type = get_mypy_type_of_runtime_value(runtime.value) if runtime_type is not None and is_subtype_helper(runtime_type, stub.type): should_error = False - # We always allow setting the stub value to ... + # We always allow setting the stub value to Ellipsis (...), but use + # _value_ type as a fallback if given. If a member is ... and _value_ + # type is given, all runtime types should be assignable to _value_. proper_type = mypy.types.get_proper_type(stub.type) if ( isinstance(proper_type, mypy.types.Instance) - and proper_type.type.fullname == "builtins.ellipsis" + and proper_type.type.fullname in mypy.types.ELLIPSIS_TYPE_NAMES ): - should_error = False + value_t = stub.info.get("_value_") + if value_t is None or value_t.type is None or runtime_type is None: + should_error = False + elif is_subtype_helper(runtime_type, value_t.type): + should_error = False + else: + note = " (incompatible '_value_')" if should_error: yield Error( - object_path, f"variable differs from runtime type {runtime_type}", stub, runtime + object_path, + f"variable differs from runtime type {runtime_type}{note}", + stub, + runtime, ) @@ -1169,7 +1359,12 @@ def verify_overloadedfuncdef( stub_sig = Signature.from_overloadedfuncdef(stub) runtime_sig = Signature.from_inspect_signature(signature) - for message in _verify_signature(stub_sig, runtime_sig, function_name=stub.name): + for message in _verify_signature( + stub_sig, + runtime_sig, + function_name=stub.name, + warn_runtime_is_object_init=runtime is object.__init__, + ): # TODO: This is a little hacky, but the addition here is super useful if "has a default value of type" in message: message += ( @@ -1219,6 +1414,19 @@ def verify_paramspecexpr( return +def _is_django_cached_property(runtime: Any) -> bool: # pragma: no cover + # This is a special case for + # https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.functional.cached_property + # This is needed in `django-stubs` project: + # https://github.com/typeddjango/django-stubs + if type(runtime).__name__ != "cached_property": + return False + try: + return bool(runtime.func) + except Exception: + return False + + def _verify_readonly_property(stub: nodes.Decorator, runtime: Any) -> Iterator[str]: assert stub.func.is_property if isinstance(runtime, property): @@ -1227,6 +1435,9 @@ def _verify_readonly_property(stub: nodes.Decorator, runtime: Any) -> Iterator[s if isinstance(runtime, functools.cached_property): yield from _verify_final_method(stub.func, runtime.func, MISSING) return + if _is_django_cached_property(runtime): + yield from _verify_final_method(stub.func, runtime.func, MISSING) + return if inspect.isdatadescriptor(runtime): # It's enough like a property... return @@ -1293,13 +1504,14 @@ def apply_decorator_to_funcitem( if ( decorator.fullname in ("builtins.staticmethod", "abc.abstractmethod") or decorator.fullname in mypy.types.OVERLOAD_NAMES + or decorator.fullname in mypy.types.OVERRIDE_DECORATOR_NAMES or decorator.fullname in mypy.types.FINAL_DECORATOR_NAMES ): return func if decorator.fullname == "builtins.classmethod": if func.arguments[0].variable.name not in ("cls", "mcs", "metacls"): raise StubtestFailure( - f"unexpected class argument name {func.arguments[0].variable.name!r} " + f"unexpected class parameter name {func.arguments[0].variable.name!r} " f"in {dec.fullname}" ) # FuncItem is written so that copy.copy() actually works, even when compiled @@ -1395,7 +1607,7 @@ def verify_typealias( # Okay, either we couldn't construct a fullname # or the fullname of the stub didn't match the fullname of the runtime. # Fallback to a full structural check of the runtime vis-a-vis the stub. - yield from verify(stub_origin, runtime_origin, object_path) + yield from verify_typeinfo(stub_origin, runtime_origin, object_path, is_alias_target=True) return if isinstance(stub_target, mypy.types.UnionType): # complain if runtime is not a Union or UnionType @@ -1429,7 +1641,7 @@ def verify_typealias( # ==================== -IGNORED_MODULE_DUNDERS: typing_extensions.Final = frozenset( +IGNORED_MODULE_DUNDERS: Final = frozenset( { "__file__", "__doc__", @@ -1440,6 +1652,7 @@ def verify_typealias( "__loader__", "__spec__", "__annotations__", + "__annotate__", "__path__", # mypy adds __path__ to packages, but C packages don't have it "__getattr__", # resulting behaviour might be typed explicitly # Created by `warnings.warn`, does not make much sense to have in stubs: @@ -1451,11 +1664,14 @@ def verify_typealias( } ) -IGNORABLE_CLASS_DUNDERS: typing_extensions.Final = frozenset( +IGNORABLE_CLASS_DUNDERS: Final = frozenset( { # Special attributes "__dict__", "__annotations__", + "__annotate__", + "__annotations_cache__", + "__annotate_func__", "__text_signature__", "__weakref__", "__hash__", @@ -1464,6 +1680,7 @@ def verify_typealias( "__vectorcalloffset__", # undocumented implementation detail of the vectorcall protocol "__firstlineno__", "__static_attributes__", + "__classdictcell__", # isinstance/issubclass hooks that type-checkers don't usually care about "__instancecheck__", "__subclasshook__", @@ -1482,6 +1699,7 @@ def verify_typealias( "__getinitargs__", "__reduce_ex__", "__reduce__", + "__slotnames__", # Cached names of slots added by `copyreg` module. # ctypes weirdness "__ctype_be__", "__ctype_le__", @@ -1514,9 +1732,17 @@ def is_probably_private(name: str) -> bool: def is_probably_a_function(runtime: Any) -> bool: return ( - isinstance(runtime, (types.FunctionType, types.BuiltinFunctionType)) - or isinstance(runtime, (types.MethodType, types.BuiltinMethodType)) + isinstance( + runtime, + ( + types.FunctionType, + types.BuiltinFunctionType, + types.MethodType, + types.BuiltinMethodType, + ), + ) or (inspect.ismethoddescriptor(runtime) and callable(runtime)) + or (isinstance(runtime, types.MethodWrapperType) and callable(runtime)) ) @@ -1525,6 +1751,71 @@ def is_read_only_property(runtime: object) -> bool: def safe_inspect_signature(runtime: Any) -> inspect.Signature | None: + if ( + hasattr(runtime, "__name__") + and runtime.__name__ == "__init__" + and hasattr(runtime, "__text_signature__") + and runtime.__text_signature__ == "($self, /, *args, **kwargs)" + and hasattr(runtime, "__objclass__") + and hasattr(runtime.__objclass__, "__text_signature__") + and runtime.__objclass__.__text_signature__ is not None + ): + # This is an __init__ method with the generic C-class signature. + # In this case, the underlying class often has a better signature, + # which we can convert into an __init__ signature by adding in the + # self parameter. + try: + s = inspect.signature(runtime.__objclass__) + + parameter_kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD + if s.parameters: + first_parameter = next(iter(s.parameters.values())) + if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY: + parameter_kind = inspect.Parameter.POSITIONAL_ONLY + return s.replace( + parameters=[inspect.Parameter("self", parameter_kind), *s.parameters.values()] + ) + except Exception: + pass + + if ( + hasattr(runtime, "__name__") + and runtime.__name__ == "__new__" + and hasattr(runtime, "__text_signature__") + and runtime.__text_signature__ == "($type, *args, **kwargs)" + and hasattr(runtime, "__self__") + and hasattr(runtime.__self__, "__text_signature__") + and runtime.__self__.__text_signature__ is not None + ): + # This is a __new__ method with the generic C-class signature. + # In this case, the underlying class often has a better signature, + # which we can convert into a __new__ signature by adding in the + # cls parameter. + + # If the attached class has a valid __init__, skip recovering a + # signature for this __new__ method. + has_init = False + if ( + hasattr(runtime.__self__, "__init__") + and hasattr(runtime.__self__.__init__, "__objclass__") + and runtime.__self__.__init__.__objclass__ is runtime.__self__ + ): + has_init = True + + if not has_init: + try: + s = inspect.signature(runtime.__self__) + parameter_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD + if s.parameters: + first_parameter = next(iter(s.parameters.values())) + if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY: + parameter_kind = inspect.Parameter.POSITIONAL_ONLY + return s.replace( + parameters=[inspect.Parameter("cls", parameter_kind), *s.parameters.values()] + ) + except Exception: + pass + try: try: return inspect.signature(runtime) @@ -1584,7 +1875,18 @@ def is_subtype_helper(left: mypy.types.Type, right: mypy.types.Type) -> bool: return mypy.subtypes.is_subtype(left, right) -def get_mypy_type_of_runtime_value(runtime: Any) -> mypy.types.Type | None: +def get_mypy_node_for_name(module: str, type_name: str) -> mypy.nodes.SymbolNode | None: + stub = get_stub(module) + if stub is None: + return None + if type_name not in stub.names: + return None + return stub.names[type_name].node + + +def get_mypy_type_of_runtime_value( + runtime: Any, type_context: mypy.types.Type | None = None +) -> mypy.types.Type | None: """Returns a mypy type object representing the type of ``runtime``. Returns None if we can't find something that works. @@ -1618,13 +1920,13 @@ def anytype() -> mypy.types.AnyType: arg_names.append( None if arg.kind == inspect.Parameter.POSITIONAL_ONLY else arg.name ) - has_default = arg.default == inspect.Parameter.empty + no_default = arg.default is inspect.Parameter.empty if arg.kind == inspect.Parameter.POSITIONAL_ONLY: - arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) + arg_kinds.append(nodes.ARG_POS if no_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: - arg_kinds.append(nodes.ARG_POS if has_default else nodes.ARG_OPT) + arg_kinds.append(nodes.ARG_POS if no_default else nodes.ARG_OPT) elif arg.kind == inspect.Parameter.KEYWORD_ONLY: - arg_kinds.append(nodes.ARG_NAMED if has_default else nodes.ARG_NAMED_OPT) + arg_kinds.append(nodes.ARG_NAMED if no_default else nodes.ARG_NAMED_OPT) elif arg.kind == inspect.Parameter.VAR_POSITIONAL: arg_kinds.append(nodes.ARG_STAR) elif arg.kind == inspect.Parameter.VAR_KEYWORD: @@ -1645,14 +1947,45 @@ def anytype() -> mypy.types.AnyType: is_ellipsis_args=True, ) - # Try and look up a stub for the runtime object - stub = get_stub(type(runtime).__module__) - if stub is None: - return None - type_name = type(runtime).__name__ - if type_name not in stub.names: + skip_type_object_type = False + if type_context: + # Don't attempt to process the type object when context is generic + # This is related to issue #3737 + type_context = mypy.types.get_proper_type(type_context) + # Callable types with a generic return value + if isinstance(type_context, mypy.types.CallableType): + if isinstance(type_context.ret_type, mypy.types.TypeVarType): + skip_type_object_type = True + # Type[x] where x is generic + if isinstance(type_context, mypy.types.TypeType): + if isinstance(type_context.item, mypy.types.TypeVarType): + skip_type_object_type = True + + if isinstance(runtime, type) and not skip_type_object_type: + + def _named_type(name: str) -> mypy.types.Instance: + parts = name.rsplit(".", maxsplit=1) + node = get_mypy_node_for_name(parts[0], parts[1]) + assert isinstance(node, nodes.TypeInfo) + any_type = mypy.types.AnyType(mypy.types.TypeOfAny.special_form) + return mypy.types.Instance(node, [any_type] * len(node.defn.type_vars)) + + # Try and look up a stub for the runtime object itself + # The logic here is similar to ExpressionChecker.analyze_ref_expr + type_info = get_mypy_node_for_name(runtime.__module__, runtime.__name__) + if isinstance(type_info, nodes.TypeInfo): + result: mypy.types.Type | None = None + result = mypy.typeops.type_object_type(type_info, _named_type) + if mypy.checkexpr.is_type_type_context(type_context): + # This is the type in a type[] expression, so substitute type + # variables with Any. + result = mypy.erasetype.erase_typevars(result) + return result + + # Try and look up a stub for the runtime object's type + type_info = get_mypy_node_for_name(type(runtime).__module__, type(runtime).__name__) + if type_info is None: return None - type_info = stub.names[type_name].node if isinstance(type_info, nodes.Var): return type_info.type if not isinstance(type_info, nodes.TypeInfo): @@ -1884,6 +2217,7 @@ class _Arguments: concise: bool ignore_missing_stub: bool ignore_positional_only: bool + ignore_disjoint_bases: bool allowlist: list[str] generate_allowlist: bool ignore_unused_allowlist: bool @@ -1891,12 +2225,12 @@ 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 -ANNOYING_STDLIB_MODULES: typing_extensions.Final = frozenset( - {"antigravity", "this", "__main__", "_ios_support"} -) +ANNOYING_STDLIB_MODULES: Final = frozenset({"antigravity", "this", "__main__", "_ios_support"}) def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: @@ -1936,6 +2270,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: @@ -1955,6 +2291,7 @@ def warning_callback(msg: str) -> None: options.process_incomplete_features( error_callback=error_callback, warning_callback=warning_callback ) + options.process_strict_bytes() try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) @@ -1974,6 +2311,8 @@ def warning_callback(msg: str) -> None: continue if args.ignore_positional_only and error.is_positional_only_related(): continue + if args.ignore_disjoint_bases and error.is_disjoint_base_related(): + continue if error.object_desc in allowlist: allowlist[error.object_desc] = True continue @@ -1991,7 +2330,7 @@ def warning_callback(msg: str) -> None: if args.generate_allowlist: generated_allowlist.add(error.object_desc) continue - print(error.get_description(concise=args.concise)) + safe_print(error.get_description(concise=args.concise)) error_count += 1 # Print unused allowlist entries @@ -2031,6 +2370,19 @@ def warning_callback(msg: str) -> None: return exit_code +def safe_print(text: str) -> None: + """Print a text replacing chars not representable in stdout encoding.""" + # If `sys.stdout` encoding is not the same as out (usually UTF8) string, + # if may cause painful crashes. I don't want to reconfigure `sys.stdout` + # to do `errors = "replace"` as that sounds scary. + out_encoding = sys.stdout.encoding + if out_encoding is not None: + # Can be None if stdout is replaced (including our own tests). This should be + # safe to omit if the actual stream doesn't care about encoding. + text = text.encode(out_encoding, errors="replace").decode(out_encoding, errors="replace") + print(text) + + def parse_options(args: list[str]) -> _Arguments: parser = argparse.ArgumentParser( description="Compares stubs to objects introspected from the runtime." @@ -2051,6 +2403,12 @@ def parse_options(args: list[str]) -> _Arguments: action="store_true", help="Ignore errors for whether an argument should or shouldn't be positional-only", ) + # TODO: Remove once PEP 800 is accepted + parser.add_argument( + "--ignore-disjoint-bases", + action="store_true", + help="Disable checks for PEP 800 @disjoint_base classes", + ) parser.add_argument( "--allowlist", "--whitelist", @@ -2089,6 +2447,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 04b36e149957d..a3c0f9b7b277d 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -5,19 +5,30 @@ import os.path import re import sys +import traceback from abc import abstractmethod from collections import defaultdict +from collections.abc import Iterable, Iterator, Mapping from contextlib import contextmanager -from typing import Final, Iterable, Iterator, Mapping -from typing_extensions import overload +from typing import Final, overload from mypy_extensions import mypyc_attr 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, Type, TypeList, TypeStrVisitor, UnboundType, UnionType +from mypy.types import ( + AnyType, + NoneType, + Type, + TypeList, + TypeStrVisitor, + UnboundType, + UnionType, + UnpackType, +) # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () @@ -69,6 +80,9 @@ def walk_packages( try: prop = inspect.get_package_properties(package_name) except InspectError: + if verbose: + tb = traceback.format_exc() + sys.stderr.write(tb) report_missing(package_name) continue yield prop.name @@ -287,6 +301,11 @@ def visit_type_list(self, t: TypeList) -> str: def visit_union_type(self, t: UnionType) -> str: return " | ".join([item.accept(self) for item in t.items]) + def visit_unpack_type(self, t: UnpackType) -> str: + if self.options.python_version >= (3, 11): + return f"*{t.type.accept(self)}" + return super().visit_unpack_type(t) + def args_str(self, args: Iterable[Type]) -> str: """Convert an array of arguments to strings and join the results with commas. @@ -306,12 +325,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: @@ -334,7 +359,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 @@ -772,11 +803,37 @@ def format_func_def( signature.format_sig( indent=self._indent, is_async=is_coroutine, - docstring=docstring if self._include_docstrings else None, + docstring=docstring, + include_docstrings=self._include_docstrings, ) ) 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, @@ -794,6 +851,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 787d5cb89b0ae..c02ff068560b4 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1,12 +1,14 @@ from __future__ import annotations +from collections.abc import Iterable, Iterator from contextlib import contextmanager -from typing import Any, Callable, Final, Iterator, List, TypeVar, cast +from typing import Any, Callable, Final, TypeVar, cast from typing_extensions import TypeAlias as _TypeAlias import mypy.applytype import mypy.constraints import mypy.typeops +from mypy.checker_state import checker_state from mypy.erasetype import erase_type from mypy.expandtype import ( expand_self_type, @@ -25,6 +27,7 @@ COVARIANT, INVARIANT, VARIANCE_NOT_READY, + Context, Decorator, FuncBase, OverloadedFuncDef, @@ -66,6 +69,7 @@ UnionType, UnpackType, find_unpack_in_list, + flatten_nested_unions, get_proper_type, is_named_instance, split_with_prefix_and_suffix, @@ -79,6 +83,7 @@ IS_CLASSVAR: Final = 2 IS_CLASS_OR_STATIC: Final = 3 IS_VAR: Final = 4 +IS_EXPLICIT_SETTER: Final = 5 TypeParameterChecker: _TypeAlias = Callable[[Type, Type, int, bool, "SubtypeContext"], bool] @@ -140,6 +145,8 @@ def is_subtype( between the type arguments (e.g., A and B), taking the variance of the type var into account. """ + if left == right: + return True if subtype_context is None: subtype_context = SubtypeContext( ignore_type_params=ignore_type_params, @@ -150,15 +157,13 @@ def is_subtype( options=options, ) else: - assert not any( - { - ignore_type_params, - ignore_pos_arg_names, - ignore_declared_variance, - always_covariant, - ignore_promotions, - options, - } + assert ( + not ignore_type_params + and not ignore_pos_arg_names + and not ignore_declared_variance + and not always_covariant + and not ignore_promotions + and options is None ), "Don't pass both context and individual flags" if type_state.is_assumed_subtype(left, right): return True @@ -203,6 +208,8 @@ def is_proper_subtype( (this is useful for runtime isinstance() checks). If keep_erased_types is True, do not consider ErasedType a subtype of all types (used by type inference against unions). """ + if left == right: + return True if subtype_context is None: subtype_context = SubtypeContext( ignore_promotions=ignore_promotions, @@ -210,8 +217,8 @@ def is_proper_subtype( keep_erased_types=keep_erased_types, ) else: - assert not any( - {ignore_promotions, erase_instances, keep_erased_types} + assert ( + not ignore_promotions and not erase_instances and not keep_erased_types ), "Don't pass both context and individual flags" if type_state.is_assumed_proper_subtype(left, right): return True @@ -325,7 +332,9 @@ def _is_subtype( and isinstance(left, Instance) and (left.type.is_enum or left.type.fullname == "builtins.bool") ): - right = UnionType(mypy.typeops.try_contracting_literals_in_union(right.items)) + right = UnionType( + mypy.typeops.try_contracting_literals_in_union(flatten_nested_unions(right.items)) + ) if proper_subtype: is_subtype_of_item = any( is_proper_subtype(orig_left, item, subtype_context=subtype_context) @@ -385,6 +394,15 @@ def check_type_parameter( class SubtypeVisitor(TypeVisitor[bool]): + __slots__ = ( + "right", + "orig_right", + "proper_subtype", + "subtype_context", + "options", + "_subtype_kind", + ) + def __init__(self, right: Type, subtype_context: SubtypeContext, proper_subtype: bool) -> None: self.right = get_proper_type(right) self.orig_right = right @@ -412,6 +430,9 @@ def _is_subtype(self, left: Type, right: Type) -> bool: return is_proper_subtype(left, right, subtype_context=self.subtype_context) return is_subtype(left, right, subtype_context=self.subtype_context) + def _all_subtypes(self, lefts: Iterable[Type], rights: Iterable[Type]) -> bool: + return all(self._is_subtype(li, ri) for (li, ri) in zip(lefts, rights)) + # visit_x(left) means: is left (which is an instance of X) a subtype of right? def visit_unbound_type(self, left: UnboundType) -> bool: @@ -472,21 +493,17 @@ def visit_instance(self, left: Instance) -> bool: return self._is_subtype(left, unpacked) if left.type.has_base(right.partial_fallback.type.fullname): if not self.proper_subtype: - # Special case to consider Foo[*tuple[Any, ...]] (i.e. bare Foo) a - # subtype of Foo[], when Foo is user defined variadic tuple type. + # Special cases to consider: + # * Plain tuple[Any, ...] instance is a subtype of all tuple types. + # * Foo[*tuple[Any, ...]] (normalized) instance is a subtype of all + # tuples with fallback to Foo (e.g. for variadic NamedTuples). mapped = map_instance_to_supertype(left, right.partial_fallback.type) - for arg in map(get_proper_type, mapped.args): - if isinstance(arg, UnpackType): - unpacked = get_proper_type(arg.type) - if not isinstance(unpacked, Instance): - break - assert unpacked.type.fullname == "builtins.tuple" - if not isinstance(get_proper_type(unpacked.args[0]), AnyType): - break - elif not isinstance(arg, AnyType): - break - else: - return True + if is_erased_instance(mapped): + if ( + mapped.type.fullname == "builtins.tuple" + or mapped.type.has_type_var_tuple_type + ): + return True return False if isinstance(right, TypeVarTupleType): # tuple[Any, ...] is like Any in the world of tuples (see special case above). @@ -499,7 +516,7 @@ def visit_instance(self, left: Instance) -> bool: return True if type_state.is_cached_negative_subtype_check(self._subtype_kind, left, right): return False - if not self.subtype_context.ignore_promotions: + if not self.subtype_context.ignore_promotions and not right.type.is_protocol: for base in left.type.mro: if base._promote and any( self._is_subtype(p, self.right) for p in base._promote @@ -554,19 +571,8 @@ def visit_instance(self, left: Instance) -> bool: right_args = ( right_prefix + (TupleType(list(right_middle), fallback),) + right_suffix ) - if not self.proper_subtype and t.args: - for arg in map(get_proper_type, t.args): - if isinstance(arg, UnpackType): - unpacked = get_proper_type(arg.type) - if not isinstance(unpacked, Instance): - break - assert unpacked.type.fullname == "builtins.tuple" - if not isinstance(get_proper_type(unpacked.args[0]), AnyType): - break - elif not isinstance(arg, AnyType): - break - else: - return True + if not self.proper_subtype and is_erased_instance(t): + return True if len(left_args) != len(right_args): return False type_params = zip(left_args, right_args, right.type.defn.type_vars) @@ -625,8 +631,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) @@ -637,7 +643,14 @@ def visit_instance(self, left: Instance) -> bool: def visit_type_var(self, left: TypeVarType) -> bool: right = self.right if isinstance(right, TypeVarType) and left.id == right.id: - return True + # Fast path for most common case. + if left.upper_bound == right.upper_bound: + return True + # Corner case for self-types in classes generic in type vars + # with value restrictions. + if left.id.is_self(): + return True + return self._is_subtype(left.upper_bound, right.upper_bound) if left.values and self._is_subtype(UnionType.make_union(left.values), right): return True return self._is_subtype(left.upper_bound, self.right) @@ -724,8 +737,7 @@ def visit_callable_type(self, left: CallableType) -> bool: elif isinstance(right, Instance): if right.type.is_protocol and "__call__" in right.type.protocol_members: # OK, a callable can implement a protocol with a `__call__` member. - # TODO: we should probably explicitly exclude self-types in this case. - call = find_member("__call__", right, left, is_operator=True) + call = find_member("__call__", right, right, is_operator=True) assert call is not None if self._is_subtype(left, call): if len(right.type.protocol_members) == 1: @@ -854,11 +866,25 @@ def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: # There are some items on the left that will never have a matching length # on the right. return False + left_prefix = left_unpack_index + left_suffix = len(left.items) - left_prefix - 1 left_unpack = left.items[left_unpack_index] assert isinstance(left_unpack, UnpackType) left_unpacked = get_proper_type(left_unpack.type) if not isinstance(left_unpacked, Instance): - # *Ts unpacks can't be split. + # *Ts unpack can't be split, except if it is all mapped to Anys or objects. + if self.is_top_type(right_item): + right_prefix_types, middle, right_suffix_types = split_with_prefix_and_suffix( + tuple(right.items), left_prefix, left_suffix + ) + if not all( + self.is_top_type(ri) or isinstance(ri, UnpackType) for ri in middle + ): + return False + # Also check the tails match as well. + return self._all_subtypes( + left.items[:left_prefix], right_prefix_types + ) and self._all_subtypes(left.items[-left_suffix:], right_suffix_types) return False assert left_unpacked.type.fullname == "builtins.tuple" left_item = left_unpacked.args[0] @@ -869,8 +895,6 @@ def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: # and then check subtyping for all finite overlaps. if not self._is_subtype(left_item, right_item): return False - left_prefix = left_unpack_index - left_suffix = len(left.items) - left_prefix - 1 max_overlap = max(0, right_prefix - left_prefix, right_suffix - left_suffix) for overlap in range(max_overlap + 1): repr_items = left.items[:left_prefix] + [left_item] * overlap @@ -881,6 +905,11 @@ def variadic_tuple_subtype(self, left: TupleType, right: TupleType) -> bool: return False return True + def is_top_type(self, typ: Type) -> bool: + if not self.proper_subtype and isinstance(get_proper_type(typ), AnyType): + return True + return is_named_instance(typ, "builtins.object") + def visit_typeddict_type(self, left: TypedDictType) -> bool: right = self.right if isinstance(right, Instance): @@ -892,32 +921,35 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: 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. - # - # 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): + # 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('A', {'x': int}) + # B = TypedDict('B', {'x': int}) # def reset_x(b: B) -> None: # b['x'] = 0 # @@ -941,7 +973,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: if isinstance(right, Instance): if right.type.is_protocol and "__call__" in right.type.protocol_members: # same as for CallableType - call = find_member("__call__", right, left, is_operator=True) + call = find_member("__call__", right, right, is_operator=True) assert call is not None if self._is_subtype(left, call): if len(right.type.protocol_members) == 1: @@ -1076,33 +1108,50 @@ def visit_partial_type(self, left: PartialType) -> bool: def visit_type_type(self, left: TypeType) -> bool: right = self.right - if isinstance(right, TypeType): - return self._is_subtype(left.item, right.item) - if isinstance(right, CallableType): - if self.proper_subtype and not right.is_type_obj(): - # We can't accept `Type[X]` as a *proper* subtype of Callable[P, X] - # since this will break transitivity of subtyping. + if left.is_type_form: + if isinstance(right, TypeType): + if not right.is_type_form: + return False + return self._is_subtype(left.item, right.item) + if isinstance(right, Instance): + if right.type.fullname == "builtins.object": + return True return False - # This is unsound, we don't check the __init__ signature. - return self._is_subtype(left.item, right.ret_type) - if isinstance(right, Instance): - if right.type.fullname in ["builtins.object", "builtins.type"]: - # TODO: Strictly speaking, the type builtins.type is considered equivalent to - # Type[Any]. However, this would break the is_proper_subtype check in - # conditional_types for cases like isinstance(x, type) when the type - # of x is Type[int]. It's unclear what's the right way to address this. - return True - item = left.item - if isinstance(item, TypeVarType): - item = get_proper_type(item.upper_bound) - if isinstance(item, Instance): - if right.type.is_protocol and is_protocol_implementation( - item, right, proper_subtype=self.proper_subtype, class_obj=True - ): + return False + else: # not left.is_type_form + if isinstance(right, TypeType): + return self._is_subtype(left.item, right.item) + if isinstance(right, Overloaded) and right.is_type_obj(): + # Same as in other direction: if it's a constructor callable, all + # items should belong to the same class' constructor, so it's enough + # to check one of them. + return self._is_subtype(left, right.items[0]) + if isinstance(right, CallableType): + if self.proper_subtype and not right.is_type_obj(): + # We can't accept `Type[X]` as a *proper* subtype of Callable[P, X] + # since this will break transitivity of subtyping. + return False + # This is unsound, we don't check the __init__ signature. + return self._is_subtype(left.item, right.ret_type) + + if isinstance(right, Instance): + if right.type.fullname in ["builtins.object", "builtins.type"]: + # TODO: Strictly speaking, the type builtins.type is considered equivalent to + # Type[Any]. However, this would break the is_proper_subtype check in + # conditional_types for cases like isinstance(x, type) when the type + # of x is Type[int]. It's unclear what's the right way to address this. return True - metaclass = item.type.metaclass_type - return metaclass is not None and self._is_subtype(metaclass, right) - return False + item = left.item + if isinstance(item, TypeVarType): + item = get_proper_type(item.upper_bound) + if isinstance(item, Instance): + if right.type.is_protocol and is_protocol_implementation( + item, right, proper_subtype=self.proper_subtype, class_obj=True + ): + return True + metaclass = item.type.metaclass_type + return metaclass is not None and self._is_subtype(metaclass, right) + return False def visit_type_alias_type(self, left: TypeAliasType) -> bool: assert False, f"This should be never called, got {left}" @@ -1168,7 +1217,7 @@ def f(self) -> A: ... ignore_names = member != "__call__" # __call__ can be passed kwargs # The third argument below indicates to what self type is bound. # We always bind self to the subtype. (Similarly to nominal types). - supertype = get_proper_type(find_member(member, right, left)) + supertype = find_member(member, right, left) assert supertype is not None subtype = mypy.typeops.get_protocol_member(left, member, class_obj) @@ -1177,15 +1226,6 @@ def f(self) -> A: ... # print(member, 'of', right, 'has type', supertype) if not subtype: return False - if isinstance(subtype, PartialType): - subtype = ( - NoneType() - if subtype.type is None - else Instance( - subtype.type, - [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars), - ) - ) if not proper_subtype: # Nominal check currently ignores arg names # NOTE: If we ever change this, be sure to also change the call to @@ -1197,15 +1237,28 @@ def f(self) -> A: ... is_compat = is_proper_subtype(subtype, supertype) if not is_compat: return False - if isinstance(subtype, NoneType) and isinstance(supertype, CallableType): + if isinstance(get_proper_type(subtype), NoneType) and isinstance( + get_proper_type(supertype), CallableType + ): # We want __hash__ = None idiom to work even without --strict-optional return False subflags = get_member_flags(member, left, class_obj=class_obj) superflags = get_member_flags(member, right) if IS_SETTABLE in superflags: # Check opposite direction for settable attributes. + if IS_EXPLICIT_SETTER in superflags: + supertype = find_member(member, right, left, is_lvalue=True) + if IS_EXPLICIT_SETTER in subflags: + subtype = mypy.typeops.get_protocol_member( + left, member, class_obj, is_lvalue=True + ) + # At this point we know attribute is present on subtype, otherwise we + # would return False above. + assert supertype is not None and subtype is not None if not is_subtype(supertype, subtype, options=options): return False + if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: + return False if not class_obj: if IS_SETTABLE not in superflags: if IS_CLASSVAR in superflags and IS_CLASSVAR not in subflags: @@ -1219,8 +1272,6 @@ def f(self) -> A: ... if IS_CLASSVAR in superflags: # This can be never matched by a class object. return False - if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: - return False # This rule is copied from nominal check in checker.py if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags: return False @@ -1239,15 +1290,94 @@ def f(self) -> A: ... def find_member( - name: str, itype: Instance, subtype: Type, is_operator: bool = False, class_obj: bool = False + name: str, + itype: Instance, + subtype: Type, + *, + is_operator: bool = False, + class_obj: bool = False, + is_lvalue: bool = False, +) -> Type | None: + type_checker = checker_state.type_checker + if type_checker is None: + # Unfortunately, there are many scenarios where someone calls is_subtype() before + # type checking phase. In this case we fallback to old (incomplete) logic. + # TODO: reduce number of such cases (e.g. semanal_typeargs, post-semanal plugins). + return find_member_simple( + name, itype, subtype, is_operator=is_operator, class_obj=class_obj, is_lvalue=is_lvalue + ) + + # We don't use ATTR_DEFINED error code below (since missing attributes can cause various + # other error codes), instead we perform quick node lookup with all the fallbacks. + info = itype.type + sym = info.get(name) + node = sym.node if sym else None + if not node: + name_not_found = True + if ( + name not in ["__getattr__", "__setattr__", "__getattribute__"] + and not is_operator + and not class_obj + and itype.extra_attrs is None # skip ModuleType.__getattr__ + ): + for method_name in ("__getattribute__", "__getattr__"): + method = info.get_method(method_name) + if method and method.info.fullname != "builtins.object": + name_not_found = False + break + if name_not_found: + if info.fallback_to_any or class_obj and info.meta_fallback_to_any: + return AnyType(TypeOfAny.special_form) + if itype.extra_attrs and name in itype.extra_attrs.attrs: + return itype.extra_attrs.attrs[name] + return None + + from mypy.checkmember import ( + MemberContext, + analyze_class_attribute_access, + analyze_instance_member_access, + ) + + mx = MemberContext( + is_lvalue=is_lvalue, + is_super=False, + is_operator=is_operator, + original_type=TypeType.make_normalized(itype) if class_obj else itype, + self_type=TypeType.make_normalized(subtype) if class_obj else subtype, + context=Context(), # all errors are filtered, but this is a required argument + chk=type_checker, + suppress_errors=True, + # This is needed to avoid infinite recursion in situations involving protocols like + # class P(Protocol[T]): + # def combine(self, other: P[S]) -> P[Tuple[T, S]]: ... + # Normally we call freshen_all_functions_type_vars() during attribute access, + # to avoid type variable id collisions, but for protocols this means we can't + # use the assumption stack, that will grow indefinitely. + # TODO: find a cleaner solution that doesn't involve massive perf impact. + preserve_type_var_ids=True, + ) + with type_checker.msg.filter_errors(filter_deprecated=True): + if class_obj: + fallback = itype.type.metaclass_type or mx.named_type("builtins.type") + return analyze_class_attribute_access(itype, name, mx, mcs_fallback=fallback) + else: + return analyze_instance_member_access(name, itype, mx, info) + + +def find_member_simple( + name: str, + itype: Instance, + subtype: Type, + *, + is_operator: bool = False, + class_obj: bool = False, + is_lvalue: bool = False, ) -> Type | None: """Find the type of member by 'name' in 'itype's TypeInfo. Find the member type after applying type arguments from 'itype', and binding 'self' to 'subtype'. Return None if member was not found. """ - # TODO: this code shares some logic with checkmember.analyze_member_access, - # consider refactoring. info = itype.type method = info.get_method(name) if method: @@ -1257,7 +1387,10 @@ def find_member( assert isinstance(method, OverloadedFuncDef) dec = method.items[0] assert isinstance(dec, Decorator) - return find_node_type(dec.var, itype, subtype, class_obj=class_obj) + # Pass on is_lvalue flag as this may be a property with different setter type. + return find_node_type( + dec.var, itype, subtype, class_obj=class_obj, is_lvalue=is_lvalue + ) return find_node_type(method, itype, subtype, class_obj=class_obj) else: # don't have such method, maybe variable or decorator? @@ -1322,7 +1455,10 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set dec = method.items[0] assert isinstance(dec, Decorator) if dec.var.is_settable_property or setattr_meth: - return {IS_VAR, IS_SETTABLE} + flags = {IS_VAR, IS_SETTABLE} + if dec.var.setter_type is not None: + flags.add(IS_EXPLICIT_SETTER) + return flags else: return {IS_VAR} return set() # Just a regular method @@ -1344,7 +1480,8 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set flags = {IS_VAR} if not v.is_final: flags.add(IS_SETTABLE) - if v.is_classvar: + # TODO: define cleaner rules for class vs instance variables. + if v.is_classvar and not is_descriptor(v.type): flags.add(IS_CLASSVAR) if class_obj and v.is_inferred: flags.add(IS_CLASSVAR) @@ -1352,8 +1489,21 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set return set() +def is_descriptor(typ: Type | None) -> bool: + typ = get_proper_type(typ) + if isinstance(typ, Instance): + return typ.type.get("__get__") is not None + if isinstance(typ, UnionType): + return all(is_descriptor(item) for item in typ.relevant_items()) + return False + + def find_node_type( - node: Var | FuncBase, itype: Instance, subtype: Type, class_obj: bool = False + node: Var | FuncBase, + itype: Instance, + subtype: Type, + class_obj: bool = False, + is_lvalue: bool = False, ) -> Type: """Find type of a variable or method 'node' (maybe also a decorated method). Apply type arguments from 'itype', and bind 'self' to 'subtype'. @@ -1365,7 +1515,13 @@ def find_node_type( node, fallback=Instance(itype.type.mro[-1], []) ) else: - typ = node.type + # This part and the one below are simply copies of the logic from checkmember.py. + if node.is_settable_property and is_lvalue: + typ = node.setter_type + if typ is None and node.is_ready: + typ = node.type + else: + typ = node.type if typ is not None: typ = expand_self_type(node, typ, subtype) p_typ = get_proper_type(typ) @@ -1389,7 +1545,15 @@ def find_node_type( ) if node.is_property and not class_obj: assert isinstance(signature, CallableType) - typ = signature.ret_type + if ( + isinstance(node, Var) + and node.is_settable_property + and is_lvalue + and node.setter_type is not None + ): + typ = signature.arg_types[0] + else: + typ = signature.ret_type else: typ = signature itype = map_instance_to_supertype(itype, node.info) @@ -1616,17 +1780,18 @@ def are_parameters_compatible( return True trivial_suffix = is_trivial_suffix(right) and not is_proper_subtype + trivial_vararg_suffix = False if ( - right.arg_kinds == [ARG_STAR] - and isinstance(get_proper_type(right.arg_types[0]), AnyType) + right.arg_kinds[-1:] == [ARG_STAR] + and isinstance(get_proper_type(right.arg_types[-1]), AnyType) and not is_proper_subtype + and all(k.is_positional(star=True) for k in left.arg_kinds) ): # Similar to how (*Any, **Any) is considered a supertype of all callables, we consider # (*Any) a supertype of all callables with positional arguments. This is needed in # particular because we often refuse to try type inference if actual type is not # a subtype of erased template type. - if all(k.is_positional() for k in left.arg_kinds) and ignore_pos_arg_names: - return True + trivial_vararg_suffix = True # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must @@ -1660,7 +1825,11 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N return not allow_partial_overlap and not trivial_suffix return not is_compat(right_arg.typ, left_arg.typ) - if _incompatible(left_star, right_star) or _incompatible(left_star2, right_star2): + if ( + _incompatible(left_star, right_star) + and not trivial_vararg_suffix + or _incompatible(left_star2, right_star2) + ): return False # Phase 1b: Check non-star args: for every arg right can accept, left must @@ -1682,11 +1851,16 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N ): return False + if trivial_suffix: + # For trivial right suffix we *only* check that every non-star right argument + # has a valid match on the left. + return True + # Phase 1c: Check var args. Right has an infinite series of optional positional # arguments. Get all further positional args of left, and make sure # they're more general than the corresponding member in right. - # TODO: are we handling UnpackType correctly here? - if right_star is not None and not trivial_suffix: + # TODO: handle suffix in UnpackType (i.e. *args: *Tuple[Ts, X, Y]). + if right_star is not None and not trivial_vararg_suffix: # Synthesize an anonymous formal argument for the right right_by_position = right.try_synthesizing_arg_from_vararg(None) assert right_by_position is not None @@ -1713,7 +1887,7 @@ def _incompatible(left_arg: FormalArgument | None, right_arg: FormalArgument | N # Phase 1d: Check kw args. Right has an infinite series of optional named # arguments. Get all further named args of left, and make sure # they're more general than the corresponding member in right. - if right_star2 is not None and not trivial_suffix: + if right_star2 is not None: right_names = {name for name in right.arg_names if name is not None} left_only_names = set() for name, kind in zip(left.arg_names, left.arg_kinds): @@ -1882,7 +2056,7 @@ def unify_generic_callable( ) if None in inferred_vars: return None - non_none_inferred_vars = cast(List[Type], inferred_vars) + non_none_inferred_vars = cast(list[Type], inferred_vars) had_errors = False def report(*args: Any) -> None: @@ -1922,7 +2096,7 @@ def try_restrict_literal_union(t: UnionType, s: Type) -> list[Type] | None: return new_items -def restrict_subtype_away(t: Type, s: Type) -> Type: +def restrict_subtype_away(t: Type, s: Type, *, consider_runtime_isinstance: bool = True) -> Type: """Return t minus s for runtime type assertions. If we can't determine a precise result, return a supertype of the @@ -1936,16 +2110,27 @@ def restrict_subtype_away(t: Type, s: Type) -> Type: new_items = try_restrict_literal_union(p_t, s) if new_items is None: new_items = [ - restrict_subtype_away(item, s) + restrict_subtype_away( + item, s, consider_runtime_isinstance=consider_runtime_isinstance + ) for item in p_t.relevant_items() - if (isinstance(get_proper_type(item), AnyType) or not covers_at_runtime(item, s)) ] - return UnionType.make_union(new_items) + return UnionType.make_union( + [item for item in new_items if not isinstance(get_proper_type(item), UninhabitedType)] + ) 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() + + if consider_runtime_isinstance: + if covers_at_runtime(t, s): + return UninhabitedType() + else: + return t else: + if is_proper_subtype(t, s, ignore_promotions=True): + return UninhabitedType() + if is_proper_subtype(t, s, ignore_promotions=True, erase_instances=True): + return UninhabitedType() return t @@ -1955,7 +2140,8 @@ def covers_at_runtime(item: Type, supertype: Type) -> bool: supertype = get_proper_type(supertype) # Since runtime type checks will ignore type arguments, erase the types. - supertype = erase_type(supertype) + if not (isinstance(supertype, FunctionLike) and supertype.is_type_obj()): + supertype = erase_type(supertype) if is_proper_subtype( erase_type(item), supertype, ignore_promotions=True, erase_instances=True ): @@ -2037,6 +2223,7 @@ def infer_variance(info: TypeInfo, i: int) -> bool: # Special case to avoid false positives (and to pass conformance tests) settable = False + # TODO: handle settable properties with setter type different from getter. typ = find_member(member, self_type, self_type) if typ: # It's okay for a method in a generic class with a contravariant type @@ -2108,3 +2295,20 @@ def erase_return_self_types(typ: Type, self_type: Instance) -> Type: ] ) return typ + + +def is_erased_instance(t: Instance) -> bool: + """Is this an instance where all args are Any types?""" + if not t.args: + return False + for arg in t.args: + if isinstance(arg, UnpackType): + unpacked = get_proper_type(arg.type) + if not isinstance(unpacked, Instance): + return False + assert unpacked.type.fullname == "builtins.tuple" + if not isinstance(get_proper_type(unpacked.args[0]), AnyType): + return False + elif not isinstance(get_proper_type(arg), AnyType): + return False + return True diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 268f3032fc9b4..756cf6ae2ccdc 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -27,9 +27,10 @@ import itertools import json import os +import sys +from collections.abc import Iterator from contextlib import contextmanager -from typing import Callable, Iterator, NamedTuple, TypeVar, cast -from typing_extensions import TypedDict +from typing import Callable, NamedTuple, TypedDict, TypeVar, cast from mypy.argmap import map_actuals_to_formals from mypy.build import Graph, State @@ -52,14 +53,14 @@ SymbolNode, SymbolTable, TypeInfo, - reverse_builtin_aliases, + Var, ) from mypy.options import Options from mypy.plugin import FunctionContext, MethodContext, Plugin from mypy.server.update import FineGrainedBuildManager from mypy.state import state from mypy.traverser import TraverserVisitor -from mypy.typeops import make_simplified_union +from mypy.typeops import bind_self, make_simplified_union from mypy.types import ( AnyType, CallableType, @@ -229,6 +230,18 @@ def is_implicit_any(typ: Type) -> bool: return isinstance(typ, AnyType) and not is_explicit_any(typ) +def _arg_accepts_function(typ: ProperType) -> bool: + return ( + # TypeVar / Callable + isinstance(typ, (TypeVarType, CallableType)) + or + # Protocol with __call__ + isinstance(typ, Instance) + and typ.type.is_protocol + and typ.type.get_method("__call__") is not None + ) + + class SuggestionEngine: """Engine for finding call sites and suggesting signatures.""" @@ -454,7 +467,7 @@ def get_guesses_from_parent(self, node: FuncDef) -> list[CallableType]: pnode = parent.names.get(node.name) if pnode and isinstance(pnode.node, (FuncDef, Decorator)): typ = get_proper_type(pnode.node.type) - # FIXME: Doesn't work right with generic tyeps + # FIXME: Doesn't work right with generic types if isinstance(typ, CallableType) and len(typ.arg_types) == len(node.arguments): # Return the first thing we find, since it probably doesn't make sense # to grab things further up in the chain if an earlier parent has it. @@ -474,7 +487,7 @@ def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature: if self.no_errors and orig_errors: raise SuggestionFailure("Function does not typecheck.") - is_method = bool(node.info) and not node.is_static + is_method = bool(node.info) and node.has_self_or_cls_argument with state.strict_optional_set(graph[mod].options.strict_optional): guesses = self.get_guesses( @@ -537,12 +550,17 @@ def find_node(self, key: str) -> tuple[str, str, FuncDef]: # TODO: Also return OverloadedFuncDef -- currently these are ignored. node: SymbolNode | None = None if ":" in key: - if key.count(":") > 1: + # A colon might be part of a drive name on Windows (like `C:/foo/bar`) + # and is also used as a delimiter between file path and lineno. + # If a colon is there for any of those reasons, it must be a file+line + # reference. + platform_key_count = 2 if sys.platform == "win32" else 1 + if key.count(":") > platform_key_count: raise SuggestionFailure( "Malformed location for function: {}. Must be either" " package.module.Class.method or path/to/file.py:line".format(key) ) - file, line = key.split(":") + file, line = key.rsplit(":", 1) if not line.isdigit(): raise SuggestionFailure(f"Line number must be a number. Got {line}") line_number = int(line) @@ -638,22 +656,27 @@ def find_node_by_file_and_line(self, file: str, line: int) -> tuple[str, SymbolN def extract_from_decorator(self, node: Decorator) -> FuncDef | None: for dec in node.decorators: typ = None - if isinstance(dec, RefExpr) and isinstance(dec.node, FuncDef): - typ = dec.node.type + if isinstance(dec, RefExpr) and isinstance(dec.node, (Var, FuncDef)): + typ = get_proper_type(dec.node.type) elif ( isinstance(dec, CallExpr) and isinstance(dec.callee, RefExpr) - and isinstance(dec.callee.node, FuncDef) - and isinstance(dec.callee.node.type, CallableType) + and isinstance(dec.callee.node, (Decorator, FuncDef, Var)) + and isinstance((call_tp := get_proper_type(dec.callee.node.type)), CallableType) ): - typ = get_proper_type(dec.callee.node.type.ret_type) + typ = get_proper_type(call_tp.ret_type) + + if isinstance(typ, Instance): + call_method = typ.type.get_method("__call__") + if isinstance(call_method, FuncDef) and isinstance(call_method.type, FunctionLike): + typ = bind_self(call_method.type, None) if not isinstance(typ, FunctionLike): return None for ct in typ.items: if not ( len(ct.arg_types) == 1 - and isinstance(ct.arg_types[0], TypeVarType) + and _arg_accepts_function(get_proper_type(ct.arg_types[0])) and ct.arg_types[0] == ct.ret_type ): return None @@ -824,8 +847,6 @@ def visit_instance(self, t: Instance) -> str: s = t.type.fullname or t.type.name or None if s is None: return "" - if s in reverse_builtin_aliases: - s = reverse_builtin_aliases[s] mod_obj = split_target(self.graph, s) assert mod_obj @@ -837,7 +858,7 @@ def visit_instance(self, t: Instance) -> str: if self.module: parts = obj.split(".") # need to split the object part if it is a nested class tree = self.graph[self.module].tree - if tree and parts[0] in tree.names: + if tree and parts[0] in tree.names and mod not in tree.names: mod = self.module if (mod, obj) == ("builtins", "tuple"): @@ -869,7 +890,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> str: def visit_union_type(self, t: UnionType) -> str: if len(t.items) == 2 and is_overlapping_none(t): - return f"Optional[{remove_optional(t).accept(self)}]" + s = remove_optional(t).accept(self) + return f"{s} | None" if self.options.use_or_syntax() else f"Optional[{s}]" else: return super().visit_union_type(t) diff --git a/mypy/test/config.py b/mypy/test/config.py index 3806cf3dfa139..2dc4208b1e9d9 100644 --- a/mypy/test/config.py +++ b/mypy/test/config.py @@ -18,6 +18,9 @@ # It is also hard-coded in numerous places, so don't change it. test_temp_dir = "tmp" +# Mypyc tests may write intermediate files (e.g. generated C) here on failure +mypyc_output_dir = os.path.join(PREFIX, ".mypyc_test_output") + # The PEP 561 tests do a bunch of pip installs which, even though they operate # on distinct temporary virtual environments, run into race conditions on shared # file-system state. To make this work reliably in parallel mode, we'll use a diff --git a/mypy/test/data.py b/mypy/test/data.py index ee567afe2125e..5b0ad84c0ba77 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -10,15 +10,17 @@ import sys import tempfile from abc import abstractmethod +from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path -from typing import Any, Final, Iterator, NamedTuple, NoReturn, Pattern, Union +from re import Pattern +from typing import Any, Final, NamedTuple, NoReturn, Union from typing_extensions import TypeAlias as _TypeAlias import pytest from mypy import defaults -from mypy.test.config import PREFIX, test_data_prefix, test_temp_dir +from mypy.test.config import PREFIX, mypyc_output_dir, test_data_prefix, test_temp_dir root_dir = os.path.normpath(PREFIX) @@ -244,7 +246,7 @@ class DataDrivenTestCase(pytest.Item): """Holds parsed data-driven test cases, and handles directory setup and teardown.""" # Override parent member type - parent: DataSuiteCollector + parent: DataFileCollector input: list[str] output: list[str] # Output for the first pass @@ -275,7 +277,7 @@ class DataDrivenTestCase(pytest.Item): def __init__( self, - parent: DataSuiteCollector, + parent: DataFileCollector, suite: DataSuite, *, file: str, @@ -289,6 +291,7 @@ def __init__( data: str, line: int, ) -> None: + assert isinstance(parent, DataFileCollector) super().__init__(name, parent) self.suite = suite self.file = file @@ -304,7 +307,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 +326,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 +374,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 @@ -587,6 +587,13 @@ def fix_cobertura_filename(line: str) -> str: ## +def pytest_sessionstart(session: Any) -> None: + # Clean up directory where mypyc tests write intermediate files on failure + # to avoid any confusion between test runs + if os.path.isdir(mypyc_output_dir): + shutil.rmtree(mypyc_output_dir) + + # This function name is special to pytest. See # https://docs.pytest.org/en/latest/reference.html#initialization-hooks def pytest_addoption(parser: Any) -> None: diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 4a80207d3ec7c..8ff6874e746a6 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -8,7 +8,9 @@ import shutil import sys import time -from typing import IO, Any, Callable, Iterable, Iterator, Pattern +from collections.abc import Iterable, Iterator +from re import Pattern +from typing import IO, Any, Callable # Exporting Suite as alias to TestCase for backwards compatibility # TODO: avoid aliasing - import and subclass TestCase directly @@ -231,6 +233,9 @@ def clean_up(a: list[str]) -> list[str]: for p in prefix, prefix.replace(os.sep, "/"): if p != "/" and p != "//" and p != "\\" and p != "\\\\": ss = ss.replace(p, "") + # Replace memory address with zeros + if "at 0x" in ss: + ss = re.sub(r"(at 0x)\w+>", r"\g<1>000000000000>", ss) # Ignore spaces at end of line. ss = re.sub(" +$", "", ss) # Remove pwd from driver.py's path @@ -256,11 +261,12 @@ def local_sys_path_set() -> Iterator[None]: def testfile_pyversion(path: str) -> tuple[int, int]: - m = re.search(r"python3([0-9]+)\.test$", path) - if m: - return 3, int(m.group(1)) + if m := re.search(r"python3([0-9]+)\.test$", path): + # For older unsupported version like python38, + # default to that earliest supported version. + return max((3, int(m.group(1))), defaults.PYTHON3_VERSION_MIN) else: - return defaults.PYTHON3_VERSION + return defaults.PYTHON3_VERSION_MIN def normalize_error_messages(messages: list[str]) -> list[str]: @@ -351,7 +357,6 @@ def parse_options( options = Options() options.error_summary = False options.hide_error_codes = True - options.force_uppercase_builtins = True options.force_union_syntax = True # Allow custom python version to override testfile_pyversion. @@ -411,8 +416,7 @@ def check_test_output_files( testcase: DataDrivenTestCase, step: int, strip_prefix: str = "" ) -> None: for path, expected_content in testcase.output_files: - if path.startswith(strip_prefix): - path = path[len(strip_prefix) :] + path = path.removeprefix(strip_prefix) if not os.path.exists(path): raise AssertionError( "Expected file {} was not produced by test case{}".format( @@ -440,6 +444,8 @@ def check_test_output_files( if testcase.suite.native_sep and os.path.sep == "\\": normalized_output = [fix_cobertura_filename(line) for line in normalized_output] normalized_output = normalize_error_messages(normalized_output) + if os.path.basename(testcase.file) == "reports.test": + normalized_output = normalize_report_meta(normalized_output) assert_string_arrays_equal( expected_content.splitlines(), normalized_output, @@ -463,6 +469,13 @@ def normalize_file_output(content: list[str], current_abs_path: str) -> list[str return result +def normalize_report_meta(content: list[str]) -> list[str]: + # libxml 2.15 and newer emits the "modern" version of this element. + # Normalize the old style to look the same. + html_meta = '' + return ['' if x == html_meta else x for x in content] + + def find_test_files(pattern: str, exclude: list[str] | None = None) -> list[str]: return [ path.name diff --git a/mypy/test/meta/_pytest.py b/mypy/test/meta/_pytest.py index b8648f033143a..0caa6b8694b79 100644 --- a/mypy/test/meta/_pytest.py +++ b/mypy/test/meta/_pytest.py @@ -3,9 +3,9 @@ import sys import textwrap import uuid +from collections.abc import Iterable from dataclasses import dataclass from pathlib import Path -from typing import Iterable from mypy.test.config import test_data_prefix diff --git a/mypy/test/meta/test_parse_data.py b/mypy/test/meta/test_parse_data.py index bff2d69776125..8c6fc1610e63d 100644 --- a/mypy/test/meta/test_parse_data.py +++ b/mypy/test/meta/test_parse_data.py @@ -50,13 +50,13 @@ def test_bad_ge_version_check(self) -> None: """ [case abc] s: str - [out version>=3.8] + [out version>=3.9] abc """ ) # Assert - assert "version>=3.8 always true since minimum runtime version is (3, 8)" in actual.stdout + assert "version>=3.9 always true since minimum runtime version is (3, 9)" in actual.stdout def test_bad_eq_version_check(self) -> None: # Act @@ -70,4 +70,4 @@ def test_bad_eq_version_check(self) -> None: ) # Assert - assert "version==3.7 always false since minimum runtime version is (3, 8)" in actual.stdout + assert "version==3.7 always false since minimum runtime version is (3, 9)" in actual.stdout diff --git a/mypy/test/test_config_parser.py b/mypy/test/test_config_parser.py new file mode 100644 index 0000000000000..597143738f23c --- /dev/null +++ b/mypy/test/test_config_parser.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import contextlib +import os +import tempfile +import unittest +from collections.abc import Iterator +from pathlib import Path + +from mypy.config_parser import _find_config_file +from mypy.defaults import CONFIG_NAMES, SHARED_CONFIG_NAMES + + +@contextlib.contextmanager +def chdir(target: Path) -> Iterator[None]: + # Replace with contextlib.chdir in Python 3.11 + dir = os.getcwd() + os.chdir(target) + try: + yield + finally: + os.chdir(dir) + + +def write_config(path: Path, content: str | None = None) -> None: + if path.suffix == ".toml": + if content is None: + content = "[tool.mypy]\nstrict = true" + path.write_text(content) + else: + if content is None: + content = "[mypy]\nstrict = True" + path.write_text(content) + + +class FindConfigFileSuite(unittest.TestCase): + + def test_no_config(self) -> None: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + (tmpdir / ".git").touch() + with chdir(tmpdir): + result = _find_config_file() + assert result is None + + def test_parent_config_with_and_without_git(self) -> None: + for name in CONFIG_NAMES + SHARED_CONFIG_NAMES: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + + config = tmpdir / name + write_config(config) + + child = tmpdir / "child" + child.mkdir() + + with chdir(child): + result = _find_config_file() + assert result is not None + assert Path(result[2]).resolve() == config.resolve() + + git = child / ".git" + git.touch() + + result = _find_config_file() + assert result is None + + git.unlink() + result = _find_config_file() + assert result is not None + hg = child / ".hg" + hg.touch() + + result = _find_config_file() + assert result is None + + def test_precedence(self) -> None: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + + pyproject = tmpdir / "pyproject.toml" + setup_cfg = tmpdir / "setup.cfg" + mypy_ini = tmpdir / "mypy.ini" + dot_mypy = tmpdir / ".mypy.ini" + + child = tmpdir / "child" + child.mkdir() + + for cwd in [tmpdir, child]: + write_config(pyproject) + write_config(setup_cfg) + write_config(mypy_ini) + write_config(dot_mypy) + + with chdir(cwd): + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == "mypy.ini" + + mypy_ini.unlink() + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == ".mypy.ini" + + dot_mypy.unlink() + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == "pyproject.toml" + + pyproject.unlink() + result = _find_config_file() + assert result is not None + assert os.path.basename(result[2]) == "setup.cfg" + + def test_precedence_missing_section(self) -> None: + with tempfile.TemporaryDirectory() as _tmpdir: + tmpdir = Path(_tmpdir) + + child = tmpdir / "child" + child.mkdir() + + parent_mypy = tmpdir / "mypy.ini" + child_pyproject = child / "pyproject.toml" + write_config(parent_mypy) + write_config(child_pyproject, content="") + + with chdir(child): + result = _find_config_file() + assert result is not None + assert Path(result[2]).resolve() == parent_mypy.resolve() diff --git a/mypy/test/test_find_sources.py b/mypy/test/test_find_sources.py index 21ba0903a8243..321f3405e9991 100644 --- a/mypy/test/test_find_sources.py +++ b/mypy/test/test_find_sources.py @@ -17,20 +17,20 @@ class FakeFSCache(FileSystemCache): def __init__(self, files: set[str]) -> None: self.files = {os.path.abspath(f) for f in files} - def isfile(self, file: str) -> bool: - return file in self.files + def isfile(self, path: str) -> bool: + return path in self.files - def isdir(self, dir: str) -> bool: - if not dir.endswith(os.sep): - dir += os.sep - return any(f.startswith(dir) for f in self.files) + def isdir(self, path: str) -> bool: + if not path.endswith(os.sep): + path += os.sep + return any(f.startswith(path) for f in self.files) - def listdir(self, dir: str) -> list[str]: - if not dir.endswith(os.sep): - dir += os.sep - return list({f[len(dir) :].split(os.sep)[0] for f in self.files if f.startswith(dir)}) + def listdir(self, path: str) -> list[str]: + if not path.endswith(os.sep): + path += os.sep + return list({f[len(path) :].split(os.sep)[0] for f in self.files if f.startswith(path)}) - def init_under_package_root(self, file: str) -> bool: + def init_under_package_root(self, path: str) -> bool: return False diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 330e191af252d..f2b7057d9f20d 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -5,9 +5,10 @@ import os import re import sys +import tempfile +from pathlib import Path from mypy import build -from mypy.build import Graph from mypy.errors import CompileError from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths from mypy.test.config import test_data_prefix, test_temp_dir @@ -37,8 +38,6 @@ typecheck_files = find_test_files(pattern="check-*.test") # Tests that use Python version specific features: -if sys.version_info < (3, 9): - typecheck_files.remove("check-python39.test") if sys.version_info < (3, 10): typecheck_files.remove("check-python310.test") if sys.version_info < (3, 11): @@ -47,16 +46,19 @@ 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"): - typecheck_files.remove("check-modules-case.test") +if sys.version_info < (3, 14): + typecheck_files.remove("check-python314.test") class TypeCheckSuite(DataSuite): files = typecheck_files def run_case(self, testcase: DataDrivenTestCase) -> None: + if os.path.basename(testcase.file) == "check-modules-case.test": + with tempfile.NamedTemporaryFile(prefix="test", dir=".") as temp_file: + temp_path = Path(temp_file.name) + if not temp_path.with_name(temp_path.name.upper()).exists(): + pytest.skip("File system is not case‐insensitive") if lxml is None and os.path.basename(testcase.file) == "check-reports.test": pytest.skip("Cannot import lxml. Is it installed?") incremental = ( @@ -138,9 +140,7 @@ def run_case_once( options.hide_error_codes = False if "abstract" not in testcase.file: options.allow_empty_bodies = not testcase.name.endswith("_no_empty") - if "lowercase" not in testcase.file: - options.force_uppercase_builtins = True - if "union-error" not in testcase.file: + if "union-error" not in testcase.file and "Pep604" not in testcase.name: options.force_union_syntax = True if incremental_step and options.incremental: @@ -163,11 +163,13 @@ def run_case_once( sys.path.insert(0, plugin_dir) res = None + blocker = False try: res = build.build(sources=sources, options=options, alt_lib_path=test_temp_dir) a = res.errors except CompileError as e: a = e.messages + blocker = True finally: assert sys.path[0] == plugin_dir del sys.path[0] @@ -198,7 +200,7 @@ def run_case_once( if res: if options.cache_dir != os.devnull: - self.verify_cache(module_data, res.errors, res.manager, res.graph) + self.verify_cache(module_data, res.manager, blocker, incremental_step) name = "targets" if incremental_step: @@ -230,40 +232,25 @@ def run_case_once( def verify_cache( self, module_data: list[tuple[str, str, str]], - a: list[str], manager: build.BuildManager, - graph: Graph, + blocker: bool, + step: int, ) -> None: - # There should be valid cache metadata for each module except - # for those that had an error in themselves or one of their - # dependencies. - error_paths = self.find_error_message_paths(a) - busted_paths = {m.path for id, m in manager.modules.items() if graph[id].transitive_error} - modules = self.find_module_files(manager) - modules.update({module_name: path for module_name, path, text in module_data}) - missing_paths = self.find_missing_cache_files(modules, manager) - # We would like to assert error_paths.issubset(busted_paths) - # but this runs into trouble because while some 'notes' are - # really errors that cause an error to be marked, many are - # just notes attached to other errors. - assert error_paths or not busted_paths, "Some modules reported error despite no errors" - if not missing_paths == busted_paths: - raise AssertionError(f"cache data discrepancy {missing_paths} != {busted_paths}") + if not blocker: + # There should be valid cache metadata for each module except + # in case of a blocking error in themselves or one of their + # dependencies. + modules = self.find_module_files(manager) + modules.update({module_name: path for module_name, path, text in module_data}) + missing_paths = self.find_missing_cache_files(modules, manager) + if missing_paths: + raise AssertionError(f"cache data missing for {missing_paths} on run {step}") assert os.path.isfile(os.path.join(manager.options.cache_dir, ".gitignore")) cachedir_tag = os.path.join(manager.options.cache_dir, "CACHEDIR.TAG") assert os.path.isfile(cachedir_tag) with open(cachedir_tag) as f: assert f.read().startswith("Signature: 8a477f597d28d172789f06886806bc55") - def find_error_message_paths(self, a: list[str]) -> set[str]: - hits = set() - for line in a: - m = re.match(r"([^\s:]+):(\d+:)?(\d+:)? (error|warning|note):", line) - if m: - p = m.group(1) - hits.add(p) - return hits - def find_module_files(self, manager: build.BuildManager) -> dict[str, str]: return {id: module.path for id, module in manager.modules.items()} diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 9bc02d3199640..11d2290429789 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -61,8 +61,6 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None: args.append("--hide-error-codes") if "--disallow-empty-bodies" not in args: args.append("--allow-empty-bodies") - if "--no-force-uppercase-builtins" not in args: - args.append("--force-uppercase-builtins") if "--no-force-union-syntax" not in args: args.append("--force-union-syntax") # Type check the program. diff --git a/mypy/test/testconstraints.py b/mypy/test/testconstraints.py index a701a173cbaae..277694a328c98 100644 --- a/mypy/test/testconstraints.py +++ b/mypy/test/testconstraints.py @@ -62,7 +62,7 @@ def test_type_var_tuple_with_prefix_and_suffix(self) -> None: Constraint(type_var=fx.s, op=SUPERTYPE_OF, target=fx.d), } - def test_unpack_homogenous_tuple(self) -> None: + def test_unpack_homogeneous_tuple(self) -> None: fx = self.fx assert set( infer_constraints( @@ -77,7 +77,7 @@ def test_unpack_homogenous_tuple(self) -> None: Constraint(type_var=fx.t, op=SUBTYPE_OF, target=fx.b), } - def test_unpack_homogenous_tuple_with_prefix_and_suffix(self) -> None: + def test_unpack_homogeneous_tuple_with_prefix_and_suffix(self) -> None: fx = self.fx assert set( infer_constraints( diff --git a/mypy/test/testexportjson.py b/mypy/test/testexportjson.py new file mode 100644 index 0000000000000..13bd96d066427 --- /dev/null +++ b/mypy/test/testexportjson.py @@ -0,0 +1,70 @@ +"""Test cases for the mypy cache JSON export tool.""" + +from __future__ import annotations + +import json +import os +import re +import sys + +from mypy import build +from mypy.errors import CompileError +from mypy.exportjson import convert_binary_cache_to_json +from mypy.modulefinder import BuildSource +from mypy.options import Options +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite +from mypy.test.helpers import assert_string_arrays_equal + + +class TypeExportSuite(DataSuite): + required_out_section = True + files = ["exportjson.test"] + + def run_case(self, testcase: DataDrivenTestCase) -> None: + error = False + src = "\n".join(testcase.input) + try: + options = Options() + options.use_builtins_fixtures = True + options.show_traceback = True + options.allow_empty_bodies = True + options.fixed_format_cache = True + fnam = os.path.join(self.base_path, "main.py") + with open(fnam, "w") as f: + f.write(src) + result = build.build( + sources=[BuildSource(fnam, "main")], options=options, alt_lib_path=test_temp_dir + ) + a = result.errors + error = bool(a) + + major, minor = sys.version_info[:2] + cache_dir = os.path.join(".mypy_cache", f"{major}.{minor}") + + for module in result.files: + if module in ( + "builtins", + "typing", + "_typeshed", + "__future__", + "typing_extensions", + "sys", + ): + continue + fnam = os.path.join(cache_dir, f"{module}.data.ff") + with open(fnam, "rb") as f: + json_data = convert_binary_cache_to_json(f.read(), implicit_names=False) + for line in json.dumps(json_data, indent=4).splitlines(): + if '"path": ' in line: + # We source file path is unpredictable, so filter it out + line = re.sub(r'"[^"]+\.pyi?"', "...", line) + assert "ERROR" not in line, line + a.append(line) + except CompileError as e: + a = e.messages + error = True + if error or "\n".join(testcase.output).strip() != "": + assert_string_arrays_equal( + testcase.output, a, f"Invalid output ({testcase.file}, line {testcase.line})" + ) diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 800ba2dff0878..b098c1fb0ad2b 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -75,7 +75,6 @@ def should_skip(self, testcase: DataDrivenTestCase) -> bool: def run_case(self, testcase: DataDrivenTestCase) -> None: if self.should_skip(testcase): pytest.skip() - return main_src = "\n".join(testcase.input) main_path = os.path.join(test_temp_dir, "main") @@ -101,8 +100,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 +247,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/testfscache.py b/mypy/test/testfscache.py index 44b0d32f57977..529402dade96c 100644 --- a/mypy/test/testfscache.py +++ b/mypy/test/testfscache.py @@ -4,7 +4,6 @@ import os import shutil -import sys import tempfile import unittest @@ -83,7 +82,7 @@ def test_isfile_case_other_directory(self) -> None: assert self.isfile_case(os.path.join(other, "other_dir.py")) assert not self.isfile_case(os.path.join(other, "Other_Dir.py")) assert not self.isfile_case(os.path.join(other, "bar.py")) - if sys.platform in ("win32", "darwin"): + if os.path.exists(os.path.join(other, "PKG/other_dir.py")): # We only check case for directories under our prefix, and since # this path is not under the prefix, case difference is fine. assert self.isfile_case(os.path.join(other, "PKG/other_dir.py")) diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index 0355e75e8c34b..c87eb66c13046 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -3,7 +3,7 @@ from __future__ import annotations import sys -from typing import AbstractSet +from collections.abc import Set as AbstractSet from mypy.build import BuildManager, BuildSourceSet, State, order_ascc, sorted_components from mypy.errors import Errors @@ -65,8 +65,8 @@ def test_sorted_components(self) -> None: "b": State("b", None, "import c", manager), "c": State("c", None, "import b, d", manager), } - res = sorted_components(graph) - assert_equal(res, [frozenset({"d"}), frozenset({"c", "b"}), frozenset({"a"})]) + res = [scc.mod_ids for scc in sorted_components(graph)] + assert_equal(res, [{"d"}, {"c", "b"}, {"a"}]) def test_order_ascc(self) -> None: manager = self._make_manager() @@ -76,7 +76,7 @@ def test_order_ascc(self) -> None: "b": State("b", None, "import c", manager), "c": State("c", None, "import b, d", manager), } - res = sorted_components(graph) + res = [scc.mod_ids for scc in sorted_components(graph)] assert_equal(res, [frozenset({"a", "d", "c", "b"})]) ascc = res[0] scc = order_ascc(graph, ascc) diff --git a/mypy/test/testinfer.py b/mypy/test/testinfer.py index 08926c179623a..9c18624e02830 100644 --- a/mypy/test/testinfer.py +++ b/mypy/test/testinfer.py @@ -134,7 +134,7 @@ def expand_caller_kinds( def expand_callee_kinds( - kinds_and_names: list[ArgKind | tuple[ArgKind, str]] + kinds_and_names: list[ArgKind | tuple[ArgKind, str]], ) -> tuple[list[ArgKind], list[str | None]]: kinds = [] names: list[str | None] = [] @@ -366,7 +366,7 @@ def test_single_pair(self) -> None: ) def test_empty_pair_list(self) -> None: - # This case should never occur in practice -- ComparisionExprs + # This case should never occur in practice -- ComparisonExprs # always contain at least one comparison. But in case it does... self.assertEqual(group_comparison_operands([], {}, set()), []) diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 0582c9ed58822..c2c75f60be296 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -13,7 +13,6 @@ UNBOUND_IMPORTED, Expression, MypyFile, - Node, SymbolTable, SymbolTableNode, TypeInfo, @@ -103,7 +102,6 @@ def build(self, source: str, testcase: DataDrivenTestCase) -> BuildResult | None options.export_types = True options.show_traceback = True options.allow_empty_bodies = True - options.force_uppercase_builtins = True main_path = os.path.join(test_temp_dir, "main") self.str_conv.options = options @@ -172,10 +170,7 @@ def format_symbol_table_node(self, node: SymbolTableNode) -> str: if node.kind == UNBOUND_IMPORTED: return "UNBOUND_IMPORTED" return "None" - if isinstance(node.node, Node): - s = f"{str(type(node.node).__name__)}<{self.id_mapper.id(node.node)}>" - else: - s = f"? ({type(node.node)})" + s = f"{str(type(node.node).__name__)}<{self.id_mapper.id(node.node)}>" if ( isinstance(node.node, Var) and node.node.type diff --git a/mypy/test/testmodulefinder.py b/mypy/test/testmodulefinder.py index 943913d6cadb1..d4ee3af041c5e 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 = [ @@ -195,6 +195,9 @@ def test__packages_with_ns(self) -> None: ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")), ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")), ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND), + # Regular package with py.typed, bundled stubs, and external stubs-only package + ("pkg_typed_w_stubs", self.path("pkg_typed_w_stubs-stubs", "__init__.pyi")), + ("pkg_typed_w_stubs.spam", self.path("pkg_typed_w_stubs-stubs", "spam.pyi")), # Regular package without py.typed ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), @@ -214,7 +217,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 {}" @@ -250,6 +253,9 @@ def test__packages_without_ns(self) -> None: ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")), ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")), ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND), + # Regular package with py.typed, bundled stubs, and external stubs-only package + ("pkg_typed_w_stubs", self.path("pkg_typed_w_stubs-stubs", "__init__.pyi")), + ("pkg_typed_w_stubs.spam", self.path("pkg_typed_w_stubs-stubs", "spam.pyi")), # Regular package without py.typed ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), @@ -269,7 +275,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..c8bcb5c0d807f 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -25,6 +25,10 @@ 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") + if sys.version_info < (3, 14): + files.remove("parse-python314.test") def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -36,13 +40,16 @@ def test_parser(testcase: DataDrivenTestCase) -> None: The argument contains the description of the test case. """ options = Options() - options.force_uppercase_builtins = True options.hide_error_codes = True if testcase.file.endswith("python310.test"): 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) + elif testcase.file.endswith("python314.test"): + options.python_version = (3, 14) else: options.python_version = defaults.PYTHON3_VERSION diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index 9d2628c1fa5f8..0afb69bc0c998 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -5,8 +5,8 @@ import subprocess import sys import tempfile +from collections.abc import Iterator from contextlib import contextmanager -from typing import Iterator import filelock @@ -23,8 +23,8 @@ class PEP561Suite(DataSuite): files = ["pep561.test"] base_path = "." - def run_case(self, test_case: DataDrivenTestCase) -> None: - test_pep561(test_case) + def run_case(self, testcase: DataDrivenTestCase) -> None: + test_pep561(testcase) @contextmanager @@ -52,7 +52,6 @@ def upgrade_pip(python_executable: str) -> None: sys.version_info >= (3, 11) or (3, 10, 3) <= sys.version_info < (3, 11) or (3, 9, 11) <= sys.version_info < (3, 10) - or (3, 8, 13) <= sys.version_info < (3, 9) ): # Skip for more recent Python releases which come with pip>=21.3.1 # out of the box - for performance reasons. @@ -146,8 +145,11 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: output.append(line[len(test_temp_dir + os.sep) :].rstrip("\r\n")) else: # Normalize paths so that the output is the same on Windows and Linux/macOS. - line = line.replace(test_temp_dir + os.sep, test_temp_dir + "/") - output.append(line.rstrip("\r\n")) + # Yes, this is naive: replace all slashes preceding first colon, if any. + path, *rest = line.split(":", maxsplit=1) + if rest: + path = path.replace(os.sep, "/") + output.append(":".join([path, *rest]).rstrip("\r\n")) iter_count = "" if i == 0 else f" on iteration {i + 1}" expected = testcase.output if i == 0 else testcase.output2.get(i + 1, []) @@ -174,38 +176,3 @@ def parse_mypy_args(line: str) -> list[str]: if not m: return [] # No args; mypy will spit out an error. return m.group(1).split() - - -def test_mypy_path_is_respected() -> None: - assert False - packages = "packages" - pkg_name = "a" - with tempfile.TemporaryDirectory() as temp_dir: - old_dir = os.getcwd() - os.chdir(temp_dir) - try: - # Create the pkg for files to go into - full_pkg_name = os.path.join(temp_dir, packages, pkg_name) - os.makedirs(full_pkg_name) - - # Create the empty __init__ file to declare a package - pkg_init_name = os.path.join(temp_dir, packages, pkg_name, "__init__.py") - open(pkg_init_name, "w", encoding="utf8").close() - - mypy_config_path = os.path.join(temp_dir, "mypy.ini") - with open(mypy_config_path, "w") as mypy_file: - mypy_file.write("[mypy]\n") - mypy_file.write(f"mypy_path = ./{packages}\n") - - with virtualenv() as venv: - venv_dir, python_executable = venv - - cmd_line_args = [] - if python_executable != sys.executable: - cmd_line_args.append(f"--python-executable={python_executable}") - cmd_line_args.extend(["--config-file", mypy_config_path, "--package", pkg_name]) - - out, err, returncode = mypy.api.run(cmd_line_args) - assert returncode == 0 - finally: - os.chdir(old_dir) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index baeea1853dedb..6d22aca07da7f 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -52,7 +52,6 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None "--no-error-summary", "--hide-error-codes", "--allow-empty-bodies", - "--force-uppercase-builtins", "--test-env", # Speeds up some checks ] interpreter = python3_path @@ -63,9 +62,9 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None additional_flags = m.group(1).split() for flag in additional_flags: if flag.startswith("--python-version="): - targetted_python_version = flag.split("=")[1] - targetted_major, targetted_minor = targetted_python_version.split(".") - if (int(targetted_major), int(targetted_minor)) > ( + targeted_python_version = flag.split("=")[1] + targeted_major, targeted_minor = targeted_python_version.split(".") + if (int(targeted_major), int(targeted_minor)) > ( sys.version_info.major, sys.version_info.minor, ): diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index cdecc47391682..741c03fc2dc28 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -3,7 +3,6 @@ from __future__ import annotations import sys -from typing import Dict from mypy import build from mypy.defaults import PYTHON3_VERSION @@ -45,7 +44,6 @@ def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Opti options.semantic_analysis_only = True options.show_traceback = True options.python_version = PYTHON3_VERSION - options.force_uppercase_builtins = True return options @@ -199,7 +197,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: ) -class TypeInfoMap(Dict[str, TypeInfo]): +class TypeInfoMap(dict[str, TypeInfo]): def __str__(self) -> str: a: list[str] = ["TypeInfoMap("] for x, y in sorted(self.items()): diff --git a/mypy/test/testsolve.py b/mypy/test/testsolve.py index 6566b03ef5e99..d60b2cb3fcc55 100644 --- a/mypy/test/testsolve.py +++ b/mypy/test/testsolve.py @@ -64,12 +64,14 @@ def test_multiple_variables(self) -> None: ) def test_no_constraints_for_var(self) -> None: - self.assert_solve([self.fx.t], [], [self.fx.uninhabited]) - self.assert_solve([self.fx.t, self.fx.s], [], [self.fx.uninhabited, self.fx.uninhabited]) + self.assert_solve([self.fx.t], [], [self.fx.a_uninhabited]) + self.assert_solve( + [self.fx.t, self.fx.s], [], [self.fx.a_uninhabited, self.fx.a_uninhabited] + ) self.assert_solve( [self.fx.t, self.fx.s], [self.supc(self.fx.s, self.fx.a)], - [self.fx.uninhabited, self.fx.a], + [self.fx.a_uninhabited, self.fx.a], ) def test_simple_constraints_with_dynamic_type(self) -> None: @@ -116,7 +118,7 @@ def test_poly_no_constraints(self) -> None: self.assert_solve( [self.fx.t, self.fx.u], [], - [self.fx.uninhabited, self.fx.uninhabited], + [self.fx.a_uninhabited, self.fx.a_uninhabited], allow_polymorphic=True, ) @@ -152,7 +154,7 @@ def test_poly_free_pair_with_bounds_uninhabited(self) -> None: self.assert_solve( [self.fx.ub, self.fx.uc], [self.subc(self.fx.ub, self.fx.uc)], - [self.fx.uninhabited, self.fx.uninhabited], + [self.fx.a_uninhabited, self.fx.a_uninhabited], [], allow_polymorphic=True, ) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e65a16c8f3956..43974cf8ec681 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, @@ -398,6 +399,164 @@ def test_infer_sig_from_docstring_bad_indentation(self) -> None: None, ) + def test_infer_sig_from_docstring_args_kwargs(self) -> None: + assert_equal( + infer_sig_from_docstring("func(*args, **kwargs) -> int", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="*args"), ArgSig(name="**kwargs")], + ret_type="int", + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(*args) -> int", "func"), + [FunctionSig(name="func", args=[ArgSig(name="*args")], ret_type="int")], + ) + + assert_equal( + infer_sig_from_docstring("func(**kwargs) -> int", "func"), + [FunctionSig(name="func", args=[ArgSig(name="**kwargs")], ret_type="int")], + ) + + @pytest.mark.xfail( + raises=AssertionError, reason="Arg and kwarg signature validation not implemented yet" + ) + def test_infer_sig_from_docstring_args_kwargs_errors(self) -> None: + # Double args + assert_equal(infer_sig_from_docstring("func(*args, *args2) -> int", "func"), []) + + # Double kwargs + assert_equal(infer_sig_from_docstring("func(**kw, **kw2) -> int", "func"), []) + + # args after kwargs + assert_equal(infer_sig_from_docstring("func(**kwargs, *args) -> int", "func"), []) + + def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: + assert_equal( + infer_sig_from_docstring("func(self, /) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="self")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(self, x, /) -> str", "func"), + [ + FunctionSig( + name="func", args=[ArgSig(name="self"), ArgSig(name="x")], ret_type="str" + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, y) -> int", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="int")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, *args) -> str", "func"), + [ + FunctionSig( + name="func", args=[ArgSig(name="x"), ArgSig(name="*args")], ret_type="str" + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, *, kwonly, **kwargs) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig(name="**kwargs")], + ret_type="str", + ) + ], + ) + + def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: + assert_equal( + infer_sig_from_docstring("func(*, x) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, *, y) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(*, x, y) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, *, kwonly, **kwargs) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig("**kwargs")], + ret_type="str", + ) + ], + ) + + def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> None: + assert_equal( + infer_sig_from_docstring("func(x, /, *, y) -> str", "func"), + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, y, *, z) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="y"), ArgSig(name="z")], + ret_type="str", + ) + ], + ) + + assert_equal( + infer_sig_from_docstring("func(x, /, y, *, z, **kwargs) -> str", "func"), + [ + FunctionSig( + name="func", + args=[ + ArgSig(name="x"), + ArgSig(name="y"), + ArgSig(name="z"), + ArgSig("**kwargs"), + ], + ret_type="str", + ) + ], + ) + + def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments_errors(self) -> None: + # / as first argument + assert_equal(infer_sig_from_docstring("func(/, x) -> str", "func"), []) + + # * as last argument + assert_equal(infer_sig_from_docstring("func(x, *) -> str", "func"), []) + + # / after * + assert_equal(infer_sig_from_docstring("func(x, *, /, y) -> str", "func"), []) + + # Two / + assert_equal(infer_sig_from_docstring("func(x, /, /, *, y) -> str", "func"), []) + + assert_equal(infer_sig_from_docstring("func(x, /, y, /, *, z) -> str", "func"), []) + + # Two * + assert_equal(infer_sig_from_docstring("func(x, /, *, *, y) -> str", "func"), []) + + assert_equal(infer_sig_from_docstring("func(x, /, *, y, *, z) -> str", "func"), []) + + # *args and * are not allowed + assert_equal(infer_sig_from_docstring("func(*args, *, kwonly) -> str", "func"), []) + def test_infer_arg_sig_from_anon_docstring(self) -> None: assert_equal( infer_arg_sig_from_anon_docstring("(*args, **kwargs)"), @@ -612,6 +771,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 +1014,30 @@ class TestClassVariableCls: assert_equal(gen.get_imports().splitlines(), ["from typing import ClassVar"]) assert_equal(output, ["class C:", " x: ClassVar[int] = ..."]) + def test_generate_c_type_none_default(self) -> None: + class TestClass: + def test(self, arg0=1, arg1=None) -> None: # type: ignore[no-untyped-def] + 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: int = ..., arg1: Incomplete | None = ...) -> None: ..."] + ) + def test_non_c_generate_signature_with_kw_only_args(self) -> None: class TestClass: def test( @@ -976,7 +1169,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) @@ -1394,6 +1587,9 @@ def test_is_valid_type(self) -> None: assert is_valid_type("Literal[True]") assert is_valid_type("Literal[Color.RED]") assert is_valid_type("Literal[None]") + assert is_valid_type("str | int") + assert is_valid_type("dict[str, int] | int") + assert is_valid_type("tuple[str, ...]") assert is_valid_type( 'Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]' ) diff --git a/mypy/test/teststubinfo.py b/mypy/test/teststubinfo.py index eccee90244f3d..ae34e78f98c67 100644 --- a/mypy/test/teststubinfo.py +++ b/mypy/test/teststubinfo.py @@ -2,11 +2,34 @@ 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("dateparser") + + 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("psutil") == "types-psutil" + assert stub_distribution_name("sassutils") == "types-libsass" + 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 70687b499651f..4bec5daf3ffbe 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -9,10 +9,17 @@ import tempfile import textwrap import unittest -from typing import Any, Callable, Iterator +from collections.abc import Iterator +from typing import Any, Callable + +from pytest import raises import mypy.stubtest +from mypy import build, nodes +from mypy.modulefinder import BuildSource +from mypy.options import Options from mypy.stubtest import parse_options, test_stubs +from mypy.test.config import test_temp_dir from mypy.test.data import root_dir @@ -47,6 +54,11 @@ def __getitem__(self, typeargs: Any) -> object: ... Generic: _SpecialForm = ... Protocol: _SpecialForm = ... Union: _SpecialForm = ... +ClassVar: _SpecialForm = ... + +Final = 0 +Literal = 0 +TypedDict = 0 class TypeVar: def __init__(self, name, covariant: bool = ..., contravariant: bool = ...) -> None: ... @@ -70,6 +82,14 @@ class Match(Generic[AnyStr]): ... class Sequence(Iterable[_T_co]): ... class Tuple(Sequence[_T_co]): ... class NamedTuple(tuple[Any, ...]): ... +class _TypedDict(Mapping[str, object]): + __required_keys__: ClassVar[frozenset[str]] + __optional_keys__: ClassVar[frozenset[str]] + __total__: ClassVar[bool] + __readonly_keys__: ClassVar[frozenset[str]] + __mutable_keys__: ClassVar[frozenset[str]] + __closed__: ClassVar[bool | None] + __extra_items__: ClassVar[Any] def overload(func: _T) -> _T: ... def type_check_only(func: _T) -> _T: ... def final(func: _T) -> _T: ... @@ -94,6 +114,8 @@ def __ge__(self, __other: tuple[T_co, ...]) -> bool: pass class dict(Mapping[KT, VT]): ... +class frozenset(Generic[T]): ... + class function: pass class ellipsis: pass @@ -144,8 +166,21 @@ def __invert__(self: _T) -> _T: pass """ +def build_helper(source: str) -> build.BuildResult: + return build.build( + sources=[BuildSource("main.pyi", None, textwrap.dedent(source))], + options=Options(), + alt_lib_path=test_temp_dir, + ) + + def run_stubtest_with_stderr( - stub: str, runtime: str, options: list[str], config_file: str | None = None + stub: str, + runtime: str, + options: list[str], + config_file: str | None = None, + output: io.StringIO | None = None, + outerr: io.StringIO | None = None, ) -> tuple[str, str]: with use_tmp_dir(TEST_MODULE_NAME) as tmp_dir: with open("builtins.pyi", "w") as f: @@ -162,8 +197,8 @@ def run_stubtest_with_stderr( with open(f"{TEST_MODULE_NAME}_config.ini", "w") as f: f.write(config_file) options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"] - output = io.StringIO() - outerr = io.StringIO() + output = io.StringIO() if output is None else output + outerr = io.StringIO() if outerr is None else outerr with contextlib.redirect_stdout(output), contextlib.redirect_stderr(outerr): test_stubs(parse_options([TEST_MODULE_NAME] + options), use_builtins_fixtures=True) filtered_output = remove_color_code( @@ -325,6 +360,21 @@ def __exit__(self, exc_type, exc_val, exc_tb): pass """, error=None, ) + yield Case( + stub="""def dunder_name(__x: int) -> None: ...""", + runtime="""def dunder_name(__x: int) -> None: ...""", + error=None, + ) + yield Case( + stub="""def dunder_name_posonly(__x: int, /) -> None: ...""", + runtime="""def dunder_name_posonly(__x: int) -> None: ...""", + error=None, + ) + yield Case( + stub="""def dunder_name_bad(x: int) -> None: ...""", + runtime="""def dunder_name_bad(__x: int) -> None: ...""", + error="dunder_name_bad", + ) @collect_cases def test_arg_kind(self) -> Iterator[Case]: @@ -529,6 +579,18 @@ def f11(text=None) -> None: pass error="f11", ) + # Simulate numpy ndarray.__bool__ that raises an error + yield Case( + stub="def f12(x=1): ...", + runtime=""" + class _ndarray: + def __eq__(self, obj): return self + def __bool__(self): raise ValueError + def f12(x=_ndarray()) -> None: pass + """, + error="f12", + ) + @collect_cases def test_static_class_method(self) -> Iterator[Case]: yield Case( @@ -801,6 +863,18 @@ def f2(self, *a) -> int: ... """, error=None, ) + yield Case( + stub=""" + @overload + def f(a: int) -> int: ... + @overload + def f(a: int, b: str, /) -> str: ... + """, + runtime=""" + def f(a, *args): ... + """, + error=None, + ) @collect_cases def test_property(self) -> Iterator[Case]: @@ -809,11 +883,13 @@ def test_property(self) -> Iterator[Case]: class Good: @property def read_only_attr(self) -> int: ... + read_only_attr_alias = read_only_attr """, runtime=""" class Good: @property def read_only_attr(self): return 1 + read_only_attr_alias = read_only_attr """, error=None, ) @@ -875,6 +951,7 @@ class Z: def read_write_attr(self) -> int: ... @read_write_attr.setter def read_write_attr(self, val: int) -> None: ... + read_write_attr_alias = read_write_attr """, runtime=""" class Z: @@ -882,6 +959,7 @@ class Z: def read_write_attr(self): return self._val @read_write_attr.setter def read_write_attr(self, val): self._val = val + read_write_attr_alias = read_write_attr """, error=None, ) @@ -1267,9 +1345,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): @@ -1282,8 +1360,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=""" @@ -1297,8 +1375,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=""" @@ -1312,8 +1390,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=""" @@ -1346,8 +1424,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=""" @@ -1360,9 +1438,11 @@ def spam(x=Flags4(0)): pass ) yield Case( stub=""" - from typing_extensions import Final, Literal + import sys + from typing import Final, Literal class BytesEnum(bytes, enum.Enum): - a: bytes + a = b'foo' + FOO: Literal[BytesEnum.a] BAR: Final = BytesEnum.a BAZ: BytesEnum @@ -1378,6 +1458,55 @@ class BytesEnum(bytes, enum.Enum): """, error=None, ) + yield Case( + stub=""" + class HasSlotsAndNothingElse: + __slots__ = ("x",) + x: int + + class HasInheritedSlots(HasSlotsAndNothingElse): + pass + + class HasEmptySlots: + __slots__ = () + """, + runtime=""" + class HasSlotsAndNothingElse: + __slots__ = ("x",) + x: int + + class HasInheritedSlots(HasSlotsAndNothingElse): + pass + + class HasEmptySlots: + __slots__ = () + """, + error=None, + ) + yield Case( + stub=""" + class HasCompatibleValue(enum.Enum): + _value_: str + FOO = ... + """, + runtime=""" + class HasCompatibleValue(enum.Enum): + FOO = "foo" + """, + error=None, + ) + yield Case( + stub=""" + class HasIncompatibleValue(enum.Enum): + _value_: int + FOO = ... + """, + runtime=""" + class HasIncompatibleValue(enum.Enum): + FOO = "foo" + """, + error="HasIncompatibleValue.FOO", + ) @collect_cases def test_decorator(self) -> Iterator[Case]: @@ -1403,7 +1532,7 @@ def test_all_at_runtime_not_stub(self) -> Iterator[Case]: runtime=""" __all__ = [] Z = 5""", - error=None, + error="__all__", ) @collect_cases @@ -1443,7 +1572,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 @@ -1460,6 +1589,34 @@ def h(x: str): ... runtime="__all__ += ['Z']\nclass Z:\n def __reduce__(self): return (Z,)", error=None, ) + # __call__ exists on type, so it appears to exist on the class. + # This checks that we identify it as missing at runtime anyway. + yield Case( + stub=""" + class ClassWithMetaclassOverride: + def __call__(*args, **kwds): ... + """, + runtime="class ClassWithMetaclassOverride: ...", + error="ClassWithMetaclassOverride.__call__", + ) + # Test that we ignore object.__setattr__ and object.__delattr__ inheritance + yield Case( + stub=""" + from typing import Any + class FakeSetattrClass: + def __setattr__(self, name: str, value: Any, /) -> None: ... + """, + runtime="class FakeSetattrClass: ...", + error="FakeSetattrClass.__setattr__", + ) + yield Case( + stub=""" + class FakeDelattrClass: + def __delattr__(self, name: str, /) -> None: ... + """, + runtime="class FakeDelattrClass: ...", + error="FakeDelattrClass.__delattr__", + ) @collect_cases def test_missing_no_runtime_all(self) -> Iterator[Case]: @@ -1523,12 +1680,11 @@ def test_dunders(self) -> Iterator[Case]: runtime="class C:\n def __init_subclass__(cls, e=1, **kwargs): pass", error=None, ) - if sys.version_info >= (3, 9): - yield Case( - stub="class D:\n def __class_getitem__(cls, type: type) -> type: ...", - runtime="class D:\n def __class_getitem__(cls, type): ...", - error=None, - ) + yield Case( + stub="class D:\n def __class_getitem__(cls, type: type) -> type: ...", + runtime="class D:\n def __class_getitem__(cls, type): ...", + error=None, + ) @collect_cases def test_not_subclassable(self) -> Iterator[Case]: @@ -1541,6 +1697,107 @@ def test_not_subclassable(self) -> Iterator[Case]: error="CannotBeSubclassed", ) + @collect_cases + def test_disjoint_base(self) -> Iterator[Case]: + yield Case( + stub=""" + class A: pass + """, + runtime=""" + class A: pass + """, + error=None, + ) + yield Case( + stub=""" + from typing_extensions import disjoint_base + + @disjoint_base + class B: pass + """, + runtime=""" + class B: pass + """, + error="test_module.B", + ) + yield Case( + stub=""" + from typing_extensions import Self + + class mytakewhile: + def __new__(cls, predicate: object, iterable: object, /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> object: ... + """, + runtime=""" + from itertools import takewhile as mytakewhile + """, + # Should have @disjoint_base + error="test_module.mytakewhile", + ) + yield Case( + stub=""" + from typing_extensions import disjoint_base, Self + + @disjoint_base + class mycorrecttakewhile: + def __new__(cls, predicate: object, iterable: object, /) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> object: ... + """, + runtime=""" + from itertools import takewhile as mycorrecttakewhile + """, + error=None, + ) + yield Case( + runtime=""" + class IsDisjointBaseBecauseItHasSlots: + __slots__ = ("a",) + a: int + """, + stub=""" + from typing_extensions import disjoint_base + + @disjoint_base + class IsDisjointBaseBecauseItHasSlots: + a: int + """, + error="test_module.IsDisjointBaseBecauseItHasSlots", + ) + yield Case( + runtime=""" + class IsFinalSoDisjointBaseIsRedundant: ... + """, + stub=""" + from typing_extensions import disjoint_base, final + + @final + @disjoint_base + class IsFinalSoDisjointBaseIsRedundant: ... + """, + error="test_module.IsFinalSoDisjointBaseIsRedundant", + ) + yield Case( + runtime=""" + import enum + + class IsEnumWithMembersSoDisjointBaseIsRedundant(enum.Enum): + A = 1 + B = 2 + """, + stub=""" + from typing_extensions import disjoint_base + import enum + + @disjoint_base + class IsEnumWithMembersSoDisjointBaseIsRedundant(enum.Enum): + A = 1 + B = 2 + """, + error="test_module.IsEnumWithMembersSoDisjointBaseIsRedundant", + ) + @collect_cases def test_has_runtime_final_decorator(self) -> Iterator[Case]: yield Case( @@ -1893,11 +2150,11 @@ def __init__(self, x): pass def test_good_literal(self) -> Iterator[Case]: yield Case( stub=r""" - from typing_extensions import Literal + from typing import Literal import enum class Color(enum.Enum): - RED: int + RED = ... NUM: Literal[1] CHAR: Literal['a'] @@ -1925,7 +2182,7 @@ class Color(enum.Enum): @collect_cases def test_bad_literal(self) -> Iterator[Case]: - yield Case("from typing_extensions import Literal", "", None) # dummy case + yield Case("from typing import Literal", "", None) # dummy case yield Case( stub="INT_FLOAT_MISMATCH: Literal[1]", runtime="INT_FLOAT_MISMATCH = 1.0", @@ -1976,7 +2233,7 @@ def test_special_subtype(self) -> Iterator[Case]: ) yield Case( stub=""" - from typing_extensions import TypedDict + from typing import TypedDict class _Options(TypedDict): a: str @@ -1997,8 +2254,8 @@ class _Options(TypedDict): @collect_cases def test_runtime_typing_objects(self) -> Iterator[Case]: yield Case( - stub="from typing_extensions import Protocol, TypedDict", - runtime="from typing_extensions import Protocol, TypedDict", + stub="from typing import Protocol, TypedDict", + runtime="from typing import Protocol, TypedDict", error=None, ) yield Case( @@ -2363,8 +2620,8 @@ class A2: ... ) # The same is true for NamedTuples and TypedDicts: yield Case( - stub="from typing_extensions import NamedTuple, TypedDict", - runtime="from typing_extensions import NamedTuple, TypedDict", + stub="from typing import NamedTuple, TypedDict", + runtime="from typing import NamedTuple, TypedDict", error=None, ) yield Case( @@ -2400,6 +2657,42 @@ def func2() -> None: ... runtime="def func2() -> None: ...", error="func2", ) + # A type that exists at runtime is allowed to alias a type marked + # as '@type_check_only' in the stubs. + yield Case( + stub=""" + @type_check_only + class _X1: ... + X2 = _X1 + """, + runtime="class X2: ...", + error=None, + ) + + @collect_cases + def test_type_default_protocol(self) -> Iterator[Case]: + yield Case( + stub=""" + from typing import Protocol + + class _FormatterClass(Protocol): + def __call__(self, *, prog: str) -> HelpFormatter: ... + + class ArgumentParser: + def __init__(self, formatter_class: _FormatterClass = ...) -> None: ... + + class HelpFormatter: + def __init__(self, prog: str, indent_increment: int = 2) -> None: ... + """, + runtime=""" + class HelpFormatter: + def __init__(self, prog, indent_increment=2) -> None: ... + + class ArgumentParser: + def __init__(self, formatter_class=HelpFormatter): ... + """, + error=None, + ) def remove_color_code(s: str) -> str: @@ -2414,8 +2707,8 @@ def test_output(self) -> None: options=[], ) expected = ( - f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub argument "number" differs ' - 'from runtime argument "num"\n' + f'error: {TEST_MODULE_NAME}.bad is inconsistent, stub parameter "number" differs ' + 'from runtime parameter "num"\n' f"Stub: in file {TEST_MODULE_NAME}.pyi:1\n" "def (number: builtins.int, text: builtins.str)\n" f"Runtime: in file {TEST_MODULE_NAME}.py:1\ndef (num, text)\n\n" @@ -2430,7 +2723,9 @@ def test_output(self) -> None: ) expected = ( "{}.bad is inconsistent, " - 'stub argument "number" differs from runtime argument "num"\n'.format(TEST_MODULE_NAME) + 'stub parameter "number" differs from runtime parameter "num"\n'.format( + TEST_MODULE_NAME + ) ) assert output == expected @@ -2513,7 +2808,7 @@ def test_mypy_build(self) -> None: output = run_stubtest(stub="+", runtime="", options=[]) assert output == ( "error: not checking stubs due to failed mypy compile:\n{}.pyi:1: " - "error: invalid syntax [syntax]\n".format(TEST_MODULE_NAME) + "error: Invalid syntax [syntax]\n".format(TEST_MODULE_NAME) ) output = run_stubtest(stub="def f(): ...\ndef f(): ...", runtime="", options=[]) @@ -2577,6 +2872,25 @@ def test_builtin_signature_with_unrepresentable_default(self) -> None: == "def (self, sep = ..., bytes_per_sep = ...)" ) + def test_overload_signature(self) -> None: + # The same argument as both positional-only and pos-or-kw in + # different overloads previously produced incorrect signatures + source = """ + from typing import overload + @overload + def myfunction(arg: int) -> None: ... + @overload + def myfunction(arg: str, /) -> None: ... + """ + result = build_helper(source) + stub = result.files["__main__"].names["myfunction"].node + assert isinstance(stub, nodes.OverloadedFuncDef) + sig = mypy.stubtest.Signature.from_overloadedfuncdef(stub) + if sys.version_info >= (3, 10): + assert str(sig) == "def (arg: builtins.int | builtins.str)" + else: + assert str(sig) == "def (arg: Union[builtins.int, builtins.str])" + def test_config_file(self) -> None: runtime = "temp = 5\n" stub = "from decimal import Decimal\ntemp: Decimal\n" @@ -2607,14 +2921,20 @@ 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" - ) + output = io.StringIO() + outerr = io.StringIO() + with raises(SystemExit): + run_stubtest_with_stderr( + stub=stub, + runtime=runtime, + options=[], + config_file=config_file, + output=output, + outerr=outerr, + ) + + assert output.getvalue() == "error: Invalid error code(s): not-a-valid-name\n" + assert outerr.getvalue() == "" def test_config_file_wrong_incomplete_feature(self) -> None: runtime = "x = 1\n" diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 480fe38a90a71..b75c22bca7f79 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, TupleType, 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( @@ -274,6 +274,9 @@ def test_type_var_tuple_unpacked_variable_length_tuple(self) -> None: Instance(self.fx.gvi, [UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))]), ) + def test_fallback_not_subtype_of_tuple(self) -> None: + self.assert_not_subtype(self.fx.a, TupleType([self.fx.b], fallback=self.fx.a)) + # IDEA: Maybe add these test cases (they are tested pretty well in type # checker tests already): # * more interface subtyping test cases diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index 9388dca02c7a2..48a3eeed21150 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -38,7 +38,6 @@ def test_transform(testcase: DataDrivenTestCase) -> None: options.use_builtins_fixtures = True options.semantic_analysis_only = True options.show_traceback = True - options.force_uppercase_builtins = True result = build.build( sources=[BuildSource("main", None, src)], options=options, alt_lib_path=test_temp_dir ) diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index 4933bd3522a0f..42d831beeecc1 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -35,7 +35,6 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: options.export_types = True options.preserve_asts = True options.allow_empty_bodies = True - options.force_uppercase_builtins = True result = build.build( sources=[BuildSource("main", None, src)], options=options, diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 0218d33cc1244..f5f4c6797db2d 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -7,7 +7,7 @@ from mypy.erasetype import erase_type, remove_instance_last_known_values from mypy.indirection import TypeIndirectionVisitor -from mypy.join import join_simple, join_types +from mypy.join import join_types from mypy.meet import meet_types, narrow_declared_type from mypy.nodes import ( ARG_NAMED, @@ -23,7 +23,6 @@ Expression, NameExpr, ) -from mypy.options import Options from mypy.plugins.common import find_shallow_matching_overload_item from mypy.state import state from mypy.subtypes import is_more_precise, is_proper_subtype, is_same_type, is_subtype @@ -130,17 +129,13 @@ def test_callable_type_with_var_args(self) -> None: ) assert_equal(str(c3), "def (X? =, *Y?) -> Any") - def test_tuple_type_upper(self) -> None: - options = Options() - options.force_uppercase_builtins = True - assert_equal(TupleType([], self.fx.std_tuple).str_with_options(options), "Tuple[()]") - assert_equal(TupleType([self.x], self.fx.std_tuple).str_with_options(options), "Tuple[X?]") - assert_equal( - TupleType( - [self.x, AnyType(TypeOfAny.special_form)], self.fx.std_tuple - ).str_with_options(options), - "Tuple[X?, Any]", - ) + def test_tuple_type_str(self) -> None: + t1 = TupleType([], self.fx.std_tuple) + assert_equal(str(t1), "tuple[()]") + t2 = TupleType([self.x], self.fx.std_tuple) + assert_equal(str(t2), "tuple[X?]") + t3 = TupleType([self.x, AnyType(TypeOfAny.special_form)], self.fx.std_tuple) + assert_equal(str(t3), "tuple[X?, Any]") def test_type_variable_binding(self) -> None: assert_equal( @@ -206,18 +201,6 @@ def test_type_alias_expand_once(self) -> None: assert get_proper_type(A) == target assert get_proper_type(target) == target - def test_type_alias_expand_all(self) -> None: - A, _ = self.fx.def_alias_1(self.fx.a) - assert A.expand_all_if_possible() is None - A, _ = self.fx.def_alias_2(self.fx.a) - assert A.expand_all_if_possible() is None - - B = self.fx.non_rec_alias(self.fx.a) - C = self.fx.non_rec_alias(TupleType([B, B], Instance(self.fx.std_tuplei, [B]))) - assert C.expand_all_if_possible() == TupleType( - [self.fx.a, self.fx.a], Instance(self.fx.std_tuplei, [self.fx.a]) - ) - def test_recursive_nested_in_non_recursive(self) -> None: A, _ = self.fx.def_alias_1(self.fx.a) T = TypeVarType( @@ -230,12 +213,14 @@ def test_recursive_nested_in_non_recursive(self) -> None: def test_indirection_no_infinite_recursion(self) -> None: A, _ = self.fx.def_alias_1(self.fx.a) visitor = TypeIndirectionVisitor() - modules = A.accept(visitor) + A.accept(visitor) + modules = visitor.modules assert modules == {"__main__", "builtins"} A, _ = self.fx.def_alias_2(self.fx.a) visitor = TypeIndirectionVisitor() - modules = A.accept(visitor) + A.accept(visitor) + modules = visitor.modules assert modules == {"__main__", "builtins"} @@ -766,18 +751,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 +784,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, @@ -811,12 +800,12 @@ def test_any_type(self) -> None: self.assert_join(t, self.fx.anyt, self.fx.anyt) def test_mixed_truth_restricted_type_simple(self) -> None: - # join_simple against differently restricted truthiness types drops restrictions. + # make_simplified_union against differently restricted truthiness types drops restrictions. true_a = true_only(self.fx.a) false_o = false_only(self.fx.o) - j = join_simple(self.fx.o, true_a, false_o) - assert j.can_be_true - assert j.can_be_false + u = make_simplified_union([true_a, false_o]) + assert u.can_be_true + assert u.can_be_false def test_mixed_truth_restricted_type(self) -> None: # join_types against differently restricted truthiness types drops restrictions. @@ -834,7 +823,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 [ @@ -1011,7 +1004,7 @@ def test_variadic_tuple_joins(self) -> None: self.assert_join( self.tuple(self.fx.a, self.fx.a), self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), - self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + Instance(self.fx.std_tuplei, [self.fx.a]), ) self.assert_join( self.tuple(self.fx.a, self.fx.a), @@ -1039,12 +1032,12 @@ def test_variadic_tuple_joins(self) -> None: self.tuple( self.fx.a, UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a ), - self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + Instance(self.fx.std_tuplei, [self.fx.a]), ) self.assert_join( self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), - self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a]))), + Instance(self.fx.std_tuplei, [self.fx.a]), ) self.assert_join( self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), @@ -1054,6 +1047,39 @@ def test_variadic_tuple_joins(self) -> None: self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a), ) + def test_join_type_type_type_var(self) -> None: + self.assert_join(self.fx.type_a, self.fx.t, self.fx.o) + self.assert_join(self.fx.t, self.fx.type_a, self.fx.o) + + def test_join_type_var_bounds(self) -> None: + tvar1 = TypeVarType( + "tvar1", + "tvar1", + TypeVarId(-100), + [], + self.fx.o, + AnyType(TypeOfAny.from_omitted_generics), + INVARIANT, + ) + any_type = AnyType(TypeOfAny.special_form) + tvar2 = TypeVarType( + "tvar2", + "tvar2", + TypeVarId(-101), + [], + upper_bound=UnionType( + [ + TupleType([any_type], self.fx.std_tuple), + TupleType([any_type, any_type], self.fx.std_tuple), + ] + ), + default=AnyType(TypeOfAny.from_omitted_generics), + variance=INVARIANT, + ) + + self.assert_join(tvar1, tvar2, self.fx.o) + self.assert_join(tvar2, tvar1, self.fx.o) + # There are additional test cases in check-inference.test. # TODO: Function types + varargs and default args. @@ -1105,8 +1131,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 +1140,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 +1171,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 +1179,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 +1237,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 +1272,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) @@ -1560,11 +1600,12 @@ def make_call(*items: tuple[str, str | None]) -> CallExpr: class TestExpandTypeLimitGetProperType(TestCase): # WARNING: do not increase this number unless absolutely necessary, # and you understand what you are doing. - ALLOWED_GET_PROPER_TYPES = 9 + ALLOWED_GET_PROPER_TYPES = 7 @skipUnless(mypy.expandtype.__file__.endswith(".py"), "Skip for compiled mypy") def test_count_get_proper_type(self) -> None: with open(mypy.expandtype.__file__) as f: code = f.read() - get_proper_type_count = len(re.findall("get_proper_type", code)) + get_proper_type_count = len(re.findall(r"get_proper_type\(", code)) + get_proper_type_count -= len(re.findall(r"get_proper_type\(\)", code)) assert get_proper_type_count == self.ALLOWED_GET_PROPER_TYPES diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index 5a813f70117c6..f70c8b94f09c5 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -78,6 +78,8 @@ def make_type_var( self.anyt = AnyType(TypeOfAny.special_form) self.nonet = NoneType() self.uninhabited = UninhabitedType() + self.a_uninhabited = UninhabitedType() + self.a_uninhabited.ambiguous = True # Abstract class TypeInfos @@ -216,6 +218,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) @@ -371,7 +374,7 @@ def def_alias_1(self, base: Instance) -> tuple[TypeAliasType, Type]: target = Instance( self.std_tuplei, [UnionType([base, A])] ) # A = Tuple[Union[base, A], ...] - AN = TypeAlias(target, "__main__.A", -1, -1) + AN = TypeAlias(target, "__main__.A", "__main__", -1, -1) A.alias = AN return A, target @@ -380,7 +383,7 @@ def def_alias_2(self, base: Instance) -> tuple[TypeAliasType, Type]: target = UnionType( [base, Instance(self.std_tuplei, [A])] ) # A = Union[base, Tuple[A, ...]] - AN = TypeAlias(target, "__main__.A", -1, -1) + AN = TypeAlias(target, "__main__.A", "__main__", -1, -1) A.alias = AN return A, target @@ -390,7 +393,7 @@ def non_rec_alias( alias_tvars: list[TypeVarLikeType] | None = None, args: list[Type] | None = None, ) -> TypeAliasType: - AN = TypeAlias(target, "__main__.A", -1, -1, alias_tvars=alias_tvars) + AN = TypeAlias(target, "__main__.A", "__main__", -1, -1, alias_tvars=alias_tvars) if args is None: args = [] return TypeAliasType(AN, args) diff --git a/mypy/test/update_data.py b/mypy/test/update_data.py index 2d66752f61bd7..84b6383b3f0cf 100644 --- a/mypy/test/update_data.py +++ b/mypy/test/update_data.py @@ -2,7 +2,7 @@ import re from collections import defaultdict -from typing import Iterator +from collections.abc import Iterator from mypy.test.data import DataDrivenTestCase, DataFileCollector, DataFileFix, parse_test_data @@ -69,7 +69,7 @@ def _iter_fixes( source_line = source_line[: comment_match.start("indent")] # strip old comment if reports: indent = comment_match.group("indent") if comment_match else " " - # multiline comments are on the first line and then on subsequent lines emtpy lines + # multiline comments are on the first line and then on subsequent lines empty lines # with a continuation backslash for j, (severity, msg) in enumerate(reports): out_l = source_line if j == 0 else " " * len(source_line) diff --git a/mypy/traverser.py b/mypy/traverser.py index 6f162c9ec5760..baf234cc1b251 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -58,6 +58,7 @@ OverloadedFuncDef, ParamSpecExpr, PassStmt, + PromoteExpr, RaiseStmt, ReturnStmt, RevealExpr, @@ -67,6 +68,7 @@ StarExpr, StrExpr, SuperExpr, + TempNode, TryStmt, TupleExpr, TypeAlias, @@ -74,9 +76,11 @@ TypeAliasStmt, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeVarExpr, TypeVarTupleExpr, UnaryExpr, + Var, WhileStmt, WithStmt, YieldExpr, @@ -111,15 +115,15 @@ def __init__(self) -> None: # Visit methods - def visit_mypy_file(self, o: MypyFile) -> None: + def visit_mypy_file(self, o: MypyFile, /) -> None: for d in o.defs: d.accept(self) - def visit_block(self, block: Block) -> None: + def visit_block(self, block: Block, /) -> None: for s in block.body: s.accept(self) - def visit_func(self, o: FuncItem) -> None: + def visit_func(self, o: FuncItem, /) -> None: if o.arguments is not None: for arg in o.arguments: init = arg.initializer @@ -131,16 +135,16 @@ def visit_func(self, o: FuncItem) -> None: o.body.accept(self) - def visit_func_def(self, o: FuncDef) -> None: + def visit_func_def(self, o: FuncDef, /) -> None: self.visit_func(o) - def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: + def visit_overloaded_func_def(self, o: OverloadedFuncDef, /) -> None: for item in o.items: item.accept(self) if o.impl: o.impl.accept(self) - def visit_class_def(self, o: ClassDef) -> None: + def visit_class_def(self, o: ClassDef, /) -> None: for d in o.decorators: d.accept(self) for base in o.base_type_exprs: @@ -153,52 +157,52 @@ def visit_class_def(self, o: ClassDef) -> None: if o.analyzed: o.analyzed.accept(self) - def visit_decorator(self, o: Decorator) -> None: + def visit_decorator(self, o: Decorator, /) -> None: o.func.accept(self) o.var.accept(self) for decorator in o.decorators: decorator.accept(self) - def visit_expression_stmt(self, o: ExpressionStmt) -> None: + def visit_expression_stmt(self, o: ExpressionStmt, /) -> None: o.expr.accept(self) - def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: o.rvalue.accept(self) for l in o.lvalues: l.accept(self) - def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt) -> None: + def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt, /) -> None: o.rvalue.accept(self) o.lvalue.accept(self) - def visit_while_stmt(self, o: WhileStmt) -> None: + def visit_while_stmt(self, o: WhileStmt, /) -> None: o.expr.accept(self) o.body.accept(self) if o.else_body: o.else_body.accept(self) - def visit_for_stmt(self, o: ForStmt) -> None: + def visit_for_stmt(self, o: ForStmt, /) -> None: o.index.accept(self) o.expr.accept(self) o.body.accept(self) if o.else_body: o.else_body.accept(self) - def visit_return_stmt(self, o: ReturnStmt) -> None: + def visit_return_stmt(self, o: ReturnStmt, /) -> None: if o.expr is not None: o.expr.accept(self) - def visit_assert_stmt(self, o: AssertStmt) -> None: + def visit_assert_stmt(self, o: AssertStmt, /) -> None: if o.expr is not None: o.expr.accept(self) if o.msg is not None: o.msg.accept(self) - def visit_del_stmt(self, o: DelStmt) -> None: + def visit_del_stmt(self, o: DelStmt, /) -> None: if o.expr is not None: o.expr.accept(self) - def visit_if_stmt(self, o: IfStmt) -> None: + def visit_if_stmt(self, o: IfStmt, /) -> None: for e in o.expr: e.accept(self) for b in o.body: @@ -206,13 +210,13 @@ def visit_if_stmt(self, o: IfStmt) -> None: if o.else_body: o.else_body.accept(self) - def visit_raise_stmt(self, o: RaiseStmt) -> None: + def visit_raise_stmt(self, o: RaiseStmt, /) -> None: if o.expr is not None: o.expr.accept(self) if o.from_expr is not None: o.from_expr.accept(self) - def visit_try_stmt(self, o: TryStmt) -> None: + def visit_try_stmt(self, o: TryStmt, /) -> None: o.body.accept(self) for i in range(len(o.types)): tp = o.types[i] @@ -227,7 +231,7 @@ def visit_try_stmt(self, o: TryStmt) -> None: if o.finally_body is not None: o.finally_body.accept(self) - def visit_with_stmt(self, o: WithStmt) -> None: + def visit_with_stmt(self, o: WithStmt, /) -> None: for i in range(len(o.expr)): o.expr[i].accept(self) targ = o.target[i] @@ -235,7 +239,7 @@ def visit_with_stmt(self, o: WithStmt) -> None: targ.accept(self) o.body.accept(self) - def visit_match_stmt(self, o: MatchStmt) -> None: + def visit_match_stmt(self, o: MatchStmt, /) -> None: o.subject.accept(self) for i in range(len(o.patterns)): o.patterns[i].accept(self) @@ -244,38 +248,38 @@ def visit_match_stmt(self, o: MatchStmt) -> None: guard.accept(self) o.bodies[i].accept(self) - def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + def visit_type_alias_stmt(self, o: TypeAliasStmt, /) -> None: o.name.accept(self) o.value.accept(self) - def visit_member_expr(self, o: MemberExpr) -> None: + def visit_member_expr(self, o: MemberExpr, /) -> None: o.expr.accept(self) - def visit_yield_from_expr(self, o: YieldFromExpr) -> None: + def visit_yield_from_expr(self, o: YieldFromExpr, /) -> None: o.expr.accept(self) - def visit_yield_expr(self, o: YieldExpr) -> None: + def visit_yield_expr(self, o: YieldExpr, /) -> None: if o.expr: o.expr.accept(self) - def visit_call_expr(self, o: CallExpr) -> None: + def visit_call_expr(self, o: CallExpr, /) -> None: o.callee.accept(self) for a in o.args: a.accept(self) if o.analyzed: o.analyzed.accept(self) - def visit_op_expr(self, o: OpExpr) -> None: + def visit_op_expr(self, o: OpExpr, /) -> None: o.left.accept(self) o.right.accept(self) if o.analyzed is not None: o.analyzed.accept(self) - def visit_comparison_expr(self, o: ComparisonExpr) -> None: + def visit_comparison_expr(self, o: ComparisonExpr, /) -> None: for operand in o.operands: operand.accept(self) - def visit_slice_expr(self, o: SliceExpr) -> None: + def visit_slice_expr(self, o: SliceExpr, /) -> None: if o.begin_index is not None: o.begin_index.accept(self) if o.end_index is not None: @@ -283,13 +287,16 @@ def visit_slice_expr(self, o: SliceExpr) -> None: if o.stride is not None: o.stride.accept(self) - def visit_cast_expr(self, o: CastExpr) -> None: + def visit_cast_expr(self, o: CastExpr, /) -> None: o.expr.accept(self) - def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + def visit_type_form_expr(self, o: TypeFormExpr, /) -> None: + pass + + def visit_assert_type_expr(self, o: AssertTypeExpr, /) -> None: o.expr.accept(self) - def visit_reveal_expr(self, o: RevealExpr) -> None: + def visit_reveal_expr(self, o: RevealExpr, /) -> None: if o.kind == REVEAL_TYPE: assert o.expr is not None o.expr.accept(self) @@ -297,38 +304,38 @@ def visit_reveal_expr(self, o: RevealExpr) -> None: # RevealLocalsExpr doesn't have an inner expression pass - def visit_assignment_expr(self, o: AssignmentExpr) -> None: + def visit_assignment_expr(self, o: AssignmentExpr, /) -> None: o.target.accept(self) o.value.accept(self) - def visit_unary_expr(self, o: UnaryExpr) -> None: + def visit_unary_expr(self, o: UnaryExpr, /) -> None: o.expr.accept(self) - def visit_list_expr(self, o: ListExpr) -> None: + def visit_list_expr(self, o: ListExpr, /) -> None: for item in o.items: item.accept(self) - def visit_tuple_expr(self, o: TupleExpr) -> None: + def visit_tuple_expr(self, o: TupleExpr, /) -> None: for item in o.items: item.accept(self) - def visit_dict_expr(self, o: DictExpr) -> None: + def visit_dict_expr(self, o: DictExpr, /) -> None: for k, v in o.items: if k is not None: k.accept(self) v.accept(self) - def visit_set_expr(self, o: SetExpr) -> None: + def visit_set_expr(self, o: SetExpr, /) -> None: for item in o.items: item.accept(self) - def visit_index_expr(self, o: IndexExpr) -> None: + def visit_index_expr(self, o: IndexExpr, /) -> None: o.base.accept(self) o.index.accept(self) if o.analyzed: o.analyzed.accept(self) - def visit_generator_expr(self, o: GeneratorExpr) -> None: + def visit_generator_expr(self, o: GeneratorExpr, /) -> None: for index, sequence, conditions in zip(o.indices, o.sequences, o.condlists): sequence.accept(self) index.accept(self) @@ -336,7 +343,7 @@ def visit_generator_expr(self, o: GeneratorExpr) -> None: cond.accept(self) o.left_expr.accept(self) - def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None: + def visit_dictionary_comprehension(self, o: DictionaryComprehension, /) -> None: for index, sequence, conditions in zip(o.indices, o.sequences, o.condlists): sequence.accept(self) index.accept(self) @@ -345,54 +352,54 @@ def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None: o.key.accept(self) o.value.accept(self) - def visit_list_comprehension(self, o: ListComprehension) -> None: + def visit_list_comprehension(self, o: ListComprehension, /) -> None: o.generator.accept(self) - def visit_set_comprehension(self, o: SetComprehension) -> None: + def visit_set_comprehension(self, o: SetComprehension, /) -> None: o.generator.accept(self) - def visit_conditional_expr(self, o: ConditionalExpr) -> None: + def visit_conditional_expr(self, o: ConditionalExpr, /) -> None: o.cond.accept(self) o.if_expr.accept(self) o.else_expr.accept(self) - def visit_type_application(self, o: TypeApplication) -> None: + def visit_type_application(self, o: TypeApplication, /) -> None: o.expr.accept(self) - def visit_lambda_expr(self, o: LambdaExpr) -> None: + def visit_lambda_expr(self, o: LambdaExpr, /) -> None: self.visit_func(o) - def visit_star_expr(self, o: StarExpr) -> None: + def visit_star_expr(self, o: StarExpr, /) -> None: o.expr.accept(self) - def visit_await_expr(self, o: AwaitExpr) -> None: + def visit_await_expr(self, o: AwaitExpr, /) -> None: o.expr.accept(self) - def visit_super_expr(self, o: SuperExpr) -> None: + def visit_super_expr(self, o: SuperExpr, /) -> None: o.call.accept(self) - def visit_as_pattern(self, o: AsPattern) -> None: + def visit_as_pattern(self, o: AsPattern, /) -> None: if o.pattern is not None: o.pattern.accept(self) if o.name is not None: o.name.accept(self) - def visit_or_pattern(self, o: OrPattern) -> None: + def visit_or_pattern(self, o: OrPattern, /) -> None: for p in o.patterns: p.accept(self) - def visit_value_pattern(self, o: ValuePattern) -> None: + def visit_value_pattern(self, o: ValuePattern, /) -> None: o.expr.accept(self) - def visit_sequence_pattern(self, o: SequencePattern) -> None: + def visit_sequence_pattern(self, o: SequencePattern, /) -> None: for p in o.patterns: p.accept(self) - def visit_starred_pattern(self, o: StarredPattern) -> None: + def visit_starred_pattern(self, o: StarredPattern, /) -> None: if o.capture is not None: o.capture.accept(self) - def visit_mapping_pattern(self, o: MappingPattern) -> None: + def visit_mapping_pattern(self, o: MappingPattern, /) -> None: for key in o.keys: key.accept(self) for value in o.values: @@ -400,21 +407,100 @@ def visit_mapping_pattern(self, o: MappingPattern) -> None: if o.rest is not None: o.rest.accept(self) - def visit_class_pattern(self, o: ClassPattern) -> None: + def visit_class_pattern(self, o: ClassPattern, /) -> None: o.class_ref.accept(self) for p in o.positionals: p.accept(self) for v in o.keyword_values: v.accept(self) - def visit_import(self, o: Import) -> None: + def visit_import(self, o: Import, /) -> None: for a in o.assignments: a.accept(self) - def visit_import_from(self, o: ImportFrom) -> None: + def visit_import_from(self, o: ImportFrom, /) -> None: for a in o.assignments: a.accept(self) + # leaf nodes + def visit_name_expr(self, o: NameExpr, /) -> None: + return None + + def visit_str_expr(self, o: StrExpr, /) -> None: + return None + + def visit_int_expr(self, o: IntExpr, /) -> None: + return None + + def visit_float_expr(self, o: FloatExpr, /) -> None: + return None + + def visit_bytes_expr(self, o: BytesExpr, /) -> None: + return None + + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: + return None + + def visit_var(self, o: Var, /) -> None: + return None + + def visit_continue_stmt(self, o: ContinueStmt, /) -> None: + return None + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_break_stmt(self, o: BreakStmt, /) -> None: + return None + + def visit_temp_node(self, o: TempNode, /) -> None: + return None + + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: + return None + + def visit_global_decl(self, o: GlobalDecl, /) -> None: + return None + + def visit_import_all(self, o: ImportAll, /) -> None: + return None + + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: + return None + + def visit_paramspec_expr(self, o: ParamSpecExpr, /) -> None: + return None + + def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr, /) -> None: + return None + + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: + return None + + def visit_type_alias(self, o: TypeAlias, /) -> None: + return None + + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: + return None + + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: + return None + + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: + return None + + def visit__promote_expr(self, o: PromoteExpr, /) -> None: + return None + + def visit_complex_expr(self, o: ComplexExpr, /) -> None: + return None + + def visit_enum_call_expr(self, o: EnumCallExpr, /) -> None: + return None + + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: + return None + class ExtendedTraverserVisitor(TraverserVisitor): """This is a more flexible traverser. @@ -422,7 +508,7 @@ class ExtendedTraverserVisitor(TraverserVisitor): In addition to the base traverser it: * has visit_ methods for leaf nodes * has common method that is called for all nodes - * allows to skip recursing into a node + * allows skipping recursing into a node Note that this traverser still doesn't visit some internal mypy constructs like _promote expression and Var. @@ -432,402 +518,407 @@ def visit(self, o: Node) -> bool: # If returns True, will continue to nested nodes. return True - def visit_mypy_file(self, o: MypyFile) -> None: + def visit_mypy_file(self, o: MypyFile, /) -> None: if not self.visit(o): return super().visit_mypy_file(o) # Module structure - def visit_import(self, o: Import) -> None: + def visit_import(self, o: Import, /) -> None: if not self.visit(o): return super().visit_import(o) - def visit_import_from(self, o: ImportFrom) -> None: + def visit_import_from(self, o: ImportFrom, /) -> None: if not self.visit(o): return super().visit_import_from(o) - def visit_import_all(self, o: ImportAll) -> None: + def visit_import_all(self, o: ImportAll, /) -> None: if not self.visit(o): return super().visit_import_all(o) # Definitions - def visit_func_def(self, o: FuncDef) -> None: + def visit_func_def(self, o: FuncDef, /) -> None: if not self.visit(o): return super().visit_func_def(o) - def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: + def visit_overloaded_func_def(self, o: OverloadedFuncDef, /) -> None: if not self.visit(o): return super().visit_overloaded_func_def(o) - def visit_class_def(self, o: ClassDef) -> None: + def visit_class_def(self, o: ClassDef, /) -> None: if not self.visit(o): return super().visit_class_def(o) - def visit_global_decl(self, o: GlobalDecl) -> None: + def visit_global_decl(self, o: GlobalDecl, /) -> None: if not self.visit(o): return super().visit_global_decl(o) - def visit_nonlocal_decl(self, o: NonlocalDecl) -> None: + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: if not self.visit(o): return super().visit_nonlocal_decl(o) - def visit_decorator(self, o: Decorator) -> None: + def visit_decorator(self, o: Decorator, /) -> None: if not self.visit(o): return super().visit_decorator(o) - def visit_type_alias(self, o: TypeAlias) -> None: + def visit_type_alias(self, o: TypeAlias, /) -> None: if not self.visit(o): return super().visit_type_alias(o) # Statements - def visit_block(self, block: Block) -> None: + def visit_block(self, block: Block, /) -> None: if not self.visit(block): return super().visit_block(block) - def visit_expression_stmt(self, o: ExpressionStmt) -> None: + def visit_expression_stmt(self, o: ExpressionStmt, /) -> None: if not self.visit(o): return super().visit_expression_stmt(o) - def visit_assignment_stmt(self, o: AssignmentStmt) -> None: + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: if not self.visit(o): return super().visit_assignment_stmt(o) - def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt) -> None: + def visit_operator_assignment_stmt(self, o: OperatorAssignmentStmt, /) -> None: if not self.visit(o): return super().visit_operator_assignment_stmt(o) - def visit_while_stmt(self, o: WhileStmt) -> None: + def visit_while_stmt(self, o: WhileStmt, /) -> None: if not self.visit(o): return super().visit_while_stmt(o) - def visit_for_stmt(self, o: ForStmt) -> None: + def visit_for_stmt(self, o: ForStmt, /) -> None: if not self.visit(o): return super().visit_for_stmt(o) - def visit_return_stmt(self, o: ReturnStmt) -> None: + def visit_return_stmt(self, o: ReturnStmt, /) -> None: if not self.visit(o): return super().visit_return_stmt(o) - def visit_assert_stmt(self, o: AssertStmt) -> None: + def visit_assert_stmt(self, o: AssertStmt, /) -> None: if not self.visit(o): return super().visit_assert_stmt(o) - def visit_del_stmt(self, o: DelStmt) -> None: + def visit_del_stmt(self, o: DelStmt, /) -> None: if not self.visit(o): return super().visit_del_stmt(o) - def visit_if_stmt(self, o: IfStmt) -> None: + def visit_if_stmt(self, o: IfStmt, /) -> None: if not self.visit(o): return super().visit_if_stmt(o) - def visit_break_stmt(self, o: BreakStmt) -> None: + def visit_break_stmt(self, o: BreakStmt, /) -> None: if not self.visit(o): return super().visit_break_stmt(o) - def visit_continue_stmt(self, o: ContinueStmt) -> None: + def visit_continue_stmt(self, o: ContinueStmt, /) -> None: if not self.visit(o): return super().visit_continue_stmt(o) - def visit_pass_stmt(self, o: PassStmt) -> None: + def visit_pass_stmt(self, o: PassStmt, /) -> None: if not self.visit(o): return super().visit_pass_stmt(o) - def visit_raise_stmt(self, o: RaiseStmt) -> None: + def visit_raise_stmt(self, o: RaiseStmt, /) -> None: if not self.visit(o): return super().visit_raise_stmt(o) - def visit_try_stmt(self, o: TryStmt) -> None: + def visit_try_stmt(self, o: TryStmt, /) -> None: if not self.visit(o): return super().visit_try_stmt(o) - def visit_with_stmt(self, o: WithStmt) -> None: + def visit_with_stmt(self, o: WithStmt, /) -> None: if not self.visit(o): return super().visit_with_stmt(o) - def visit_match_stmt(self, o: MatchStmt) -> None: + def visit_match_stmt(self, o: MatchStmt, /) -> None: if not self.visit(o): return super().visit_match_stmt(o) # Expressions (default no-op implementation) - def visit_int_expr(self, o: IntExpr) -> None: + def visit_int_expr(self, o: IntExpr, /) -> None: if not self.visit(o): return super().visit_int_expr(o) - def visit_str_expr(self, o: StrExpr) -> None: + def visit_str_expr(self, o: StrExpr, /) -> None: if not self.visit(o): return super().visit_str_expr(o) - def visit_bytes_expr(self, o: BytesExpr) -> None: + def visit_bytes_expr(self, o: BytesExpr, /) -> None: if not self.visit(o): return super().visit_bytes_expr(o) - def visit_float_expr(self, o: FloatExpr) -> None: + def visit_float_expr(self, o: FloatExpr, /) -> None: if not self.visit(o): return super().visit_float_expr(o) - def visit_complex_expr(self, o: ComplexExpr) -> None: + def visit_complex_expr(self, o: ComplexExpr, /) -> None: if not self.visit(o): return super().visit_complex_expr(o) - def visit_ellipsis(self, o: EllipsisExpr) -> None: + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: if not self.visit(o): return super().visit_ellipsis(o) - def visit_star_expr(self, o: StarExpr) -> None: + def visit_star_expr(self, o: StarExpr, /) -> None: if not self.visit(o): return super().visit_star_expr(o) - def visit_name_expr(self, o: NameExpr) -> None: + def visit_name_expr(self, o: NameExpr, /) -> None: if not self.visit(o): return super().visit_name_expr(o) - def visit_member_expr(self, o: MemberExpr) -> None: + def visit_member_expr(self, o: MemberExpr, /) -> None: if not self.visit(o): return super().visit_member_expr(o) - def visit_yield_from_expr(self, o: YieldFromExpr) -> None: + def visit_yield_from_expr(self, o: YieldFromExpr, /) -> None: if not self.visit(o): return super().visit_yield_from_expr(o) - def visit_yield_expr(self, o: YieldExpr) -> None: + def visit_yield_expr(self, o: YieldExpr, /) -> None: if not self.visit(o): return super().visit_yield_expr(o) - def visit_call_expr(self, o: CallExpr) -> None: + def visit_call_expr(self, o: CallExpr, /) -> None: if not self.visit(o): return super().visit_call_expr(o) - def visit_op_expr(self, o: OpExpr) -> None: + def visit_op_expr(self, o: OpExpr, /) -> None: if not self.visit(o): return super().visit_op_expr(o) - def visit_comparison_expr(self, o: ComparisonExpr) -> None: + def visit_comparison_expr(self, o: ComparisonExpr, /) -> None: if not self.visit(o): return super().visit_comparison_expr(o) - def visit_cast_expr(self, o: CastExpr) -> None: + def visit_cast_expr(self, o: CastExpr, /) -> None: if not self.visit(o): return super().visit_cast_expr(o) - def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + def visit_type_form_expr(self, o: TypeFormExpr, /) -> None: + if not self.visit(o): + return + super().visit_type_form_expr(o) + + def visit_assert_type_expr(self, o: AssertTypeExpr, /) -> None: if not self.visit(o): return super().visit_assert_type_expr(o) - def visit_reveal_expr(self, o: RevealExpr) -> None: + def visit_reveal_expr(self, o: RevealExpr, /) -> None: if not self.visit(o): return super().visit_reveal_expr(o) - def visit_super_expr(self, o: SuperExpr) -> None: + def visit_super_expr(self, o: SuperExpr, /) -> None: if not self.visit(o): return super().visit_super_expr(o) - def visit_assignment_expr(self, o: AssignmentExpr) -> None: + def visit_assignment_expr(self, o: AssignmentExpr, /) -> None: if not self.visit(o): return super().visit_assignment_expr(o) - def visit_unary_expr(self, o: UnaryExpr) -> None: + def visit_unary_expr(self, o: UnaryExpr, /) -> None: if not self.visit(o): return super().visit_unary_expr(o) - def visit_list_expr(self, o: ListExpr) -> None: + def visit_list_expr(self, o: ListExpr, /) -> None: if not self.visit(o): return super().visit_list_expr(o) - def visit_dict_expr(self, o: DictExpr) -> None: + def visit_dict_expr(self, o: DictExpr, /) -> None: if not self.visit(o): return super().visit_dict_expr(o) - def visit_tuple_expr(self, o: TupleExpr) -> None: + def visit_tuple_expr(self, o: TupleExpr, /) -> None: if not self.visit(o): return super().visit_tuple_expr(o) - def visit_set_expr(self, o: SetExpr) -> None: + def visit_set_expr(self, o: SetExpr, /) -> None: if not self.visit(o): return super().visit_set_expr(o) - def visit_index_expr(self, o: IndexExpr) -> None: + def visit_index_expr(self, o: IndexExpr, /) -> None: if not self.visit(o): return super().visit_index_expr(o) - def visit_type_application(self, o: TypeApplication) -> None: + def visit_type_application(self, o: TypeApplication, /) -> None: if not self.visit(o): return super().visit_type_application(o) - def visit_lambda_expr(self, o: LambdaExpr) -> None: + def visit_lambda_expr(self, o: LambdaExpr, /) -> None: if not self.visit(o): return super().visit_lambda_expr(o) - def visit_list_comprehension(self, o: ListComprehension) -> None: + def visit_list_comprehension(self, o: ListComprehension, /) -> None: if not self.visit(o): return super().visit_list_comprehension(o) - def visit_set_comprehension(self, o: SetComprehension) -> None: + def visit_set_comprehension(self, o: SetComprehension, /) -> None: if not self.visit(o): return super().visit_set_comprehension(o) - def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None: + def visit_dictionary_comprehension(self, o: DictionaryComprehension, /) -> None: if not self.visit(o): return super().visit_dictionary_comprehension(o) - def visit_generator_expr(self, o: GeneratorExpr) -> None: + def visit_generator_expr(self, o: GeneratorExpr, /) -> None: if not self.visit(o): return super().visit_generator_expr(o) - def visit_slice_expr(self, o: SliceExpr) -> None: + def visit_slice_expr(self, o: SliceExpr, /) -> None: if not self.visit(o): return super().visit_slice_expr(o) - def visit_conditional_expr(self, o: ConditionalExpr) -> None: + def visit_conditional_expr(self, o: ConditionalExpr, /) -> None: if not self.visit(o): return super().visit_conditional_expr(o) - def visit_type_var_expr(self, o: TypeVarExpr) -> None: + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: if not self.visit(o): return super().visit_type_var_expr(o) - def visit_paramspec_expr(self, o: ParamSpecExpr) -> None: + def visit_paramspec_expr(self, o: ParamSpecExpr, /) -> None: if not self.visit(o): return super().visit_paramspec_expr(o) - def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr) -> None: + def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr, /) -> None: if not self.visit(o): return super().visit_type_var_tuple_expr(o) - def visit_type_alias_expr(self, o: TypeAliasExpr) -> None: + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: if not self.visit(o): return super().visit_type_alias_expr(o) - def visit_namedtuple_expr(self, o: NamedTupleExpr) -> None: + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: if not self.visit(o): return super().visit_namedtuple_expr(o) - def visit_enum_call_expr(self, o: EnumCallExpr) -> None: + def visit_enum_call_expr(self, o: EnumCallExpr, /) -> None: if not self.visit(o): return super().visit_enum_call_expr(o) - def visit_typeddict_expr(self, o: TypedDictExpr) -> None: + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: if not self.visit(o): return super().visit_typeddict_expr(o) - def visit_newtype_expr(self, o: NewTypeExpr) -> None: + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: if not self.visit(o): return super().visit_newtype_expr(o) - def visit_await_expr(self, o: AwaitExpr) -> None: + def visit_await_expr(self, o: AwaitExpr, /) -> None: if not self.visit(o): return super().visit_await_expr(o) # Patterns - def visit_as_pattern(self, o: AsPattern) -> None: + def visit_as_pattern(self, o: AsPattern, /) -> None: if not self.visit(o): return super().visit_as_pattern(o) - def visit_or_pattern(self, o: OrPattern) -> None: + def visit_or_pattern(self, o: OrPattern, /) -> None: if not self.visit(o): return super().visit_or_pattern(o) - def visit_value_pattern(self, o: ValuePattern) -> None: + def visit_value_pattern(self, o: ValuePattern, /) -> None: if not self.visit(o): return super().visit_value_pattern(o) - def visit_singleton_pattern(self, o: SingletonPattern) -> None: + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: if not self.visit(o): return super().visit_singleton_pattern(o) - def visit_sequence_pattern(self, o: SequencePattern) -> None: + def visit_sequence_pattern(self, o: SequencePattern, /) -> None: if not self.visit(o): return super().visit_sequence_pattern(o) - def visit_starred_pattern(self, o: StarredPattern) -> None: + def visit_starred_pattern(self, o: StarredPattern, /) -> None: if not self.visit(o): return super().visit_starred_pattern(o) - def visit_mapping_pattern(self, o: MappingPattern) -> None: + def visit_mapping_pattern(self, o: MappingPattern, /) -> None: if not self.visit(o): return super().visit_mapping_pattern(o) - def visit_class_pattern(self, o: ClassPattern) -> None: + def visit_class_pattern(self, o: ClassPattern, /) -> None: if not self.visit(o): return super().visit_class_pattern(o) @@ -853,6 +944,41 @@ def has_return_statement(fdef: FuncBase) -> bool: return seeker.found +class NameAndMemberCollector(TraverserVisitor): + def __init__(self) -> None: + super().__init__() + self.name_exprs: list[NameExpr] = [] + self.member_exprs: list[MemberExpr] = [] + + def visit_name_expr(self, o: NameExpr, /) -> None: + self.name_exprs.append(o) + super().visit_name_expr(o) + + def visit_member_expr(self, o: MemberExpr, /) -> None: + self.member_exprs.append(o) + super().visit_member_expr(o) + + +def all_name_and_member_expressions(node: Expression) -> tuple[list[NameExpr], list[MemberExpr]]: + v = NameAndMemberCollector() + node.accept(v) + return (v.name_exprs, v.member_exprs) + + +class StringSeeker(TraverserVisitor): + def __init__(self) -> None: + self.found = False + + def visit_str_expr(self, o: StrExpr, /) -> None: + self.found = True + + +def has_str_expression(node: Expression) -> bool: + v = StringSeeker() + node.accept(v) + return v.found + + class FuncCollectorBase(TraverserVisitor): def __init__(self) -> None: self.inside_func = False @@ -958,9 +1084,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..f5af5fb777b58 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -5,7 +5,8 @@ from __future__ import annotations -from typing import Iterable, Optional, cast +from collections.abc import Iterable +from typing import Optional, cast from mypy.nodes import ( GDEF, @@ -82,6 +83,7 @@ TypeAliasExpr, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeVarExpr, TypeVarTupleExpr, UnaryExpr, @@ -276,7 +278,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. @@ -539,6 +541,9 @@ def visit_comparison_expr(self, node: ComparisonExpr) -> ComparisonExpr: def visit_cast_expr(self, node: CastExpr) -> CastExpr: return CastExpr(self.expr(node.expr), self.type(node.type)) + def visit_type_form_expr(self, node: TypeFormExpr) -> TypeFormExpr: + return TypeFormExpr(self.type(node.type)) + def visit_assert_type_expr(self, node: AssertTypeExpr) -> AssertTypeExpr: return AssertTypeExpr(self.expr(node.expr), self.type(node.type)) @@ -558,7 +563,7 @@ def visit_super_expr(self, node: SuperExpr) -> SuperExpr: return new def visit_assignment_expr(self, node: AssignmentExpr) -> AssignmentExpr: - return AssignmentExpr(self.expr(node.target), self.expr(node.value)) + return AssignmentExpr(self.duplicate_name(node.target), self.expr(node.value)) def visit_unary_expr(self, node: UnaryExpr) -> UnaryExpr: new = UnaryExpr(node.op, self.expr(node.expr)) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 8aac7e5c2bbdc..1b38481ba0004 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -14,7 +14,8 @@ from __future__ import annotations from abc import abstractmethod -from typing import Any, Callable, Final, Generic, Iterable, Sequence, TypeVar, cast +from collections.abc import Iterable, Sequence +from typing import Any, Final, Generic, TypeVar, cast from mypy_extensions import mypyc_attr, trait @@ -50,7 +51,7 @@ get_proper_type, ) -T = TypeVar("T") +T = TypeVar("T", covariant=True) @trait @@ -62,87 +63,87 @@ class TypeVisitor(Generic[T]): """ @abstractmethod - def visit_unbound_type(self, t: UnboundType) -> T: + def visit_unbound_type(self, t: UnboundType, /) -> T: pass @abstractmethod - def visit_any(self, t: AnyType) -> T: + def visit_any(self, t: AnyType, /) -> T: pass @abstractmethod - def visit_none_type(self, t: NoneType) -> T: + def visit_none_type(self, t: NoneType, /) -> T: pass @abstractmethod - def visit_uninhabited_type(self, t: UninhabitedType) -> T: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> T: pass @abstractmethod - def visit_erased_type(self, t: ErasedType) -> T: + def visit_erased_type(self, t: ErasedType, /) -> T: pass @abstractmethod - def visit_deleted_type(self, t: DeletedType) -> T: + def visit_deleted_type(self, t: DeletedType, /) -> T: pass @abstractmethod - def visit_type_var(self, t: TypeVarType) -> T: + def visit_type_var(self, t: TypeVarType, /) -> T: pass @abstractmethod - def visit_param_spec(self, t: ParamSpecType) -> T: + def visit_param_spec(self, t: ParamSpecType, /) -> T: pass @abstractmethod - def visit_parameters(self, t: Parameters) -> T: + def visit_parameters(self, t: Parameters, /) -> T: pass @abstractmethod - def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> T: pass @abstractmethod - def visit_instance(self, t: Instance) -> T: + def visit_instance(self, t: Instance, /) -> T: pass @abstractmethod - def visit_callable_type(self, t: CallableType) -> T: + def visit_callable_type(self, t: CallableType, /) -> T: pass @abstractmethod - def visit_overloaded(self, t: Overloaded) -> T: + def visit_overloaded(self, t: Overloaded, /) -> T: pass @abstractmethod - def visit_tuple_type(self, t: TupleType) -> T: + def visit_tuple_type(self, t: TupleType, /) -> T: pass @abstractmethod - def visit_typeddict_type(self, t: TypedDictType) -> T: + def visit_typeddict_type(self, t: TypedDictType, /) -> T: pass @abstractmethod - def visit_literal_type(self, t: LiteralType) -> T: + def visit_literal_type(self, t: LiteralType, /) -> T: pass @abstractmethod - def visit_union_type(self, t: UnionType) -> T: + def visit_union_type(self, t: UnionType, /) -> T: pass @abstractmethod - def visit_partial_type(self, t: PartialType) -> T: + def visit_partial_type(self, t: PartialType, /) -> T: pass @abstractmethod - def visit_type_type(self, t: TypeType) -> T: + def visit_type_type(self, t: TypeType, /) -> T: pass @abstractmethod - def visit_type_alias_type(self, t: TypeAliasType) -> T: + def visit_type_alias_type(self, t: TypeAliasType, /) -> T: pass @abstractmethod - def visit_unpack_type(self, t: UnpackType) -> T: + def visit_unpack_type(self, t: UnpackType, /) -> T: pass @@ -155,23 +156,23 @@ class SyntheticTypeVisitor(TypeVisitor[T]): """ @abstractmethod - def visit_type_list(self, t: TypeList) -> T: + def visit_type_list(self, t: TypeList, /) -> T: pass @abstractmethod - def visit_callable_argument(self, t: CallableArgument) -> T: + def visit_callable_argument(self, t: CallableArgument, /) -> T: pass @abstractmethod - def visit_ellipsis_type(self, t: EllipsisType) -> T: + def visit_ellipsis_type(self, t: EllipsisType, /) -> T: pass @abstractmethod - def visit_raw_expression_type(self, t: RawExpressionType) -> T: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> T: pass @abstractmethod - def visit_placeholder_type(self, t: PlaceholderType) -> T: + def visit_placeholder_type(self, t: PlaceholderType, /) -> T: pass @@ -201,25 +202,25 @@ def set_cached(self, orig: Type, new: Type) -> None: self.cache = {} self.cache[orig] = new - def visit_unbound_type(self, t: UnboundType) -> Type: + def visit_unbound_type(self, t: UnboundType, /) -> Type: return t - def visit_any(self, t: AnyType) -> Type: + def visit_any(self, t: AnyType, /) -> Type: return t - def visit_none_type(self, t: NoneType) -> Type: + def visit_none_type(self, t: NoneType, /) -> Type: return t - def visit_uninhabited_type(self, t: UninhabitedType) -> Type: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> Type: return t - def visit_erased_type(self, t: ErasedType) -> Type: + def visit_erased_type(self, t: ErasedType, /) -> Type: return t - def visit_deleted_type(self, t: DeletedType) -> Type: + def visit_deleted_type(self, t: DeletedType, /) -> Type: return t - def visit_instance(self, t: Instance) -> Type: + def visit_instance(self, t: Instance, /) -> Type: last_known_value: LiteralType | None = None if t.last_known_value is not None: raw_last_known_value = t.last_known_value.accept(self) @@ -227,48 +228,48 @@ def visit_instance(self, t: Instance) -> Type: last_known_value = raw_last_known_value return Instance( typ=t.type, - args=self.translate_types(t.args), + args=self.translate_type_tuple(t.args), 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: + def visit_type_var(self, t: TypeVarType, /) -> Type: return t - def visit_param_spec(self, t: ParamSpecType) -> Type: + def visit_param_spec(self, t: ParamSpecType, /) -> Type: return t - def visit_parameters(self, t: Parameters) -> Type: - return t.copy_modified(arg_types=self.translate_types(t.arg_types)) + def visit_parameters(self, t: Parameters, /) -> Type: + return t.copy_modified(arg_types=self.translate_type_list(t.arg_types)) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> Type: return t - def visit_partial_type(self, t: PartialType) -> Type: + def visit_partial_type(self, t: PartialType, /) -> Type: return t - def visit_unpack_type(self, t: UnpackType) -> Type: + def visit_unpack_type(self, t: UnpackType, /) -> Type: return UnpackType(t.type.accept(self)) - def visit_callable_type(self, t: CallableType) -> Type: + def visit_callable_type(self, t: CallableType, /) -> Type: return t.copy_modified( - arg_types=self.translate_types(t.arg_types), + arg_types=self.translate_type_list(t.arg_types), ret_type=t.ret_type.accept(self), variables=self.translate_variables(t.variables), ) - def visit_tuple_type(self, t: TupleType) -> Type: + def visit_tuple_type(self, t: TupleType, /) -> Type: return TupleType( - self.translate_types(t.items), + self.translate_type_list(t.items), # TODO: This appears to be unsafe. cast(Any, t.partial_fallback.accept(self)), t.line, t.column, ) - def visit_typeddict_type(self, t: TypedDictType) -> 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 @@ -285,12 +286,12 @@ def visit_typeddict_type(self, t: TypedDictType) -> Type: self.set_cached(t, result) return result - def visit_literal_type(self, t: LiteralType) -> Type: + def visit_literal_type(self, t: LiteralType, /) -> Type: fallback = t.fallback.accept(self) assert isinstance(fallback, Instance) # type: ignore[misc] return LiteralType(value=t.value, fallback=fallback, line=t.line, column=t.column) - def visit_union_type(self, t: UnionType) -> Type: + 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 @@ -298,7 +299,7 @@ def visit_union_type(self, t: UnionType) -> Type: return cached result = UnionType( - self.translate_types(t.items), + self.translate_type_list(t.items), t.line, t.column, uses_pep604_syntax=t.uses_pep604_syntax, @@ -307,15 +308,18 @@ def visit_union_type(self, t: UnionType) -> Type: self.set_cached(t, result) return result - def translate_types(self, types: Iterable[Type]) -> list[Type]: + def translate_type_list(self, types: list[Type]) -> list[Type]: return [t.accept(self) for t in types] + def translate_type_tuple(self, types: tuple[Type, ...]) -> tuple[Type, ...]: + return tuple(t.accept(self) for t in types) + def translate_variables( self, variables: Sequence[TypeVarLikeType] ) -> Sequence[TypeVarLikeType]: return variables - def visit_overloaded(self, t: Overloaded) -> Type: + def visit_overloaded(self, t: Overloaded, /) -> Type: items: list[CallableType] = [] for item in t.items: new = item.accept(self) @@ -323,11 +327,13 @@ def visit_overloaded(self, t: Overloaded) -> Type: items.append(new) return Overloaded(items=items) - def visit_type_type(self, t: TypeType) -> Type: - return TypeType.make_normalized(t.item.accept(self), line=t.line, column=t.column) + def visit_type_type(self, t: TypeType, /) -> Type: + return TypeType.make_normalized( + t.item.accept(self), line=t.line, column=t.column, is_type_form=t.is_type_form + ) @abstractmethod - def visit_type_alias_type(self, t: TypeAliasType) -> Type: + def visit_type_alias_type(self, t: TypeAliasType, /) -> Type: # This method doesn't have a default implementation for type translators, # because type aliases are special: some information is contained in the # TypeAlias node, and we normally don't generate new nodes. Every subclass @@ -343,107 +349,111 @@ class TypeQuery(SyntheticTypeVisitor[T]): common use cases involve a boolean query using `any` or `all`. Note: this visitor keeps an internal state (tracks type aliases to avoid - recursion), so it should *never* be re-used for querying different types, + recursion), so it should *never* be reused for querying different types, create a new visitor instance instead. # TODO: check that we don't have existing violations of this rule. """ - def __init__(self, strategy: Callable[[list[T]], T]) -> None: - self.strategy = strategy + def __init__(self) -> None: # Keep track of the type aliases already visited. This is needed to avoid # infinite recursion on types like A = Union[int, List[A]]. - self.seen_aliases: set[TypeAliasType] = set() + self.seen_aliases: set[TypeAliasType] | None = None # By default, we eagerly expand type aliases, and query also types in the # alias target. In most cases this is a desired behavior, but we may want # to skip targets in some cases (e.g. when collecting type variables). self.skip_alias_target = False - def visit_unbound_type(self, t: UnboundType) -> T: + @abstractmethod + def strategy(self, items: list[T]) -> T: + raise NotImplementedError + + def visit_unbound_type(self, t: UnboundType, /) -> T: return self.query_types(t.args) - def visit_type_list(self, t: TypeList) -> T: + def visit_type_list(self, t: TypeList, /) -> T: return self.query_types(t.items) - def visit_callable_argument(self, t: CallableArgument) -> T: + def visit_callable_argument(self, t: CallableArgument, /) -> T: return t.typ.accept(self) - def visit_any(self, t: AnyType) -> T: + def visit_any(self, t: AnyType, /) -> T: return self.strategy([]) - def visit_uninhabited_type(self, t: UninhabitedType) -> T: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> T: return self.strategy([]) - def visit_none_type(self, t: NoneType) -> T: + def visit_none_type(self, t: NoneType, /) -> T: return self.strategy([]) - def visit_erased_type(self, t: ErasedType) -> T: + def visit_erased_type(self, t: ErasedType, /) -> T: return self.strategy([]) - def visit_deleted_type(self, t: DeletedType) -> T: + def visit_deleted_type(self, t: DeletedType, /) -> T: return self.strategy([]) - def visit_type_var(self, t: TypeVarType) -> T: + def visit_type_var(self, t: TypeVarType, /) -> T: return self.query_types([t.upper_bound, t.default] + t.values) - def visit_param_spec(self, t: ParamSpecType) -> T: + def visit_param_spec(self, t: ParamSpecType, /) -> T: return self.query_types([t.upper_bound, t.default, t.prefix]) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> T: return self.query_types([t.upper_bound, t.default]) - def visit_unpack_type(self, t: UnpackType) -> T: + def visit_unpack_type(self, t: UnpackType, /) -> T: return self.query_types([t.type]) - def visit_parameters(self, t: Parameters) -> T: + def visit_parameters(self, t: Parameters, /) -> T: return self.query_types(t.arg_types) - def visit_partial_type(self, t: PartialType) -> T: + def visit_partial_type(self, t: PartialType, /) -> T: return self.strategy([]) - def visit_instance(self, t: Instance) -> T: + def visit_instance(self, t: Instance, /) -> T: return self.query_types(t.args) - def visit_callable_type(self, t: CallableType) -> T: + def visit_callable_type(self, t: CallableType, /) -> T: # FIX generics return self.query_types(t.arg_types + [t.ret_type]) - def visit_tuple_type(self, t: TupleType) -> T: - return self.query_types(t.items) + def visit_tuple_type(self, t: TupleType, /) -> T: + return self.query_types([t.partial_fallback] + t.items) - def visit_typeddict_type(self, t: TypedDictType) -> T: + def visit_typeddict_type(self, t: TypedDictType, /) -> T: return self.query_types(t.items.values()) - def visit_raw_expression_type(self, t: RawExpressionType) -> T: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> T: return self.strategy([]) - def visit_literal_type(self, t: LiteralType) -> T: + def visit_literal_type(self, t: LiteralType, /) -> T: return self.strategy([]) - def visit_union_type(self, t: UnionType) -> T: + def visit_union_type(self, t: UnionType, /) -> T: return self.query_types(t.items) - def visit_overloaded(self, t: Overloaded) -> T: + def visit_overloaded(self, t: Overloaded, /) -> T: return self.query_types(t.items) - def visit_type_type(self, t: TypeType) -> T: + def visit_type_type(self, t: TypeType, /) -> T: return t.item.accept(self) - def visit_ellipsis_type(self, t: EllipsisType) -> T: + def visit_ellipsis_type(self, t: EllipsisType, /) -> T: return self.strategy([]) - def visit_placeholder_type(self, t: PlaceholderType) -> T: + def visit_placeholder_type(self, t: PlaceholderType, /) -> T: return self.query_types(t.args) - def visit_type_alias_type(self, t: TypeAliasType) -> T: - # Skip type aliases already visited types to avoid infinite recursion. - # TODO: Ideally we should fire subvisitors here (or use caching) if we care - # about duplicates. - if t in self.seen_aliases: - return self.strategy([]) - self.seen_aliases.add(t) + def visit_type_alias_type(self, t: TypeAliasType, /) -> T: if self.skip_alias_target: return self.query_types(t.args) + # Skip type aliases already visited types to avoid infinite recursion + # (also use this as a simple-minded cache). + if self.seen_aliases is None: + self.seen_aliases = set() + elif t in self.seen_aliases: + return self.strategy([]) + self.seen_aliases.add(t) return get_proper_type(t).accept(self) def query_types(self, types: Iterable[Type]) -> T: @@ -466,7 +476,7 @@ class BoolTypeQuery(SyntheticTypeVisitor[bool]): be ANY_STRATEGY or ALL_STRATEGY. Note: This visitor keeps an internal state (tracks type aliases to avoid - recursion), so it should *never* be re-used for querying different types + recursion), so it should *never* be reused for querying different types unless you call reset() first. """ @@ -493,52 +503,52 @@ def reset(self) -> None: """ self.seen_aliases = None - def visit_unbound_type(self, t: UnboundType) -> bool: + def visit_unbound_type(self, t: UnboundType, /) -> bool: return self.query_types(t.args) - def visit_type_list(self, t: TypeList) -> bool: + def visit_type_list(self, t: TypeList, /) -> bool: return self.query_types(t.items) - def visit_callable_argument(self, t: CallableArgument) -> bool: + def visit_callable_argument(self, t: CallableArgument, /) -> bool: return t.typ.accept(self) - def visit_any(self, t: AnyType) -> bool: + def visit_any(self, t: AnyType, /) -> bool: return self.default - def visit_uninhabited_type(self, t: UninhabitedType) -> bool: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> bool: return self.default - def visit_none_type(self, t: NoneType) -> bool: + def visit_none_type(self, t: NoneType, /) -> bool: return self.default - def visit_erased_type(self, t: ErasedType) -> bool: + def visit_erased_type(self, t: ErasedType, /) -> bool: return self.default - def visit_deleted_type(self, t: DeletedType) -> bool: + def visit_deleted_type(self, t: DeletedType, /) -> bool: return self.default - def visit_type_var(self, t: TypeVarType) -> bool: + def visit_type_var(self, t: TypeVarType, /) -> bool: return self.query_types([t.upper_bound, t.default] + t.values) - def visit_param_spec(self, t: ParamSpecType) -> bool: - return self.query_types([t.upper_bound, t.default]) + def visit_param_spec(self, t: ParamSpecType, /) -> bool: + return self.query_types([t.upper_bound, t.default, t.prefix]) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> bool: return self.query_types([t.upper_bound, t.default]) - def visit_unpack_type(self, t: UnpackType) -> bool: + def visit_unpack_type(self, t: UnpackType, /) -> bool: return self.query_types([t.type]) - def visit_parameters(self, t: Parameters) -> bool: + def visit_parameters(self, t: Parameters, /) -> bool: return self.query_types(t.arg_types) - def visit_partial_type(self, t: PartialType) -> bool: + def visit_partial_type(self, t: PartialType, /) -> bool: return self.default - def visit_instance(self, t: Instance) -> bool: + def visit_instance(self, t: Instance, /) -> bool: return self.query_types(t.args) - def visit_callable_type(self, t: CallableType) -> bool: + def visit_callable_type(self, t: CallableType, /) -> bool: # FIX generics # Avoid allocating any objects here as an optimization. args = self.query_types(t.arg_types) @@ -548,44 +558,43 @@ def visit_callable_type(self, t: CallableType) -> bool: else: return args and ret - def visit_tuple_type(self, t: TupleType) -> bool: - return self.query_types(t.items) + def visit_tuple_type(self, t: TupleType, /) -> bool: + return self.query_types([t.partial_fallback] + t.items) - def visit_typeddict_type(self, t: TypedDictType) -> bool: + 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: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> bool: return self.default - def visit_literal_type(self, t: LiteralType) -> bool: + def visit_literal_type(self, t: LiteralType, /) -> bool: return self.default - def visit_union_type(self, t: UnionType) -> bool: + def visit_union_type(self, t: UnionType, /) -> bool: return self.query_types(t.items) - def visit_overloaded(self, t: Overloaded) -> bool: + def visit_overloaded(self, t: Overloaded, /) -> bool: return self.query_types(t.items) # type: ignore[arg-type] - def visit_type_type(self, t: TypeType) -> bool: + def visit_type_type(self, t: TypeType, /) -> bool: return t.item.accept(self) - def visit_ellipsis_type(self, t: EllipsisType) -> bool: + def visit_ellipsis_type(self, t: EllipsisType, /) -> bool: return self.default - def visit_placeholder_type(self, t: PlaceholderType) -> bool: + def visit_placeholder_type(self, t: PlaceholderType, /) -> bool: return self.query_types(t.args) - def visit_type_alias_type(self, t: TypeAliasType) -> bool: - # Skip type aliases already visited types to avoid infinite recursion. - # TODO: Ideally we should fire subvisitors here (or use caching) if we care - # about duplicates. + def visit_type_alias_type(self, t: TypeAliasType, /) -> bool: + if self.skip_alias_target: + return self.query_types(t.args) + # Skip type aliases already visited types to avoid infinite recursion + # (also use this as a simple-minded cache). if self.seen_aliases is None: self.seen_aliases = set() elif t in self.seen_aliases: return self.default self.seen_aliases.add(t) - if self.skip_alias_target: - return self.query_types(t.args) return get_proper_type(t).accept(self) def query_types(self, types: list[Type] | tuple[Type, ...]) -> bool: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 0a6b7689136e4..3e5f522f39076 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -3,12 +3,13 @@ from __future__ import annotations import itertools +from collections.abc import Iterable, Iterator, Sequence from contextlib import contextmanager -from typing import Callable, Final, Iterable, Iterator, List, Sequence, Tuple, TypeVar -from typing_extensions import Protocol +from typing import Callable, Final, Protocol, TypeVar from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode +from mypy.errors import ErrorInfo from mypy.expandtype import expand_type from mypy.message_registry import ( INVALID_PARAM_SPEC_LOCATION, @@ -34,6 +35,7 @@ ArgKind, Context, Decorator, + ImportFrom, MypyFile, ParamSpecExpr, PlaceholderNode, @@ -46,9 +48,8 @@ Var, check_arg_kinds, check_arg_names, - get_nongen_builtins, ) -from mypy.options import INLINE_TYPEDDICT, Options +from mypy.options import INLINE_TYPEDDICT, TYPE_FORM, Options from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface from mypy.semanal_shared import ( SemanticAnalyzerCoreInterface, @@ -61,10 +62,14 @@ from mypy.types import ( ANNOTATED_TYPE_NAMES, ANY_STRATEGY, + CONCATENATE_TYPE_NAMES, FINAL_TYPE_NAMES, LITERAL_TYPE_NAMES, NEVER_NAMES, + TUPLE_NAMES, TYPE_ALIAS_NAMES, + TYPE_NAMES, + UNPACK_TYPE_NAMES, AnyType, BoolTypeQuery, CallableArgument, @@ -109,7 +114,7 @@ get_proper_type, has_type_vars, ) -from mypy.types_utils import is_bad_type_type_item +from mypy.types_utils import get_bad_type_type_item from mypy.typevars import fill_typevars T = TypeVar("T") @@ -133,12 +138,6 @@ "mypy_extensions.KwArg": ARG_STAR2, } -GENERIC_STUB_NOT_AT_RUNTIME_TYPES: Final = { - "queue.Queue", - "builtins._PathLike", - "asyncio.futures.Future", -} - SELF_TYPE_NAMES: Final = {"typing.Self", "typing_extensions.Self"} @@ -148,6 +147,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, @@ -167,6 +167,7 @@ def analyze_type_alias( tvar_scope, plugin, options, + cur_mod_node, is_typeshed_stub, defining_alias=True, allow_placeholder=allow_placeholder, @@ -177,21 +178,10 @@ 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 -def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> str: - class_name = name.split(".")[-1] - msg = f'"{class_name}" is not subscriptable' - # This should never be called if the python_version is 3.9 or newer - nongen_builtins = get_nongen_builtins((3, 8)) - replacement = nongen_builtins[name] - if replacement and propose_alt: - msg += f', use "{replacement}" instead' - return msg - - class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface): """Semantic analyzer for types. @@ -213,6 +203,7 @@ def __init__( tvar_scope: TypeVarLikeScope, plugin: Plugin, options: Options, + cur_mod_node: MypyFile, is_typeshed_stub: bool, *, defining_alias: bool = False, @@ -221,10 +212,12 @@ def __init__( allow_unbound_tvars: bool = False, allow_placeholder: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = True, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, + prohibit_special_class_field_types: str | None = None, allowed_alias_tvars: list[TypeVarLikeType] | None = None, allow_type_any: bool = False, alias_type_params_names: list[str] | None = None, @@ -255,6 +248,8 @@ def __init__( self.allow_placeholder = allow_placeholder # Are we in a context where Required[] is allowed? self.allow_typed_dict_special_forms = allow_typed_dict_special_forms + # Set True when we analyze ClassVar else False + self.allow_final = allow_final # 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? @@ -266,10 +261,13 @@ 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() self.prohibit_self_type = prohibit_self_type + # Set when we analyze TypedDicts or NamedTuples, since they are special: + self.prohibit_special_class_field_types = prohibit_special_class_field_types # Allow variables typed as Type[Any] and type (useful for base classes). self.allow_type_any = allow_type_any self.allow_type_var_tuple = False @@ -280,8 +278,8 @@ def lookup_qualified( ) -> SymbolTableNode | None: return self.api.lookup_qualified(name, ctx, suppress_errors) - def lookup_fully_qualified(self, name: str) -> SymbolTableNode: - return self.api.lookup_fully_qualified(name) + def lookup_fully_qualified(self, fullname: str) -> SymbolTableNode: + return self.api.lookup_fully_qualified(fullname) def visit_unbound_type(self, t: UnboundType, defining_literal: bool = False) -> Type: typ = self.visit_unbound_type_nonoptional(t, defining_literal) @@ -299,6 +297,15 @@ def not_declared_in_type_params(self, tvar_name: str) -> bool: def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) -> Type: sym = self.lookup_qualified(t.name, t) + param_spec_name = None + if t.name.endswith((".args", ".kwargs")): + param_spec_name = t.name.rsplit(".", 1)[0] + maybe_param_spec = self.lookup_qualified(param_spec_name, t) + if maybe_param_spec and isinstance(maybe_param_spec.node, ParamSpecExpr): + sym = maybe_param_spec + else: + param_spec_name = None + if sym is not None: node = sym.node if isinstance(node, PlaceholderNode): @@ -338,23 +345,25 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) hook = self.plugin.get_type_analyze_hook(fullname) if hook is not None: return hook(AnalyzeTypeContext(t, t, self)) - if ( - fullname in get_nongen_builtins(self.options.python_version) - and t.args - and not self.always_allow_new_syntax - ): - self.fail( - no_subscript_builtin_alias(fullname, propose_alt=not self.defining_alias), t - ) tvar_def = self.tvar_scope.get_binding(sym) + if tvar_def is not None: + # We need to cover special-case explained in get_typevarlike_argument() here, + # since otherwise the deferral will not be triggered if the type variable is + # used in a different module. Using isinstance() should be safe for this purpose. + tvar_params = [tvar_def.upper_bound, tvar_def.default] + if isinstance(tvar_def, TypeVarType): + tvar_params += tvar_def.values + if any(isinstance(tp, PlaceholderType) for tp in tvar_params): + self.api.defer() if isinstance(sym.node, ParamSpecExpr): if tvar_def is None: if self.allow_unbound_tvars: return t + name = param_spec_name or t.name if self.defining_alias and self.not_declared_in_type_params(t.name): - msg = f'ParamSpec "{t.name}" is not included in type_params' + msg = f'ParamSpec "{name}" is not included in type_params' else: - msg = f'ParamSpec "{t.name}" is unbound' + msg = f'ParamSpec "{name}" is unbound' self.fail(msg, t, code=codes.VALID_TYPE) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, ParamSpecType) @@ -362,6 +371,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.fail( f'ParamSpec "{t.name}" used with arguments', t, code=codes.VALID_TYPE ) + if param_spec_name is not None and not self.allow_param_spec_literals: + self.fail( + "ParamSpec components are not allowed here", t, code=codes.VALID_TYPE + ) + return AnyType(TypeOfAny.from_error) # Change the line number return ParamSpecType( tvar_def.name, @@ -499,7 +513,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) elif node.fullname in TYPE_ALIAS_NAMES: return AnyType(TypeOfAny.special_form) # Concatenate is an operator, no need for a proper type - elif node.fullname in ("typing_extensions.Concatenate", "typing.Concatenate"): + elif node.fullname in CONCATENATE_TYPE_NAMES: # We check the return type further up the stack for valid use locations return self.apply_concatenate_operator(t) else: @@ -588,19 +602,24 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ """ if fullname == "builtins.None": return NoneType() - elif fullname == "typing.Any" or fullname == "builtins.Any": + elif fullname == "typing.Any": return AnyType(TypeOfAny.explicit, line=t.line, column=t.column) elif fullname in FINAL_TYPE_NAMES: - self.fail( - "Final can be only used as an outermost qualifier in a variable annotation", - t, - code=codes.VALID_TYPE, - ) + if self.prohibit_special_class_field_types: + self.fail( + f"Final[...] can't be used inside a {self.prohibit_special_class_field_types}", + t, + code=codes.VALID_TYPE, + ) + else: + if not self.allow_final: + self.fail( + "Final can be only used as an outermost qualifier in a variable annotation", + t, + code=codes.VALID_TYPE, + ) return AnyType(TypeOfAny.from_error) - elif fullname == "typing.Tuple" or ( - fullname == "builtins.tuple" - and (self.always_allow_new_syntax or self.options.python_version >= (3, 9)) - ): + elif fullname in TUPLE_NAMES: # Tuple is special because it is involved in builtin import cycle # and may be not ready when used. sym = self.api.lookup_fully_qualified_or_none("builtins.tuple") @@ -624,7 +643,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ ) elif fullname == "typing.Union": items = self.anal_array(t.args) - return UnionType.make_union(items) + return UnionType.make_union(items, line=t.line, column=t.column) elif fullname == "typing.Optional": if len(t.args) != 1: self.fail( @@ -635,10 +654,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ return make_optional_type(item) elif fullname == "typing.Callable": return self.analyze_callable_type(t) - elif fullname == "typing.Type" or ( - fullname == "builtins.type" - and (self.always_allow_new_syntax or self.options.python_version >= (3, 9)) - ): + elif fullname in TYPE_NAMES: if len(t.args) == 0: if fullname == "typing.Type": any_type = self.get_omitted_any(t) @@ -647,21 +663,49 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ # To prevent assignment of 'builtins.type' inferred as 'builtins.object' # See https://github.com/python/mypy/issues/9476 for more information return None + type_str = "Type[...]" if fullname == "typing.Type" else "type[...]" if len(t.args) != 1: - type_str = "Type[...]" if fullname == "typing.Type" else "type[...]" self.fail( - type_str + " must have exactly one type argument", t, code=codes.VALID_TYPE + f"{type_str} must have exactly one type argument", t, code=codes.VALID_TYPE ) item = self.anal_type(t.args[0]) - if is_bad_type_type_item(item): - self.fail("Type[...] can't contain another Type[...]", t, code=codes.VALID_TYPE) + bad_item_name = get_bad_type_type_item(item) + if bad_item_name: + self.fail(f'{type_str} can\'t contain "{bad_item_name}"', t, code=codes.VALID_TYPE) item = AnyType(TypeOfAny.from_error) return TypeType.make_normalized(item, line=t.line, column=t.column) + elif fullname in ("typing_extensions.TypeForm", "typing.TypeForm"): + if TYPE_FORM not in self.options.enable_incomplete_feature: + self.fail( + "TypeForm is experimental," + " must be enabled with --enable-incomplete-feature=TypeForm", + t, + ) + if len(t.args) == 0: + any_type = self.get_omitted_any(t) + return TypeType(any_type, line=t.line, column=t.column, is_type_form=True) + if len(t.args) != 1: + type_str = "TypeForm[...]" + self.fail( + type_str + " must have exactly one type argument", t, code=codes.VALID_TYPE + ) + item = self.anal_type(t.args[0]) + return TypeType.make_normalized(item, line=t.line, column=t.column, is_type_form=True) elif fullname == "typing.ClassVar": if self.nesting_level > 0: self.fail( "Invalid type: ClassVar nested inside other type", t, code=codes.VALID_TYPE ) + if self.prohibit_special_class_field_types: + self.fail( + f"ClassVar[...] can't be used inside a {self.prohibit_special_class_field_types}", + t, + code=codes.VALID_TYPE, + ) + if self.defining_alias: + self.fail( + "ClassVar[...] can't be used inside a type alias", t, code=codes.VALID_TYPE + ) if len(t.args) == 0: return AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) if len(t.args) != 1: @@ -669,7 +713,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ "ClassVar[...] must have at most one type argument", t, code=codes.VALID_TYPE ) return AnyType(TypeOfAny.from_error) - return self.anal_type(t.args[0]) + return self.anal_type(t.args[0], allow_final=self.options.python_version >= (3, 13)) elif fullname in NEVER_NAMES: return UninhabitedType() elif fullname in LITERAL_TYPE_NAMES: @@ -683,7 +727,9 @@ 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_typed_dict_special_forms: self.fail( @@ -736,7 +782,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ ): # In most contexts, TypeGuard[...] acts as an alias for bool (ignoring its args) return self.named_type("builtins.bool") - elif fullname in ("typing.Unpack", "typing_extensions.Unpack"): + elif fullname in UNPACK_TYPE_NAMES: if len(t.args) != 1: self.fail("Unpack[...] requires exactly one type argument", t) return AnyType(TypeOfAny.from_error) @@ -759,7 +805,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ if self.api.type.has_base("builtins.type"): self.fail("Self type cannot be used in a metaclass", t) if self.api.type.self_type is not None: - if self.api.type.is_final: + if self.api.type.is_final or self.api.type.is_enum and self.api.type.enum_members: return fill_typevars(self.api.type) return self.api.type.self_type.copy_modified(line=t.line, column=t.column) # TODO: verify this is unreachable and replace with an assert? @@ -771,6 +817,25 @@ 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)) + and not any( + info.fullname == p or info.fullname.startswith(f"{p}.") + for p in self.options.deprecated_calls_exclude + ) + ): + 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: @@ -779,6 +844,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) @@ -802,6 +869,8 @@ def analyze_type_with_type_info( ctx.line, ctx.column, ) + instance.end_line = ctx.end_line + instance.end_column = ctx.end_column if len(info.type_vars) == 1 and info.has_param_spec_type: instance.args = tuple(self.pack_paramspec_args(instance.args)) @@ -959,7 +1028,7 @@ def analyze_unbound_type_without_type_info( elif unbound_tvar: assert isinstance(sym.node, TypeVarLikeExpr) if sym.node.is_new_style: - # PEP 695 type paramaters are never considered unbound -- they are undefined + # PEP 695 type parameters are never considered unbound -- they are undefined # in contexts where they aren't valid, such as in argument default values. message = 'Name "{}" is not defined' name = name.split(".")[-1] @@ -968,14 +1037,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' @@ -1068,48 +1135,59 @@ def visit_callable_type( variables = t.variables else: variables, _ = self.bind_function_type_variables(t, t) - type_guard = self.anal_type_guard(t.ret_type) - type_is = self.anal_type_is(t.ret_type) + type_guard = self.anal_type_guard(t.ret_type) if t.type_guard is None else t.type_guard + type_is = self.anal_type_is(t.ret_type) if t.type_is is None else t.type_is + arg_kinds = t.arg_kinds - if len(arg_kinds) >= 2 and arg_kinds[-2] == ARG_STAR and arg_kinds[-1] == ARG_STAR2: - arg_types = self.anal_array(t.arg_types[:-2], nested=nested) + [ - self.anal_star_arg_type(t.arg_types[-2], ARG_STAR, nested=nested), - self.anal_star_arg_type(t.arg_types[-1], ARG_STAR2, nested=nested), - ] - # If nested is True, it means we are analyzing a Callable[...] type, rather - # than a function definition type. We need to "unpack" ** TypedDict annotation - # here (for function definitions it is done in semanal). - if nested and isinstance(arg_types[-1], UnpackType): + arg_types = [] + param_spec_with_args = param_spec_with_kwargs = None + param_spec_invalid = False + for kind, ut in zip(arg_kinds, t.arg_types): + if kind == ARG_STAR: + param_spec_with_args, at = self.anal_star_arg_type(ut, kind, nested=nested) + elif kind == ARG_STAR2: + param_spec_with_kwargs, at = self.anal_star_arg_type(ut, kind, nested=nested) + else: + if param_spec_with_args: + param_spec_invalid = True + self.fail( + "Arguments not allowed after ParamSpec.args", t, code=codes.VALID_TYPE + ) + at = self.anal_type(ut, nested=nested, allow_unpack=False) + arg_types.append(at) + + if nested and arg_types: + # If we've got a Callable[[Unpack[SomeTypedDict]], None], make sure + # Unpack is interpreted as `**` and not as `*`. + last = arg_types[-1] + if isinstance(last, UnpackType): # TODO: it would be better to avoid this get_proper_type() call. - unpacked = get_proper_type(arg_types[-1].type) - if isinstance(unpacked, TypedDictType): - arg_types[-1] = unpacked + p_at = get_proper_type(last.type) + if isinstance(p_at, TypedDictType) and not last.from_star_syntax: + # Automatically detect Unpack[Foo] in Callable as backwards + # compatible syntax for **Foo, if Foo is a TypedDict. + arg_kinds[-1] = ARG_STAR2 + arg_types[-1] = p_at unpacked_kwargs = True - arg_types = self.check_unpacks_in_list(arg_types) - else: - star_index = None + arg_types = self.check_unpacks_in_list(arg_types) + + if not param_spec_invalid and param_spec_with_args != param_spec_with_kwargs: + # If already invalid, do not report more errors - definition has + # to be fixed anyway + name = param_spec_with_args or param_spec_with_kwargs + self.fail( + f'ParamSpec must have "*args" typed as "{name}.args" and "**kwargs" typed as "{name}.kwargs"', + t, + code=codes.VALID_TYPE, + ) + param_spec_invalid = True + + if param_spec_invalid: if ARG_STAR in arg_kinds: - star_index = arg_kinds.index(ARG_STAR) - star2_index = None + arg_types[arg_kinds.index(ARG_STAR)] = AnyType(TypeOfAny.from_error) if ARG_STAR2 in arg_kinds: - star2_index = arg_kinds.index(ARG_STAR2) - arg_types = [] - for i, ut in enumerate(t.arg_types): - at = self.anal_type( - ut, nested=nested, allow_unpack=i in (star_index, star2_index) - ) - if nested and isinstance(at, UnpackType) and i == star_index: - # TODO: it would be better to avoid this get_proper_type() call. - p_at = get_proper_type(at.type) - if isinstance(p_at, TypedDictType) and not at.from_star_syntax: - # Automatically detect Unpack[Foo] in Callable as backwards - # compatible syntax for **Foo, if Foo is a TypedDict. - at = p_at - arg_kinds[i] = ARG_STAR2 - unpacked_kwargs = True - arg_types.append(at) - if nested: - arg_types = self.check_unpacks_in_list(arg_types) + arg_types[arg_kinds.index(ARG_STAR2)] = AnyType(TypeOfAny.from_error) + # If there were multiple (invalid) unpacks, the arg types list will become shorter, # we need to trim the kinds/names as well to avoid crashes. arg_kinds = t.arg_kinds[: len(arg_types)] @@ -1164,7 +1242,7 @@ def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None: return self.anal_type(t.args[0]) return None - def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: + def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> tuple[str | None, Type]: """Analyze signature argument type for *args and **kwargs argument.""" if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args: components = t.name.split(".") @@ -1191,7 +1269,7 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: ) else: assert False, kind - return make_paramspec( + return tvar_name, make_paramspec( tvar_def.name, tvar_def.fullname, tvar_def.id, @@ -1199,7 +1277,7 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: line=t.line, column=t.column, ) - return self.anal_type(t, nested=nested, allow_unpack=True) + return None, self.anal_type(t, nested=nested, allow_unpack=True) def visit_overloaded(self, t: Overloaded) -> Type: # Overloaded types are manually constructed in semanal.py by analyzing the @@ -1348,7 +1426,9 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type: return AnyType(TypeOfAny.from_error) def visit_type_type(self, t: TypeType) -> Type: - return TypeType.make_normalized(self.anal_type(t.item), line=t.line) + return TypeType.make_normalized( + self.anal_type(t.item), line=t.line, is_type_form=t.is_type_form + ) def visit_placeholder_type(self, t: PlaceholderType) -> Type: n = ( @@ -1432,7 +1512,7 @@ def analyze_callable_args_for_concatenate( return None if sym.node is None: return None - if sym.node.fullname not in ("typing_extensions.Concatenate", "typing.Concatenate"): + if sym.node.fullname not in CONCATENATE_TYPE_NAMES: return None tvar_def = self.anal_type(callable_args, allow_param_spec=True) @@ -1581,7 +1661,7 @@ def analyze_callable_args( return None elif ( isinstance(arg, UnboundType) - and self.refers_to_full_names(arg, ("typing_extensions.Unpack", "typing.Unpack")) + and self.refers_to_full_names(arg, UNPACK_TYPE_NAMES) or isinstance(arg, UnpackType) ): if seen_unpack: @@ -1723,8 +1803,8 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] self.fail(f"Parameter {idx} of Literal[...] is invalid", ctx, code=codes.VALID_TYPE) return None - def analyze_type(self, t: Type) -> Type: - return t.accept(self) + def analyze_type(self, typ: Type) -> Type: + return typ.accept(self) def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None: self.fail_func(msg, ctx, code=code) @@ -1768,7 +1848,7 @@ def infer_type_variables( def bind_function_type_variables( self, fun_type: CallableType, defn: Context - ) -> tuple[Sequence[TypeVarLikeType], bool]: + ) -> tuple[tuple[TypeVarLikeType, ...], bool]: """Find the type variables of the function type and bind them in our tvar_scope""" has_self_type = False if fun_type.variables: @@ -1783,7 +1863,7 @@ def bind_function_type_variables( assert isinstance(var_expr, TypeVarLikeExpr) binding = self.tvar_scope.bind_new(var.name, var_expr) defs.append(binding) - return defs, has_self_type + return tuple(defs), has_self_type typevars, has_self_type = self.infer_type_variables(fun_type) # Do not define a new type variable if already defined in scope. typevars = [ @@ -1792,15 +1872,12 @@ def bind_function_type_variables( defs = [] for name, tvar in typevars: if not self.tvar_scope.allow_binding(tvar.fullname): - self.fail( - f'Type variable "{name}" is bound by an outer class', - defn, - code=codes.VALID_TYPE, - ) + err_msg = message_registry.TYPE_VAR_REDECLARED_IN_NESTED_CLASS.format(name) + self.fail(err_msg.value, defn, code=err_msg.code) binding = self.tvar_scope.bind_new(name, tvar) defs.append(binding) - return defs, has_self_type + return tuple(defs), has_self_type def is_defined_type_var(self, tvar: str, context: Context) -> bool: tvar_node = self.lookup_qualified(tvar, context) @@ -1838,11 +1915,13 @@ def anal_type( allow_unpack: bool = False, allow_ellipsis: bool = False, allow_typed_dict_special_forms: bool = False, + allow_final: bool = False, ) -> Type: if nested: self.nesting_level += 1 old_allow_typed_dict_special_forms = self.allow_typed_dict_special_forms self.allow_typed_dict_special_forms = allow_typed_dict_special_forms + self.allow_final = allow_final old_allow_ellipsis = self.allow_ellipsis self.allow_ellipsis = allow_ellipsis old_allow_unpack = self.allow_unpack @@ -1898,13 +1977,9 @@ def anal_var_defs(self, var_defs: Sequence[TypeVarLikeType]) -> list[TypeVarLike return [self.anal_var_def(vd) for vd in var_defs] def named_type( - self, - fully_qualified_name: str, - args: list[Type] | None = None, - line: int = -1, - column: int = -1, + self, fullname: str, args: list[Type] | None = None, line: int = -1, column: int = -1 ) -> Instance: - node = self.lookup_fully_qualified(fully_qualified_name) + node = self.lookup_fully_qualified(fullname) assert isinstance(node.node, TypeInfo) any_type = AnyType(TypeOfAny.special_form) if args is not None: @@ -1931,7 +2006,7 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]: if num_unpacks > 1: assert final_unpack is not None - self.fail("More than one Unpack in a type is not allowed", final_unpack) + self.fail("More than one variadic Unpack in a type is not allowed", final_unpack.type) return new_items def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType: @@ -1941,11 +2016,13 @@ def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType: ) -TypeVarLikeList = List[Tuple[str, TypeVarLikeExpr]] +TypeVarLikeList = list[tuple[str, TypeVarLikeExpr]] class MsgCallback(Protocol): - def __call__(self, __msg: str, __ctx: Context, *, code: ErrorCode | None = None) -> None: ... + def __call__( + self, __msg: str, __ctx: Context, *, code: ErrorCode | None = None + ) -> ErrorInfo | None: ... def get_omitted_any( @@ -1958,44 +2035,14 @@ def get_omitted_any( unexpanded_type: Type | None = None, ) -> AnyType: if disallow_any: - nongen_builtins = get_nongen_builtins(options.python_version) - if fullname in nongen_builtins: - typ = orig_type - # We use a dedicated error message for builtin generics (as the most common case). - alternative = nongen_builtins[fullname] - fail( - message_registry.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative), - typ, - code=codes.TYPE_ARG, - ) - else: - typ = unexpanded_type or orig_type - type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ, options) + typ = unexpanded_type or orig_type + type_str = typ.name if isinstance(typ, UnboundType) else format_type_bare(typ, options) - fail( - message_registry.BARE_GENERIC.format(quote_type_string(type_str)), - typ, - code=codes.TYPE_ARG, - ) - base_type = get_proper_type(orig_type) - base_fullname = ( - base_type.type.fullname if isinstance(base_type, Instance) else fullname - ) - # Ideally, we'd check whether the type is quoted or `from __future__ annotations` - # is set before issuing this note - if ( - options.python_version < (3, 9) - and base_fullname in GENERIC_STUB_NOT_AT_RUNTIME_TYPES - ): - # Recommend `from __future__ import annotations` or to put type in quotes - # (string literal escaping) for classes not generic at runtime - note( - "Subscripting classes that are not generic at runtime may require " - "escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html" - "#not-generic-runtime", - typ, - code=codes.TYPE_ARG, - ) + fail( + message_registry.BARE_GENERIC.format(quote_type_string(type_str)), + typ, + code=codes.TYPE_ARG, + ) any_type = AnyType(TypeOfAny.from_error, line=typ.line, column=typ.column) else: @@ -2137,6 +2184,8 @@ def instantiate_type_alias( tp = Instance(node.target.type, args) tp.line = ctx.line tp.column = ctx.column + tp.end_line = ctx.end_line + tp.end_column = ctx.end_column return tp if node.tvar_tuple_index is None: if any(isinstance(a, UnpackType) for a in args): @@ -2255,7 +2304,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 @@ -2278,15 +2328,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.""" @@ -2364,9 +2405,9 @@ def has_explicit_any(t: Type) -> bool: return t.accept(HasExplicitAny()) -class HasExplicitAny(TypeQuery[bool]): +class HasExplicitAny(BoolTypeQuery): def __init__(self) -> None: - super().__init__(any) + super().__init__(ANY_STRATEGY) def visit_any(self, t: AnyType) -> bool: return t.type_of_any == TypeOfAny.explicit @@ -2404,16 +2445,12 @@ def collect_all_inner_types(t: Type) -> list[Type]: return t.accept(CollectAllInnerTypesQuery()) -class CollectAllInnerTypesQuery(TypeQuery[List[Type]]): - def __init__(self) -> None: - super().__init__(self.combine_lists_strategy) - +class CollectAllInnerTypesQuery(TypeQuery[list[Type]]): def query_types(self, types: Iterable[Type]) -> list[Type]: return self.strategy([t.accept(self) for t in types]) + list(types) - @classmethod - def combine_lists_strategy(cls, it: Iterable[list[Type]]) -> list[Type]: - return list(itertools.chain.from_iterable(it)) + def strategy(self, items: Iterable[list[Type]]) -> list[Type]: + return list(itertools.chain.from_iterable(items)) def make_optional_type(t: Type) -> Type: @@ -2543,7 +2580,6 @@ def __init__(self, api: SemanticAnalyzerCoreInterface, scope: TypeVarLikeScope) self.scope = scope self.type_var_likes: list[tuple[str, TypeVarLikeExpr]] = [] self.has_self_type = False - self.seen_aliases: set[TypeAliasType] | None = None self.include_callables = True def _seems_like_callable(self, type: UnboundType) -> bool: @@ -2553,18 +2589,7 @@ def _seems_like_callable(self, type: UnboundType) -> bool: def visit_unbound_type(self, t: UnboundType) -> None: name = t.name - node = None - - # Special case P.args and P.kwargs for ParamSpecs only. - if name.endswith("args"): - if name.endswith((".args", ".kwargs")): - base = ".".join(name.split(".")[:-1]) - n = self.api.lookup_qualified(base, t) - if n is not None and isinstance(n.node, ParamSpecExpr): - node = n - name = base - if node is None: - node = self.api.lookup_qualified(name, t) + node = self.api.lookup_qualified(name, t) if node and node.fullname in SELF_TYPE_NAMES: self.has_self_type = True if ( @@ -2613,7 +2638,7 @@ def visit_type_var(self, t: TypeVarType) -> None: self.process_types([t.upper_bound, t.default] + t.values) def visit_param_spec(self, t: ParamSpecType) -> None: - self.process_types([t.upper_bound, t.default]) + self.process_types([t.upper_bound, t.default, t.prefix]) def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: self.process_types([t.upper_bound, t.default]) @@ -2651,7 +2676,8 @@ def visit_union_type(self, t: UnionType) -> None: self.process_types(t.items) def visit_overloaded(self, t: Overloaded) -> None: - self.process_types(t.items) # type: ignore[arg-type] + for it in t.items: + it.accept(self) def visit_type_type(self, t: TypeType) -> None: t.item.accept(self) @@ -2663,12 +2689,6 @@ def visit_placeholder_type(self, t: PlaceholderType) -> None: return self.process_types(t.args) def visit_type_alias_type(self, t: TypeAliasType) -> None: - # Skip type aliases in already visited types to avoid infinite recursion. - if self.seen_aliases is None: - self.seen_aliases = set() - elif t in self.seen_aliases: - return - self.seen_aliases.add(t) self.process_types(t.args) def process_types(self, types: list[Type] | tuple[Type, ...]) -> None: diff --git a/mypy/typeops.py b/mypy/typeops.py index 0699cda53cfa9..f6646740031d0 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -8,13 +8,14 @@ from __future__ import annotations import itertools -from typing import Any, Iterable, List, Sequence, TypeVar, cast +from collections.abc import Iterable, Sequence +from typing import Any, Callable, TypeVar, cast +from mypy.checker_state import checker_state from mypy.copytype import copy_type 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, @@ -26,12 +27,12 @@ FuncItem, OverloadedFuncDef, StrExpr, + SymbolNode, TypeInfo, Var, ) from mypy.state import state from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, ExtraAttrs, @@ -62,7 +63,9 @@ flatten_nested_unions, get_proper_type, get_proper_types, + remove_dups, ) +from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars @@ -123,7 +126,8 @@ def tuple_fallback(typ: TupleType) -> Instance: ) -def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Type | None: +def get_self_type(func: CallableType, def_info: TypeInfo) -> Type | None: + default_self = fill_typevars(def_info) if isinstance(get_proper_type(func.ret_type), UninhabitedType): return func.ret_type elif func.arg_types and func.arg_types[0] != default_self and func.arg_kinds[0] == ARG_POS: @@ -132,6 +136,120 @@ def get_self_type(func: CallableType, default_self: Instance | TupleType) -> Typ return None +def type_object_type(info: TypeInfo, named_type: Callable[[str], Instance]) -> ProperType: + """Return the type of a type object. + + For a generic type G with type variables T and S the type is generally of form + + Callable[..., G[T, S]] + + where ... are argument types for the __init__/__new__ method (without the self + argument). Also, the fallback type will be 'type' instead of 'function'. + """ + allow_cache = ( + checker_state.type_checker is not None + and checker_state.type_checker.allow_constructor_cache + ) + + if info.type_object_type is not None: + if allow_cache: + return info.type_object_type + info.type_object_type = None + + # We take the type from whichever of __init__ and __new__ is first + # in the MRO, preferring __init__ if there is a tie. + init_method = info.get("__init__") + new_method = info.get("__new__") + if not init_method or not is_valid_constructor(init_method.node): + # Must be an invalid class definition. + return AnyType(TypeOfAny.from_error) + # There *should* always be a __new__ method except the test stubs + # lack it, so just copy init_method in that situation + new_method = new_method or init_method + if not is_valid_constructor(new_method.node): + # Must be an invalid class definition. + return AnyType(TypeOfAny.from_error) + + # The two is_valid_constructor() checks ensure this. + assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) + assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) + + init_index = info.mro.index(init_method.node.info) + new_index = info.mro.index(new_method.node.info) + + if info.metaclass_type is not None: + fallback = info.metaclass_type + elif checker_state.type_checker: + # Prefer direct call when it is available. It is faster, and, + # unfortunately, some callers provide bogus callback. + fallback = checker_state.type_checker.named_type("builtins.type") + else: + fallback = named_type("builtins.type") + + if init_index < new_index: + method: FuncBase | Decorator = init_method.node + is_new = False + elif init_index > new_index: + method = new_method.node + is_new = True + else: + if init_method.node.info.fullname == "builtins.object": + # Both are defined by object. But if we've got a bogus + # base class, we can't know for sure, so check for that. + if info.fallback_to_any: + # Construct a universal callable as the prototype. + any_type = AnyType(TypeOfAny.special_form) + sig = CallableType( + arg_types=[any_type, any_type], + arg_kinds=[ARG_STAR, ARG_STAR2], + arg_names=["_args", "_kwds"], + ret_type=any_type, + is_bound=True, + fallback=named_type("builtins.function"), + ) + result: FunctionLike = class_callable(sig, info, fallback, None, is_new=False) + if allow_cache and state.strict_optional: + info.type_object_type = result + return result + + # Otherwise prefer __init__ in a tie. It isn't clear that this + # is the right thing, but __new__ caused problems with + # typeshed (#5647). + method = init_method.node + is_new = False + # Construct callable type based on signature of __init__. Adjust + # return type and insert type arguments. + if isinstance(method, FuncBase): + if isinstance(method, OverloadedFuncDef) and not method.type: + # Do not cache if the type is not ready. Same logic for decorators is + # achieved in early return above because is_valid_constructor() is False. + allow_cache = False + t = function_type(method, fallback) + else: + assert isinstance(method.type, ProperType) + assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this + t = method.type + result = type_object_type_from_function(t, info, method.info, fallback, is_new) + # Only write cached result is strict_optional=True, otherwise we may get + # inconsistent behaviour because of union simplification. + if allow_cache and state.strict_optional: + info.type_object_type = result + return result + + +def is_valid_constructor(n: SymbolNode | None) -> bool: + """Does this node represents a valid constructor method? + + This includes normal functions, overloaded functions, and decorators + that return a callable type. + """ + if isinstance(n, SYMBOL_FUNCBASE_TYPES): + return True + if isinstance(n, Decorator): + return isinstance(get_proper_type(n.type), FunctionLike) + return False + + def type_object_type_from_function( signature: FunctionLike, info: TypeInfo, def_info: TypeInfo, fallback: Instance, is_new: bool ) -> FunctionLike: @@ -140,9 +258,8 @@ def type_object_type_from_function( # self-types only in the defining class, similar to __new__ (but not exactly the same, # see comment in class_callable below). This is mostly useful for annotating library # classes such as subprocess.Popen. - default_self = fill_typevars(info) if not is_new and not info.is_newtype: - orig_self_types = [get_self_type(it, default_self) for it in signature.items] + orig_self_types = [get_self_type(it, def_info) for it in signature.items] else: orig_self_types = [None] * len(signature.items) @@ -158,7 +275,7 @@ def type_object_type_from_function( # We need to map B's __init__ to the type (List[T]) -> None. signature = bind_self( signature, - original_type=default_self, + original_type=fill_typevars(info), is_classmethod=is_new, # Explicit instance self annotations have special handling in class_callable(), # we don't need to bind any type variables in them if they are generic. @@ -202,7 +319,7 @@ def class_callable( default_ret_type = fill_typevars(info) explicit_type = init_ret_type if is_new else orig_self_type if ( - isinstance(explicit_type, (Instance, TupleType, UninhabitedType)) + isinstance(explicit_type, (Instance, TupleType, UninhabitedType, LiteralType)) # We have to skip protocols, because it can be a subtype of a return type # by accident. Like `Hashable` is a subtype of `object`. See #11799 and isinstance(default_ret_type, Instance) @@ -306,33 +423,15 @@ class B(A): pass """ if isinstance(method, Overloaded): - 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 - ] + 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 + func: CallableType = method if not func.arg_types: # Invalid method, return something. - return cast(F, func) + return method 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, @@ -341,7 +440,7 @@ class B(A): pass # 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) + return method self_param_type = get_proper_type(func.arg_types[0]) variables: Sequence[TypeVarLikeType] @@ -385,56 +484,16 @@ class B(A): pass else: variables = func.variables - original_type = get_proper_type(original_type) - if isinstance(original_type, CallableType) and original_type.is_type_obj(): - original_type = TypeType.make_normalized(original_type.ret_type) res = func.copy_modified( arg_types=func.arg_types[1:], arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], variables=variables, - bound_args=[original_type], + is_bound=True, ) 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) @@ -442,14 +501,14 @@ def erase_to_bound(t: Type) -> Type: return t.upper_bound if isinstance(t, TypeType): if isinstance(t.item, TypeVarType): - return TypeType.make_normalized(t.item.upper_bound) + return TypeType.make_normalized(t.item.upper_bound, is_type_form=t.is_type_form) return t def callable_corresponding_argument( typ: NormalizedCallableType | Parameters, model: FormalArgument ) -> FormalArgument | None: - """Return the argument a function that corresponds to `model`""" + """Return the argument of a function that corresponds to `model`""" by_name = typ.argument_by_name(model.name) by_pos = typ.argument_by_position(model.pos) @@ -463,16 +522,23 @@ def callable_corresponding_argument( # taking both *args and **args, or a pair of functions like so: # def right(a: int = ...) -> None: ... - # def left(__a: int = ..., *, a: int = ...) -> None: ... - from mypy.subtypes import is_equivalent + # def left(x: int = ..., /, *, a: int = ...) -> None: ... + from mypy.meet import meet_types if ( not (by_name.required or by_pos.required) and by_pos.name is None and by_name.pos is None - and is_equivalent(by_name.typ, by_pos.typ) + # This is not principled, but prevents a crash. It's weird to have a FormalArgument + # that has an UnpackType. + and not isinstance(by_name.typ, UnpackType) + and not isinstance(by_pos.typ, UnpackType) ): - return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False) + return FormalArgument( + by_name.name, by_pos.pos, meet_types(by_name.typ, by_pos.typ), False + ) + return by_name + return by_name if by_name is not None else by_pos @@ -606,15 +672,15 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[ else: # If not, check if we've seen a supertype of this type for j, tj in enumerate(new_items): - tj = get_proper_type(tj) + proper_tj = get_proper_type(tj) # If tj is an Instance with a last_known_value, do not remove proper_ti # (unless it's an instance with the same last_known_value) if ( - isinstance(tj, Instance) - and tj.last_known_value is not None + isinstance(proper_tj, Instance) + and proper_tj.last_known_value is not None and not ( isinstance(proper_ti, Instance) - and tj.last_known_value == proper_ti.last_known_value + and proper_tj.last_known_value == proper_ti.last_known_value ) ): continue @@ -648,8 +714,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) @@ -725,7 +794,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) and state.strict_optional: + return UninhabitedType(line=t.line) + elif isinstance(t, LiteralType) and t.is_enum_literal() and state.strict_optional: return UninhabitedType(line=t.line) new_t = copy_type(t) @@ -775,8 +847,8 @@ def function_type(func: FuncBase, fallback: Instance) -> FunctionLike: if isinstance(func, FuncItem): return callable_type(func, fallback) else: - # Broken overloads can have self.type set to None. - # TODO: should we instead always set the type in semantic analyzer? + # Either a broken overload, or decorated overload type is not ready. + # TODO: make sure the caller defers if possible. assert isinstance(func, OverloadedFuncDef) any_type = AnyType(TypeOfAny.from_error) dummy = CallableType( @@ -797,7 +869,7 @@ def callable_type( fdef: FuncItem, fallback: Instance, ret_type: Type | None = None ) -> CallableType: # TODO: somewhat unfortunate duplication with prepare_method_signature in semanal - if fdef.info and (not fdef.is_static or fdef.name == "__new__") and fdef.arg_names: + if fdef.info and fdef.has_self_or_cls_argument and fdef.arg_names: self_type: Type = fill_typevars(fdef.info) if fdef.is_class or fdef.name == "__new__": self_type = TypeType.make_normalized(self_type) @@ -933,7 +1005,7 @@ def is_singleton_type(typ: Type) -> bool: return typ.is_singleton_type() -def try_expanding_sum_type_to_union(typ: Type, target_fullname: str) -> ProperType: +def try_expanding_sum_type_to_union(typ: Type, target_fullname: str) -> Type: """Attempts to recursively expand any enum Instances with the given target_fullname into a Union of all of its component LiteralTypes. @@ -949,34 +1021,28 @@ class Status(Enum): FAILURE = 2 UNKNOWN = 3 - ...and if we call `try_expanding_enum_to_union(Union[Color, Status], 'module.Color')`, + ...and if we call `try_expanding_sum_type_to_union(Union[Color, Status], 'module.Color')`, this function will return Literal[Color.RED, Color.BLUE, Color.YELLOW, Status]. """ typ = get_proper_type(typ) if isinstance(typ, UnionType): + # Non-empty enums cannot subclass each other so simply removing duplicates is enough. items = [ - try_expanding_sum_type_to_union(item, target_fullname) for item in typ.relevant_items() + try_expanding_sum_type_to_union(item, target_fullname) + for item in remove_dups(flatten_nested_unions(typ.relevant_items())) ] - return make_simplified_union(items, contract_literals=False) - elif isinstance(typ, Instance) and typ.type.fullname == target_fullname: + return UnionType.make_union(items) + + if isinstance(typ, Instance) and typ.type.fullname == target_fullname: + if typ.type.fullname == "builtins.bool": + return UnionType([LiteralType(True, typ), LiteralType(False, typ)]) + 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 UnionType.make_union(items) return typ @@ -984,6 +1050,8 @@ class Status(Enum): def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType]: """Contracts any literal types back into a sum type if possible. + Requires a flattened union and does not descend into children. + Will replace the first instance of the literal with the sum type and remove all others. @@ -1002,7 +1070,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} ), @@ -1035,7 +1103,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 @@ -1050,14 +1118,14 @@ def get_all_type_vars(tp: Type) -> list[TypeVarLikeType]: return tp.accept(TypeVarExtractor(include_all=True)) -class TypeVarExtractor(TypeQuery[List[TypeVarLikeType]]): +class TypeVarExtractor(TypeQuery[list[TypeVarLikeType]]): def __init__(self, include_all: bool = False) -> None: - super().__init__(self._merge) + super().__init__() self.include_all = include_all - def _merge(self, iter: Iterable[list[TypeVarLikeType]]) -> list[TypeVarLikeType]: + def strategy(self, items: Iterable[list[TypeVarLikeType]]) -> list[TypeVarLikeType]: out = [] - for item in iter: + for item in items: out.extend(item) return out @@ -1071,6 +1139,17 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> list[TypeVarLikeType]: return [t] if self.include_all else [] +def freeze_all_type_vars(member_type: Type) -> None: + member_type.accept(FreezeTypeVarsVisitor()) + + +class FreezeTypeVarsVisitor(TypeTraverserVisitor): + def visit_callable_type(self, t: CallableType) -> None: + for v in t.variables: + v.id.meta_level = 0 + super().visit_callable_type(t) + + def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool: """Does this type have a custom special method such as __format__() or __eq__()? @@ -1092,6 +1171,10 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool if isinstance(typ, FunctionLike) and typ.is_type_obj(): # Look up __method__ on the metaclass for class objects. return custom_special_method(typ.fallback, name, check_all) + if isinstance(typ, TypeType) and isinstance(typ.item, Instance): + if typ.item.type.metaclass_type: + # Look up __method__ on the metaclass for class objects. + return custom_special_method(typ.item.type.metaclass_type, name, check_all) if isinstance(typ, AnyType): # Avoid false positives in uncertain cases. return True @@ -1148,21 +1231,84 @@ def fixup_partial_type(typ: Type) -> Type: return Instance(typ.type, [AnyType(TypeOfAny.unannotated)] * len(typ.type.type_vars)) -def get_protocol_member(left: Instance, member: str, class_obj: bool) -> ProperType | None: +def get_protocol_member( + left: Instance, member: str, class_obj: bool, is_lvalue: bool = False +) -> Type | None: if member == "__call__" and class_obj: # Special case: class objects always have __call__ that is just the constructor. - from mypy.checkmember import type_object_type + # TODO: this is wrong, it creates callables that are not recognized as type objects. + # Long-term, we should probably get rid of this callback argument altogether. def named_type(fullname: str) -> Instance: return Instance(left.type.mro[-1], []) return type_object_type(left.type, named_type) - if member == "__call__" and left.type.is_metaclass(): + if member == "__call__" and left.type.is_metaclass(precise=True): # Special case: we want to avoid falling back to metaclass __call__ # if constructor signature didn't match, this can cause many false negatives. return None from mypy.subtypes import find_member - return get_proper_type(find_member(member, left, left, class_obj=class_obj)) + subtype = find_member(member, left, left, class_obj=class_obj, is_lvalue=is_lvalue) + if isinstance(subtype, PartialType): + subtype = ( + NoneType() + if subtype.type is None + else Instance( + subtype.type, [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars) + ) + ) + return subtype + + +def _is_disjoint_base(info: TypeInfo) -> bool: + # It either has the @disjoint_base decorator or defines nonempty __slots__. + if info.is_disjoint_base: + return True + if not info.slots: + return False + own_slots = { + slot + for slot in info.slots + if not any( + base_info.type.slots is not None and slot in base_info.type.slots + for base_info in info.bases + ) + } + return bool(own_slots) + + +def _get_disjoint_base_of(instance: Instance) -> TypeInfo | None: + """Returns the disjoint base of the given instance, if it exists.""" + if _is_disjoint_base(instance.type): + return instance.type + for base in instance.type.mro: + if _is_disjoint_base(base): + return base + return None + + +def can_have_shared_disjoint_base(instances: list[Instance]) -> bool: + """Returns whether the given instances can share a disjoint base. + + This means that a child class of these classes can exist at runtime. + """ + # Ignore None disjoint bases (which are `object`). + disjoint_bases = [ + base for instance in instances if (base := _get_disjoint_base_of(instance)) is not None + ] + if not disjoint_bases: + # All are `object`. + return True + + candidate = disjoint_bases[0] + for base in disjoint_bases[1:]: + if candidate.has_base(base.fullname): + continue + elif base.has_base(candidate.fullname): + candidate = base + else: + return False + return True diff --git a/mypy/types.py b/mypy/types.py index dff7e2c0c829a..09e4b74bb821c 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -4,41 +4,55 @@ import sys from abc import abstractmethod -from typing import ( - TYPE_CHECKING, - Any, - ClassVar, - Dict, - Final, - Iterable, - NamedTuple, - NewType, - Sequence, - TypeVar, - Union, - cast, +from collections.abc import Iterable, Sequence +from typing import TYPE_CHECKING, Any, ClassVar, Final, NewType, TypeVar, Union, cast, overload +from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard + +from librt.internal import ( + read_int as read_int_bare, + read_str as read_str_bare, + write_int as write_int_bare, + write_str as write_str_bare, ) -from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard, overload import mypy.nodes from mypy.bogus_type import Bogus -from mypy.nodes import ( - ARG_POS, - ARG_STAR, - ARG_STAR2, - INVARIANT, - ArgKind, - FakeInfo, - FuncDef, - SymbolNode, +from mypy.cache import ( + DICT_STR_GEN, + END_TAG, + EXTRA_ATTRS, + LIST_GEN, + LITERAL_NONE, + ReadBuffer, + Tag, + WriteBuffer, + read_bool, + read_int, + read_int_list, + read_literal, + read_str, + read_str_list, + read_str_opt, + read_str_opt_list, + read_tag, + write_bool, + write_int, + write_int_list, + write_literal, + write_str, + write_str_list, + write_str_opt, + write_str_opt_list, + write_tag, ) +from mypy.nodes import ARG_KINDS, ARG_POS, ARG_STAR, ARG_STAR2, INVARIANT, ArgKind, SymbolNode from mypy.options import Options from mypy.state import state from mypy.util import IdMapper T = TypeVar("T") -JsonDict: _TypeAlias = Dict[str, Any] +JsonDict: _TypeAlias = dict[str, Any] # The set of all valid expressions that can currently be contained # inside of a Literal[...]. @@ -85,6 +99,9 @@ TypeVisitor as TypeVisitor, ) +TUPLE_NAMES: Final = ("builtins.tuple", "typing.Tuple") +TYPE_NAMES: Final = ("builtins.type", "typing.Type") + TYPE_VAR_LIKE_NAMES: Final = ( "typing.TypeVar", "typing_extensions.TypeVar", @@ -131,9 +148,18 @@ # Supported Annotated type names. ANNOTATED_TYPE_NAMES: Final = ("typing.Annotated", "typing_extensions.Annotated") -# Supported @deprecated type names +# Supported Concatenate type names. +CONCATENATE_TYPE_NAMES: Final = ("typing.Concatenate", "typing_extensions.Concatenate") + +# Supported Unpack type names. +UNPACK_TYPE_NAMES: Final = ("typing.Unpack", "typing_extensions.Unpack") + +# Supported @deprecated decorator names DEPRECATED_TYPE_NAMES: Final = ("warnings.deprecated", "typing_extensions.deprecated") +# Supported @disjoint_base decorator names +DISJOINT_BASE_DECORATOR_NAMES: Final = ("typing.disjoint_base", "typing_extensions.disjoint_base") + # We use this constant in various places when checking `tuple` subtyping: TUPLE_LIKE_INSTANCE_NAMES: Final = ( "builtins.tuple", @@ -150,10 +176,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", @@ -177,6 +199,10 @@ # Supported @override decorator names. OVERRIDE_DECORATOR_NAMES: Final = ("typing.override", "typing_extensions.override") +ELLIPSIS_TYPE_NAMES: Final = ("builtins.ellipsis", "types.EllipsisType") + +NOT_IMPLEMENTED_TYPE_NAMES: Final = ("builtins._NotImplementedType", "types.NotImplementedType") + # A placeholder used for Bogus[...] parameters _dummy: Final[Any] = object() @@ -287,6 +313,13 @@ def serialize(self) -> JsonDict | str: def deserialize(cls, data: JsonDict) -> Type: raise NotImplementedError(f"Cannot deserialize {cls.__name__} instance") + def write(self, data: WriteBuffer) -> None: + raise NotImplementedError(f"Cannot serialize {self.__class__.__name__} instance") + + @classmethod + def read(cls, data: ReadBuffer) -> Type: + raise NotImplementedError(f"Cannot deserialize {cls.__name__} instance") + def is_singleton_type(self) -> bool: return False @@ -349,34 +382,7 @@ def _expand_once(self) -> Type: ): mapping[tvar.id] = sub - new_tp = self.alias.target.accept(InstantiateAliasVisitor(mapping)) - new_tp.accept(LocationSetter(self.line, self.column)) - new_tp.line = self.line - new_tp.column = self.column - return new_tp - - def _partial_expansion(self, nothing_args: bool = False) -> tuple[ProperType, bool]: - # Private method mostly for debugging and testing. - unroller = UnrollAliasVisitor(set(), {}) - if nothing_args: - alias = self.copy_modified(args=[UninhabitedType()] * len(self.args)) - else: - alias = self - unrolled = alias.accept(unroller) - assert isinstance(unrolled, ProperType) - return unrolled, unroller.recursed - - def expand_all_if_possible(self, nothing_args: bool = False) -> ProperType | None: - """Attempt a full expansion of the type alias (including nested aliases). - - If the expansion is not possible, i.e. the alias is (mutually-)recursive, - return None. If nothing_args is True, replace all type arguments with an - UninhabitedType() (used to detect recursively defined aliases). - """ - unrolled, recursed = self._partial_expansion(nothing_args=nothing_args) - if recursed: - return None - return unrolled + return self.alias.target.accept(InstantiateAliasVisitor(mapping)) @property def is_recursive(self) -> bool: @@ -390,7 +396,7 @@ def is_recursive(self) -> bool: assert self.alias is not None, "Unfixed type alias" is_recursive = self.alias._is_recursive if is_recursive is None: - is_recursive = self.expand_all_if_possible(nothing_args=True) is None + is_recursive = self.alias in self.alias.target.accept(CollectAliasesVisitor()) # We cache the value on the underlying TypeAlias node as an optimization, # since the value is the same for all instances of the same alias. self.alias._is_recursive = is_recursive @@ -406,6 +412,11 @@ def can_be_false_default(self) -> bool: return self.alias.target.can_be_false return super().can_be_false_default() + def copy_modified(self, *, args: list[Type] | None = None) -> TypeAliasType: + return TypeAliasType( + self.alias, args if args is not None else self.args.copy(), self.line, self.column + ) + def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_type_alias_type(self) @@ -439,10 +450,19 @@ def deserialize(cls, data: JsonDict) -> TypeAliasType: alias.type_ref = data["type_ref"] return alias - def copy_modified(self, *, args: list[Type] | None = None) -> TypeAliasType: - return TypeAliasType( - self.alias, args if args is not None else self.args.copy(), self.line, self.column - ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_ALIAS_TYPE) + write_type_list(data, self.args) + assert self.alias is not None + write_str(data, self.alias.fullname) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeAliasType: + alias = TypeAliasType(None, read_type_list(data)) + alias.type_ref = read_str(data) + assert read_tag(data) == END_TAG + return alias class TypeGuardedType(Type): @@ -457,6 +477,11 @@ def __init__(self, type_guard: Type) -> None: def __repr__(self) -> str: return f"TypeGuard({self.type_guard})" + # This may hide some real bugs, but it is convenient for various "synthetic" + # visitors, similar to RequiredType and ReadOnlyType below. + def accept(self, visitor: TypeVisitor[T]) -> T: + return self.type_guard.accept(visitor) + class RequiredType(Type): """Required[T] or NotRequired[T]. Only usable at top-level of a TypedDict definition.""" @@ -512,7 +537,7 @@ class TypeVarId: # function type variables. # Metavariables are allocated unique ids starting from 1. - raw_id: int + raw_id: Final[int] # Level of the variable in type inference. Currently either 0 for # declared types, or 1 for type inference metavariables. @@ -540,6 +565,10 @@ def __repr__(self) -> str: return self.raw_id.__repr__() def __eq__(self, other: object) -> bool: + # Although this call is not expensive (like UnionType or TypedDictType), + # most of the time we get the same object here, so add a fast path. + if self is other: + return True return ( isinstance(other, TypeVarId) and self.raw_id == other.raw_id @@ -551,7 +580,7 @@ def __ne__(self, other: object) -> bool: return not (self == other) def __hash__(self) -> int: - return hash((self.raw_id, self.meta_level, self.namespace)) + return self.raw_id ^ (self.meta_level << 8) ^ hash(self.namespace) def is_meta_var(self) -> bool: return self.meta_level > 0 @@ -606,6 +635,11 @@ def has_default(self) -> bool: t = get_proper_type(self.default) return not (isinstance(t, AnyType) and t.type_of_any == TypeOfAny.from_omitted_generics) + def values_or_bound(self) -> ProperType: + if isinstance(self, TypeVarType) and self.values: + return UnionType(self.values) + return get_proper_type(self.upper_bound) + class TypeVarType(TypeVarLikeType): """Type that refers to a type variable.""" @@ -697,6 +731,32 @@ def deserialize(cls, data: JsonDict) -> TypeVarType: variance=data["variance"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_VAR_TYPE) + write_str(data, self.name) + write_str(data, self.fullname) + write_int(data, self.id.raw_id) + write_str(data, self.id.namespace) + write_type_list(data, self.values) + self.upper_bound.write(data) + self.default.write(data) + write_int(data, self.variance) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeVarType: + ret = TypeVarType( + read_str(data), + read_str(data), + TypeVarId(read_int(data), namespace=read_str(data)), + read_type_list(data), + read_type(data), + read_type(data), + read_int(data), + ) + assert read_tag(data) == END_TAG + return ret + class ParamSpecFlavor: # Simple ParamSpec reference such as "P" @@ -826,6 +886,34 @@ def deserialize(cls, data: JsonDict) -> ParamSpecType: prefix=Parameters.deserialize(data["prefix"]), ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, PARAM_SPEC_TYPE) + self.prefix.write(data) + write_str(data, self.name) + write_str(data, self.fullname) + write_int(data, self.id.raw_id) + write_str(data, self.id.namespace) + write_int(data, self.flavor) + self.upper_bound.write(data) + self.default.write(data) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> ParamSpecType: + assert read_tag(data) == PARAMETERS + prefix = Parameters.read(data) + ret = ParamSpecType( + read_str(data), + read_str(data), + TypeVarId(read_int(data), namespace=read_str(data)), + read_int(data), + read_type(data), + read_type(data), + prefix=prefix, + ) + assert read_tag(data) == END_TAG + return ret + class TypeVarTupleType(TypeVarLikeType): """Type that refers to a TypeVarTuple. @@ -881,6 +969,34 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleType: min_len=data["min_len"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_VAR_TUPLE_TYPE) + self.tuple_fallback.write(data) + write_str(data, self.name) + write_str(data, self.fullname) + write_int(data, self.id.raw_id) + write_str(data, self.id.namespace) + self.upper_bound.write(data) + self.default.write(data) + write_int(data, self.min_len) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypeVarTupleType: + assert read_tag(data) == INSTANCE + fallback = Instance.read(data) + ret = TypeVarTupleType( + read_str(data), + read_str(data), + TypeVarId(read_int(data), namespace=read_str(data)), + read_type(data), + fallback, + read_type(data), + min_len=read_int(data), + ) + assert read_tag(data) == END_TAG + return ret + def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_type_var_tuple(self) @@ -1012,6 +1128,25 @@ def deserialize(cls, data: JsonDict) -> UnboundType: original_str_fallback=data["expr_fallback"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, UNBOUND_TYPE) + write_str(data, self.name) + write_type_list(data, self.args) + write_str_opt(data, self.original_str_expr) + write_str_opt(data, self.original_str_fallback) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> UnboundType: + ret = UnboundType( + read_str(data), + read_type_list(data), + original_str_expr=read_str_opt(data), + original_str_fallback=read_str_opt(data), + ) + assert read_tag(data) == END_TAG + return ret + class CallableArgument(ProperType): """Represents a Arg(type, 'name') inside a Callable's type list. @@ -1106,6 +1241,17 @@ def accept(self, visitor: TypeVisitor[T]) -> T: def serialize(self) -> JsonDict: return {".class": "UnpackType", "type": self.type.serialize()} + def write(self, data: WriteBuffer) -> None: + write_tag(data, UNPACK_TYPE) + self.type.write(data) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> UnpackType: + ret = UnpackType(read_type(data)) + assert read_tag(data) == END_TAG + return ret + @classmethod def deserialize(cls, data: JsonDict) -> UnpackType: assert data[".class"] == "UnpackType" @@ -1207,6 +1353,25 @@ def deserialize(cls, data: JsonDict) -> AnyType: data["missing_import_name"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, ANY_TYPE) + write_type_opt(data, self.source_any) + write_int(data, self.type_of_any) + write_str_opt(data, self.missing_import_name) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> AnyType: + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == ANY_TYPE + source_any = AnyType.read(data) + else: + source_any = None + ret = AnyType(read_int(data), source_any, read_str_opt(data)) + assert read_tag(data) == END_TAG + return ret + class UninhabitedType(ProperType): """This type has no members. @@ -1240,10 +1405,10 @@ def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_uninhabited_type(self) def __hash__(self) -> int: - return hash(UninhabitedType) + return hash((UninhabitedType, self.ambiguous)) def __eq__(self, other: object) -> bool: - return isinstance(other, UninhabitedType) + return isinstance(other, UninhabitedType) and other.ambiguous == self.ambiguous def serialize(self) -> JsonDict: return {".class": "UninhabitedType"} @@ -1253,6 +1418,15 @@ def deserialize(cls, data: JsonDict) -> UninhabitedType: assert data[".class"] == "UninhabitedType" return UninhabitedType() + def write(self, data: WriteBuffer) -> None: + write_tag(data, UNINHABITED_TYPE) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> UninhabitedType: + assert read_tag(data) == END_TAG + return UninhabitedType() + class NoneType(ProperType): """The type of 'None'. @@ -1285,6 +1459,15 @@ def deserialize(cls, data: JsonDict) -> NoneType: assert data[".class"] == "NoneType" return NoneType() + def write(self, data: WriteBuffer) -> None: + write_tag(data, NONE_TYPE) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> NoneType: + assert read_tag(data) == END_TAG + return NoneType() + def is_singleton_type(self) -> bool: return True @@ -1332,6 +1515,17 @@ def deserialize(cls, data: JsonDict) -> DeletedType: assert data[".class"] == "DeletedType" return DeletedType(data["source"]) + def write(self, data: WriteBuffer) -> None: + write_tag(data, DELETED_TYPE) + write_str_opt(data, self.source) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> DeletedType: + ret = DeletedType(read_str_opt(data)) + assert read_tag(data) == END_TAG + return ret + # Fake TypeInfo to be used as a placeholder during Instance de-serialization. NOT_READY: Final = mypy.nodes.FakeInfo("De-serialization failure: TypeInfo not fixed") @@ -1374,7 +1568,7 @@ def serialize(self) -> JsonDict: return { ".class": "ExtraAttrs", "attrs": {k: v.serialize() for k, v in self.attrs.items()}, - "immutable": list(self.immutable), + "immutable": sorted(self.immutable), "mod_name": self.mod_name, } @@ -1387,6 +1581,19 @@ def deserialize(cls, data: JsonDict) -> ExtraAttrs: data["mod_name"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, EXTRA_ATTRS) + write_type_map(data, self.attrs) + write_str_list(data, sorted(self.immutable)) + write_str_opt(data, self.mod_name) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> ExtraAttrs: + ret = ExtraAttrs(read_type_map(data), set(read_str_list(data)), read_str_opt(data)) + assert read_tag(data) == END_TAG + return ret + class Instance(ProperType): """An instance type of form C[T1, ..., Tn]. @@ -1491,11 +1698,13 @@ def __eq__(self, other: object) -> bool: def serialize(self) -> JsonDict | str: assert self.type is not None type_ref = self.type.fullname - if not self.args and not self.last_known_value: + if not self.args and not self.last_known_value and not self.extra_attrs: 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 @@ -1521,6 +1730,83 @@ def deserialize(cls, data: JsonDict | str) -> Instance: inst.extra_attrs = ExtraAttrs.deserialize(data["extra_attrs"]) return inst + def write(self, data: WriteBuffer) -> None: + write_tag(data, INSTANCE) + if not self.args and not self.last_known_value and not self.extra_attrs: + type_ref = self.type.fullname + if type_ref == "builtins.str": + write_tag(data, INSTANCE_STR) + elif type_ref == "builtins.function": + write_tag(data, INSTANCE_FUNCTION) + elif type_ref == "builtins.int": + write_tag(data, INSTANCE_INT) + elif type_ref == "builtins.bool": + write_tag(data, INSTANCE_BOOL) + elif type_ref == "builtins.object": + write_tag(data, INSTANCE_OBJECT) + else: + write_tag(data, INSTANCE_SIMPLE) + write_str_bare(data, type_ref) + return + write_tag(data, INSTANCE_GENERIC) + write_str(data, self.type.fullname) + write_type_list(data, self.args) + write_type_opt(data, self.last_known_value) + if self.extra_attrs is None: + write_tag(data, LITERAL_NONE) + else: + self.extra_attrs.write(data) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> Instance: + tag = read_tag(data) + # This is quite verbose, but this is very hot code, so we are not + # using dictionary lookups here. + if tag == INSTANCE_STR: + if instance_cache.str_type is None: + instance_cache.str_type = Instance(NOT_READY, []) + instance_cache.str_type.type_ref = "builtins.str" + return instance_cache.str_type + if tag == INSTANCE_FUNCTION: + if instance_cache.function_type is None: + instance_cache.function_type = Instance(NOT_READY, []) + instance_cache.function_type.type_ref = "builtins.function" + return instance_cache.function_type + if tag == INSTANCE_INT: + if instance_cache.int_type is None: + instance_cache.int_type = Instance(NOT_READY, []) + instance_cache.int_type.type_ref = "builtins.int" + return instance_cache.int_type + if tag == INSTANCE_BOOL: + if instance_cache.bool_type is None: + instance_cache.bool_type = Instance(NOT_READY, []) + instance_cache.bool_type.type_ref = "builtins.bool" + return instance_cache.bool_type + if tag == INSTANCE_OBJECT: + if instance_cache.object_type is None: + instance_cache.object_type = Instance(NOT_READY, []) + instance_cache.object_type.type_ref = "builtins.object" + return instance_cache.object_type + if tag == INSTANCE_SIMPLE: + inst = Instance(NOT_READY, []) + inst.type_ref = read_str_bare(data) + return inst + assert tag == INSTANCE_GENERIC + type_ref = read_str(data) + inst = Instance(NOT_READY, read_type_list(data)) + inst.type_ref = type_ref + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == LITERAL_TYPE + inst.last_known_value = LiteralType.read(data) + tag = read_tag(data) + if tag != LITERAL_NONE: + assert tag == EXTRA_ATTRS + inst.extra_attrs = ExtraAttrs.read(data) + assert read_tag(data) == END_TAG + return inst + def copy_modified( self, *, @@ -1537,7 +1823,6 @@ def copy_modified( ), extra_attrs=self.extra_attrs, ) - # We intentionally don't copy the extra_attrs here, so they will be erased. new.can_be_true = self.can_be_true new.can_be_false = self.can_be_false return new @@ -1557,15 +1842,28 @@ 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 - or self.type.fullname in {"builtins.ellipsis", "types.EllipsisType"} + and len(self.type.enum_members) == 1 + or self.type.fullname in ELLIPSIS_TYPE_NAMES ) - 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 InstanceCache: + def __init__(self) -> None: + self.str_type: Instance | None = None + self.function_type: Instance | None = None + self.int_type: Instance | None = None + self.bool_type: Instance | None = None + self.object_type: Instance | None = None + + def reset(self) -> None: + self.str_type = None + self.function_type = None + self.int_type = None + self.bool_type = None + self.object_type = None + + +instance_cache: Final = InstanceCache() class FunctionLike(ProperType): @@ -1600,12 +1898,29 @@ def with_name(self, name: str) -> FunctionLike: def get_name(self) -> str | None: pass + def bound(self) -> bool: + return bool(self.items) and self.items[0].is_bound -class FormalArgument(NamedTuple): - name: str | None - pos: int | None - typ: Type - required: bool + +class FormalArgument: + def __init__(self, name: str | None, pos: int | None, typ: Type, required: bool) -> None: + self.name = name + self.pos = pos + self.typ = typ + self.required = required + + def __eq__(self, other: object) -> bool: + if not isinstance(other, FormalArgument): + return NotImplemented + return ( + self.name == other.name + and self.pos == other.pos + and self.typ == other.typ + and self.required == other.required + ) + + def __hash__(self) -> int: + return hash((self.name, self.pos, self.typ, self.required)) class Parameters(ProperType): @@ -1778,12 +2093,37 @@ def deserialize(cls, data: JsonDict) -> Parameters: assert data[".class"] == "Parameters" return Parameters( [deserialize_type(t) for t in data["arg_types"]], - [ArgKind(x) for x in data["arg_kinds"]], + # This is a micro-optimization until mypyc gets dedicated enum support. Otherwise, + # we would spend ~20% of types deserialization time in Enum.__call__(). + [ARG_KINDS[x] for x in data["arg_kinds"]], data["arg_names"], variables=[cast(TypeVarLikeType, deserialize_type(v)) for v in data["variables"]], imprecise_arg_kinds=data["imprecise_arg_kinds"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, PARAMETERS) + write_type_list(data, self.arg_types) + write_int_list(data, [int(x.value) for x in self.arg_kinds]) + write_str_opt_list(data, self.arg_names) + write_type_list(data, self.variables) + write_bool(data, self.imprecise_arg_kinds) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> Parameters: + ret = Parameters( + read_type_list(data), + # This is a micro-optimization until mypyc gets dedicated enum support. Otherwise, + # we would spend ~20% of types deserialization time in Enum.__call__(). + [ARG_KINDS[ak] for ak in read_int_list(data)], + read_str_opt_list(data), + variables=read_type_var_likes(data), + imprecise_arg_kinds=read_bool(data), + ) + assert read_tag(data) == END_TAG + return ret + def __hash__(self) -> int: return hash( ( @@ -1795,7 +2135,7 @@ def __hash__(self) -> int: ) def __eq__(self, other: object) -> bool: - if isinstance(other, (Parameters, CallableType)): + if isinstance(other, Parameters): return ( self.arg_types == other.arg_types and self.arg_names == other.arg_names @@ -1825,14 +2165,11 @@ 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 - # tools that consume mypy ASTs - "def_extras", # Information about original definition we want to serialize. - # This is used for more detailed error messages. + "is_bound", # Is this a bound method? "type_guard", # T, if -> TypeGuard[T] (ret_type is bool in this case). "type_is", # T, if -> TypeIs[T] (ret_type is bool in this case). "from_concatenate", # whether this callable is from a concatenate object @@ -1858,8 +2195,7 @@ def __init__( implicit: bool = False, special_sig: str | None = None, from_type_type: bool = False, - bound_args: Sequence[Type | None] = (), - def_extras: dict[str, Any] | None = None, + is_bound: bool = False, type_guard: Type | None = None, type_is: Type | None = None, from_concatenate: bool = False, @@ -1868,14 +2204,12 @@ def __init__( ) -> None: super().__init__(line, column) assert len(arg_types) == len(arg_kinds) == len(arg_names) - for t, k in zip(arg_types, arg_kinds): + self.arg_types = list(arg_types) + for t in self.arg_types: if isinstance(t, ParamSpecType): assert not t.prefix.arg_types # TODO: should we assert that only ARG_STAR contain ParamSpecType? # See testParamSpecJoin, that relies on passing e.g `P.args` as plain argument. - if variables is None: - variables = [] - self.arg_types = list(arg_types) self.arg_kinds = arg_kinds self.arg_names = list(arg_names) self.min_args = arg_kinds.count(ARG_POS) @@ -1883,33 +2217,22 @@ def __init__( self.fallback = fallback assert not name or " mypy.nodes.TypeInfo: ret = ret.partial_fallback if isinstance(ret, TypedDictType): ret = ret.fallback + if isinstance(ret, LiteralType): + ret = ret.fallback assert isinstance(ret, Instance) return ret.type @@ -2213,7 +2536,7 @@ def with_normalized_var_args(self) -> Self: new_unpack = nested_unpacked.args[0] else: if not isinstance(nested_unpacked, TypeVarTupleType): - # We found a non-nomralized tuple type, this means this method + # We found a non-normalized tuple type, this means this method # is called during semantic analysis (e.g. from get_proper_type()) # there is no point in normalizing callables at this stage. return self @@ -2232,15 +2555,9 @@ def with_normalized_var_args(self) -> Self: ) def __hash__(self) -> int: - # self.is_type_obj() will fail if self.fallback.type is a FakeInfo - if isinstance(self.fallback.type, FakeInfo): - is_type_obj = 2 - else: - is_type_obj = self.is_type_obj() return hash( ( self.ret_type, - is_type_obj, self.is_ellipsis_args, self.name, tuple(self.arg_types), @@ -2258,8 +2575,9 @@ def __eq__(self, other: object) -> bool: and self.arg_names == other.arg_names and self.arg_kinds == other.arg_kinds and self.name == other.name - and self.is_type_obj() == other.is_type_obj() and self.is_ellipsis_args == other.is_ellipsis_args + and self.type_guard == other.type_guard + and self.type_is == other.type_is and self.fallback == other.fallback ) else: @@ -2280,8 +2598,7 @@ def serialize(self) -> JsonDict: "variables": [v.serialize() for v in self.variables], "is_ellipsis_args": self.is_ellipsis_args, "implicit": self.implicit, - "bound_args": [(None if t is None else t.serialize()) for t in self.bound_args], - "def_extras": dict(self.def_extras), + "is_bound": self.is_bound, "type_guard": self.type_guard.serialize() if self.type_guard is not None else None, "type_is": (self.type_is.serialize() if self.type_is is not None else None), "from_concatenate": self.from_concatenate, @@ -2292,10 +2609,10 @@ def serialize(self) -> JsonDict: @classmethod def deserialize(cls, data: JsonDict) -> CallableType: assert data[".class"] == "CallableType" - # TODO: Set definition to the containing SymbolNode? + # The .definition link is set in fixup.py. return CallableType( [deserialize_type(t) for t in data["arg_types"]], - [ArgKind(x) for x in data["arg_kinds"]], + [ARG_KINDS[x] for x in data["arg_kinds"]], data["arg_names"], deserialize_type(data["ret_type"]), Instance.deserialize(data["fallback"]), @@ -2303,8 +2620,7 @@ def deserialize(cls, data: JsonDict) -> CallableType: variables=[cast(TypeVarLikeType, deserialize_type(v)) for v in data["variables"]], is_ellipsis_args=data["is_ellipsis_args"], implicit=data["implicit"], - bound_args=[(None if t is None else deserialize_type(t)) for t in data["bound_args"]], - def_extras=data["def_extras"], + is_bound=data["is_bound"], type_guard=( deserialize_type(data["type_guard"]) if data["type_guard"] is not None else None ), @@ -2314,6 +2630,49 @@ def deserialize(cls, data: JsonDict) -> CallableType: unpack_kwargs=data["unpack_kwargs"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, CALLABLE_TYPE) + self.fallback.write(data) + write_type_list(data, self.arg_types) + write_int_list(data, [int(x.value) for x in self.arg_kinds]) + write_str_opt_list(data, self.arg_names) + self.ret_type.write(data) + write_str_opt(data, self.name) + write_type_list(data, self.variables) + write_bool(data, self.is_ellipsis_args) + write_bool(data, self.implicit) + write_bool(data, self.is_bound) + write_type_opt(data, self.type_guard) + write_type_opt(data, self.type_is) + write_bool(data, self.from_concatenate) + write_bool(data, self.imprecise_arg_kinds) + write_bool(data, self.unpack_kwargs) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> CallableType: + assert read_tag(data) == INSTANCE + fallback = Instance.read(data) + ret = CallableType( + read_type_list(data), + [ARG_KINDS[ak] for ak in read_int_list(data)], + read_str_opt_list(data), + read_type(data), + fallback, + name=read_str_opt(data), + variables=read_type_var_likes(data), + is_ellipsis_args=read_bool(data), + implicit=read_bool(data), + is_bound=read_bool(data), + type_guard=read_type_opt(data), + type_is=read_type_opt(data), + from_concatenate=read_bool(data), + imprecise_arg_kinds=read_bool(data), + unpack_kwargs=read_bool(data), + ) + assert read_tag(data) == END_TAG + return ret + # This is a little safety net to prevent reckless special-casing of callables # that can potentially break Unpack[...] with **kwargs. @@ -2389,6 +2748,21 @@ def deserialize(cls, data: JsonDict) -> Overloaded: assert data[".class"] == "Overloaded" return Overloaded([CallableType.deserialize(t) for t in data["items"]]) + def write(self, data: WriteBuffer) -> None: + write_tag(data, OVERLOADED) + write_type_list(data, self.items) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> Overloaded: + items = [] + assert read_tag(data) == LIST_GEN + for _ in range(read_int_bare(data)): + assert read_tag(data) == CALLABLE_TYPE + items.append(CallableType.read(data)) + assert read_tag(data) == END_TAG + return Overloaded(items) + class TupleType(ProperType): """The tuple type Tuple[T1, ..., Tn] (at least one type argument). @@ -2485,6 +2859,21 @@ def deserialize(cls, data: JsonDict) -> TupleType: implicit=data["implicit"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TUPLE_TYPE) + self.partial_fallback.write(data) + write_type_list(data, self.items) + write_bool(data, self.implicit) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TupleType: + assert read_tag(data) == INSTANCE + fallback = Instance.read(data) + ret = TupleType(read_type_list(data), fallback, implicit=read_bool(data)) + assert read_tag(data) == END_TAG + return ret + def copy_modified( self, *, fallback: Instance | None = None, items: list[Type] | None = None ) -> TupleType: @@ -2500,6 +2889,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) @@ -2652,6 +3044,24 @@ def deserialize(cls, data: JsonDict) -> TypedDictType: Instance.deserialize(data["fallback"]), ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPED_DICT_TYPE) + self.fallback.write(data) + write_type_map(data, self.items) + write_str_list(data, sorted(self.required_keys)) + write_str_list(data, sorted(self.readonly_keys)) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> TypedDictType: + assert read_tag(data) == INSTANCE + fallback = Instance.read(data) + ret = TypedDictType( + read_type_map(data), set(read_str_list(data)), set(read_str_list(data)), fallback + ) + assert read_tag(data) == END_TAG + return ret + @property def is_final(self) -> bool: return self.fallback.type.is_final @@ -2823,10 +3233,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: @@ -2882,6 +3310,21 @@ def deserialize(cls, data: JsonDict) -> LiteralType: assert data[".class"] == "LiteralType" return LiteralType(value=data["value"], fallback=Instance.deserialize(data["fallback"])) + def write(self, data: WriteBuffer) -> None: + write_tag(data, LITERAL_TYPE) + self.fallback.write(data) + write_literal(data, self.value) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> LiteralType: + assert read_tag(data) == INSTANCE + fallback = Instance.read(data) + tag = read_tag(data) + ret = LiteralType(read_literal(data, tag), fallback) + assert read_tag(data) == END_TAG + return ret + def is_singleton_type(self) -> bool: return self.is_enum_literal() or isinstance(self.value, bool) @@ -2932,6 +3375,8 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: if not isinstance(other, UnionType): return NotImplemented + if self is other: + return True return frozenset(self.items) == frozenset(other.items) @overload @@ -2981,6 +3426,18 @@ def deserialize(cls, data: JsonDict) -> UnionType: uses_pep604_syntax=data["uses_pep604_syntax"], ) + def write(self, data: WriteBuffer) -> None: + write_tag(data, UNION_TYPE) + write_type_list(data, self.items) + write_bool(data, self.uses_pep604_syntax) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> UnionType: + ret = UnionType(read_type_list(data), uses_pep604_syntax=read_bool(data)) + assert read_tag(data) == END_TAG + return ret + class PartialType(ProperType): """Type such as List[?] where type arguments are unknown, or partial None type. @@ -3041,11 +3498,14 @@ def serialize(self) -> JsonDict: class TypeType(ProperType): - """For types like Type[User]. + """For types like Type[User] or TypeForm[User | None]. - This annotates variables that are class objects, constrained by + Type[C] annotates variables that are class objects, constrained by the type argument. See PEP 484 for more details. + TypeForm[T] annotates variables that hold the result of evaluating + a type expression. See PEP 747 for more details. + We may encounter expressions whose values are specific classes; those are represented as callables (possibly overloaded) corresponding to the class's constructor's signature and returning @@ -3068,35 +3528,47 @@ class TypeType(ProperType): assumption). """ - __slots__ = ("item",) + __slots__ = ("item", "is_type_form") # This can't be everything, but it can be a class reference, # a generic class instance, a union, Any, a type variable... item: ProperType + # If True then this TypeType represents a TypeForm[T]. + # If False then this TypeType represents a Type[C]. + is_type_form: bool + def __init__( self, item: Bogus[Instance | AnyType | TypeVarType | TupleType | NoneType | CallableType], *, line: int = -1, column: int = -1, + is_type_form: bool = False, ) -> None: """To ensure Type[Union[A, B]] is always represented as Union[Type[A], Type[B]], item of type UnionType must be handled through make_normalized static method. """ super().__init__(line, column) self.item = item + self.is_type_form = is_type_form @staticmethod - def make_normalized(item: Type, *, line: int = -1, column: int = -1) -> ProperType: + def make_normalized( + item: Type, *, line: int = -1, column: int = -1, is_type_form: bool = False + ) -> ProperType: item = get_proper_type(item) - if isinstance(item, UnionType): - return UnionType.make_union( - [TypeType.make_normalized(union_item) for union_item in item.items], - line=line, - column=column, - ) - return TypeType(item, line=line, column=column) # type: ignore[arg-type] + if is_type_form: + # Don't convert TypeForm[X | Y] to (TypeForm[X] | TypeForm[Y]) + pass + else: + if isinstance(item, UnionType): + return UnionType.make_union( + [TypeType.make_normalized(union_item) for union_item in item.items], + line=line, + column=column, + ) + return TypeType(item, line=line, column=column, is_type_form=is_type_form) # type: ignore[arg-type] def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_type_type(self) @@ -3107,15 +3579,32 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: if not isinstance(other, TypeType): return NotImplemented - return self.item == other.item + return self.item == other.item and self.is_type_form == other.is_type_form def serialize(self) -> JsonDict: - return {".class": "TypeType", "item": self.item.serialize()} + return { + ".class": "TypeType", + "item": self.item.serialize(), + "is_type_form": self.is_type_form, + } @classmethod def deserialize(cls, data: JsonDict) -> Type: assert data[".class"] == "TypeType" - return TypeType.make_normalized(deserialize_type(data["item"])) + return TypeType.make_normalized( + deserialize_type(data["item"]), is_type_form=data["is_type_form"] + ) + + def write(self, data: WriteBuffer) -> None: + write_tag(data, TYPE_TYPE) + self.item.write(data) + write_tag(data, END_TAG) + + @classmethod + def read(cls, data: ReadBuffer) -> Type: + ret = TypeType.make_normalized(read_type(data)) + assert read_tag(data) == END_TAG + return ret class PlaceholderType(ProperType): @@ -3179,7 +3668,8 @@ def get_proper_type(typ: Type | None) -> ProperType | None: """ if typ is None: return None - if isinstance(typ, TypeGuardedType): # type: ignore[misc] + # TODO: this is an ugly hack, remove. + if isinstance(typ, TypeGuardedType): typ = typ.type_guard while isinstance(typ, TypeAliasType): typ = typ._expand_once() @@ -3193,19 +3683,17 @@ def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: @overload def get_proper_types( - types: list[Type | None] | tuple[Type | None, ...] + types: list[Type | None] | tuple[Type | None, ...], ) -> list[ProperType | None]: ... def get_proper_types( - types: list[Type] | list[Type | None] | tuple[Type | None, ...] + types: list[Type] | list[Type | None] | tuple[Type | None, ...], ) -> list[ProperType] | list[ProperType | None]: if isinstance(types, list): typelist = types # Optimize for the common case so that we don't need to allocate anything - if not any( - isinstance(t, (TypeAliasType, TypeGuardedType)) for t in typelist # type: ignore[misc] - ): + if not any(isinstance(t, (TypeAliasType, TypeGuardedType)) for t in typelist): return cast("list[ProperType]", typelist) return [get_proper_type(t) for t in typelist] else: @@ -3225,7 +3713,6 @@ def get_proper_types( TypeTranslator as TypeTranslator, TypeVisitor as TypeVisitor, ) -from mypy.typetraverser import TypeTraverserVisitor class TypeStrVisitor(SyntheticTypeVisitor[str]): @@ -3242,46 +3729,44 @@ class TypeStrVisitor(SyntheticTypeVisitor[str]): def __init__(self, id_mapper: IdMapper | None = None, *, options: Options) -> None: self.id_mapper = id_mapper - self.any_as_dots = False self.options = options + self.dotted_aliases: set[TypeAliasType] | None = None - def visit_unbound_type(self, t: UnboundType) -> str: + def visit_unbound_type(self, t: UnboundType, /) -> str: s = t.name + "?" if t.args: s += f"[{self.list_str(t.args)}]" return s - def visit_type_list(self, t: TypeList) -> str: + def visit_type_list(self, t: TypeList, /) -> str: return f"" - def visit_callable_argument(self, t: CallableArgument) -> str: + def visit_callable_argument(self, t: CallableArgument, /) -> str: typ = t.typ.accept(self) if t.name is None: return f"{t.constructor}({typ})" else: return f"{t.constructor}({typ}, {t.name})" - def visit_any(self, t: AnyType) -> str: - if self.any_as_dots and t.type_of_any == TypeOfAny.special_form: - return "..." + def visit_any(self, t: AnyType, /) -> str: return "Any" - def visit_none_type(self, t: NoneType) -> str: + def visit_none_type(self, t: NoneType, /) -> str: return "None" - def visit_uninhabited_type(self, t: UninhabitedType) -> str: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> str: return "Never" - def visit_erased_type(self, t: ErasedType) -> str: + def visit_erased_type(self, t: ErasedType, /) -> str: return "" - def visit_deleted_type(self, t: DeletedType) -> str: + def visit_deleted_type(self, t: DeletedType, /) -> str: if t.source is None: return "" else: return f"" - def visit_instance(self, t: Instance) -> str: + def visit_instance(self, t: Instance, /) -> str: if t.last_known_value and not t.args: # Instances with a literal fallback should never be generic. If they are, # something went wrong so we fall back to showing the full Instance repr. @@ -3301,37 +3786,27 @@ def visit_instance(self, t: Instance) -> str: s += f"<{self.id_mapper.id(t.type)}>" return s - def visit_type_var(self, t: TypeVarType) -> str: - if t.name is None: - # Anonymous type variable type (only numeric id). - s = f"`{t.id}" - else: - # Named type variable type. - s = f"{t.name}`{t.id}" + def visit_type_var(self, t: TypeVarType, /) -> str: + s = f"{t.name}`{t.id}" if self.id_mapper and t.upper_bound: s += f"(upper_bound={t.upper_bound.accept(self)})" if t.has_default(): s += f" = {t.default.accept(self)}" return s - def visit_param_spec(self, t: ParamSpecType) -> str: + def visit_param_spec(self, t: ParamSpecType, /) -> str: # prefixes are displayed as Concatenate s = "" if t.prefix.arg_types: s += f"[{self.list_str(t.prefix.arg_types)}, **" - if t.name is None: - # Anonymous type variable type (only numeric id). - s += f"`{t.id}" - else: - # Named type variable type. - s += f"{t.name_with_suffix()}`{t.id}" + s += f"{t.name_with_suffix()}`{t.id}" if t.prefix.arg_types: s += "]" if t.has_default(): s += f" = {t.default.accept(self)}" return s - def visit_parameters(self, t: Parameters) -> str: + def visit_parameters(self, t: Parameters, /) -> str: # This is copied from visit_callable -- is there a way to decrease duplication? if t.is_ellipsis_args: return "..." @@ -3360,18 +3835,13 @@ def visit_parameters(self, t: Parameters) -> str: return f"[{s}]" - def visit_type_var_tuple(self, t: TypeVarTupleType) -> str: - if t.name is None: - # Anonymous type variable type (only numeric id). - s = f"`{t.id}" - else: - # Named type variable type. - s = f"{t.name}`{t.id}" + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> str: + s = f"{t.name}`{t.id}" if t.has_default(): s += f" = {t.default.accept(self)}" return s - def visit_callable_type(self, t: CallableType) -> str: + def visit_callable_type(self, t: CallableType, /) -> str: param_spec = t.param_spec() if param_spec is not None: num_skip = 2 @@ -3444,22 +3914,21 @@ def visit_callable_type(self, t: CallableType) -> str: return f"def {s}" - def visit_overloaded(self, t: Overloaded) -> str: + def visit_overloaded(self, t: Overloaded, /) -> str: a = [] for i in t.items: a.append(i.accept(self)) return f"Overload({', '.join(a)})" - def visit_tuple_type(self, t: TupleType) -> str: + def visit_tuple_type(self, t: TupleType, /) -> str: s = self.list_str(t.items) or "()" - tuple_name = "tuple" if self.options.use_lowercase_names() else "Tuple" if t.partial_fallback and t.partial_fallback.type: fallback_name = t.partial_fallback.type.fullname if fallback_name != "builtins.tuple": - return f"{tuple_name}[{s}, fallback={t.partial_fallback.accept(self)}]" - return f"{tuple_name}[{s}]" + return f"tuple[{s}, fallback={t.partial_fallback.accept(self)}]" + return f"tuple[{s}]" - def visit_typeddict_type(self, t: TypedDictType) -> str: + def visit_typeddict_type(self, t: TypedDictType, /) -> str: def item_str(name: str, typ: str) -> str: modifier = "" if name not in t.required_keys: @@ -3479,98 +3948,104 @@ def item_str(name: str, typ: str) -> str: prefix = repr(t.fallback.type.fullname) + ", " return f"TypedDict({prefix}{s})" - def visit_raw_expression_type(self, t: RawExpressionType) -> str: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> str: return repr(t.literal_value) - def visit_literal_type(self, t: LiteralType) -> str: + def visit_literal_type(self, t: LiteralType, /) -> str: return f"Literal[{t.value_repr()}]" - def visit_union_type(self, t: UnionType) -> str: - s = self.list_str(t.items) - return f"Union[{s}]" + def visit_union_type(self, t: UnionType, /) -> str: + use_or_syntax = self.options.use_or_syntax() + s = self.list_str(t.items, use_or_syntax=use_or_syntax) + return s if use_or_syntax else f"Union[{s}]" - def visit_partial_type(self, t: PartialType) -> str: + def visit_partial_type(self, t: PartialType, /) -> str: if t.type is None: return "" else: return "".format(t.type.name, ", ".join(["?"] * len(t.type.type_vars))) - def visit_ellipsis_type(self, t: EllipsisType) -> str: + def visit_ellipsis_type(self, t: EllipsisType, /) -> str: return "..." - def visit_type_type(self, t: TypeType) -> str: - if self.options.use_lowercase_names(): - type_name = "type" + def visit_type_type(self, t: TypeType, /) -> str: + if t.is_type_form: + type_name = "TypeForm" else: - type_name = "Type" + type_name = "type" return f"{type_name}[{t.item.accept(self)}]" - def visit_placeholder_type(self, t: PlaceholderType) -> str: + def visit_placeholder_type(self, t: PlaceholderType, /) -> str: return f"" - def visit_type_alias_type(self, t: TypeAliasType) -> str: - if t.alias is not None: - unrolled, recursed = t._partial_expansion() - self.any_as_dots = recursed - type_str = unrolled.accept(self) - self.any_as_dots = False - return type_str - return "" + def visit_type_alias_type(self, t: TypeAliasType, /) -> str: + if t.alias is None: + return "" + if not t.is_recursive: + return get_proper_type(t).accept(self) + if self.dotted_aliases is None: + self.dotted_aliases = set() + elif t in self.dotted_aliases: + return "..." + self.dotted_aliases.add(t) + type_str = get_proper_type(t).accept(self) + self.dotted_aliases.discard(t) + return type_str - def visit_unpack_type(self, t: UnpackType) -> str: + def visit_unpack_type(self, t: UnpackType, /) -> str: return f"Unpack[{t.type.accept(self)}]" - def list_str(self, a: Iterable[Type]) -> str: + def list_str(self, a: Iterable[Type], *, use_or_syntax: bool = False) -> str: """Convert items of an array to strings (pretty-print types) and join the results with commas. """ res = [] for t in a: res.append(t.accept(self)) - return ", ".join(res) + sep = ", " if not use_or_syntax else " | " + return sep.join(res) class TrivialSyntheticTypeTranslator(TypeTranslator, SyntheticTypeVisitor[Type]): """A base class for type translators that need to be run during semantic analysis.""" - def visit_placeholder_type(self, t: PlaceholderType) -> Type: + def visit_placeholder_type(self, t: PlaceholderType, /) -> Type: return t - def visit_callable_argument(self, t: CallableArgument) -> Type: + def visit_callable_argument(self, t: CallableArgument, /) -> Type: return t - def visit_ellipsis_type(self, t: EllipsisType) -> Type: + def visit_ellipsis_type(self, t: EllipsisType, /) -> Type: return t - def visit_raw_expression_type(self, t: RawExpressionType) -> Type: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> Type: return t - def visit_type_list(self, t: TypeList) -> Type: + def visit_type_list(self, t: TypeList, /) -> Type: return t -class UnrollAliasVisitor(TrivialSyntheticTypeTranslator): - 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 - - def visit_type_alias_type(self, t: TypeAliasType) -> Type: - if t in self.initial_aliases: - self.recursed = True - return AnyType(TypeOfAny.special_form) - # Create a new visitor on encountering a new type alias, so that an alias like - # A = Tuple[B, B] - # B = int - # will not be detected as recursive on the second encounter of B. - subvisitor = UnrollAliasVisitor(self.initial_aliases | {t}, self.cache) - result = get_proper_type(t).accept(subvisitor) - if subvisitor.recursed: - self.recursed = True - return result +class CollectAliasesVisitor(TypeQuery[list[mypy.nodes.TypeAlias]]): + def __init__(self) -> None: + super().__init__() + self.seen_alias_nodes: set[mypy.nodes.TypeAlias] = set() + + def strategy(self, items: list[list[mypy.nodes.TypeAlias]]) -> list[mypy.nodes.TypeAlias]: + out = [] + for item in items: + out.extend(item) + return out + + def visit_type_alias_type(self, t: TypeAliasType, /) -> list[mypy.nodes.TypeAlias]: + assert t.alias is not None + if t.alias not in self.seen_alias_nodes: + self.seen_alias_nodes.add(t.alias) + res = [t.alias] + t.alias.target.accept(self) + else: + res = [] + for arg in t.args: + res.extend(arg.accept(self)) + return res def is_named_instance(t: Type, fullnames: str | tuple[str, ...]) -> TypeGuard[Instance]: @@ -3581,24 +4056,9 @@ def is_named_instance(t: Type, fullnames: str | tuple[str, ...]) -> TypeGuard[In return isinstance(t, Instance) and t.type.fullname in fullnames -class LocationSetter(TypeTraverserVisitor): - # TODO: Should we update locations of other Type subclasses? - def __init__(self, line: int, column: int) -> None: - self.line = line - self.column = column - - def visit_instance(self, typ: Instance) -> None: - typ.line = self.line - typ.column = self.column - super().visit_instance(typ) - - def visit_type_alias_type(self, typ: TypeAliasType) -> None: - typ.line = self.line - typ.column = self.column - super().visit_type_alias_type(typ) - - 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 @@ -3690,8 +4150,8 @@ def flatten_nested_unions( flat_items: list[Type] = [] for t in typelist: - if handle_type_alias_type: - if not handle_recursive and isinstance(t, TypeAliasType) and t.is_recursive: + if handle_type_alias_type and isinstance(t, TypeAliasType): + if not handle_recursive and t.is_recursive: tp: Type = t else: tp = get_proper_type(t) @@ -3699,7 +4159,11 @@ def flatten_nested_unions( 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) + flatten_nested_unions( + tp.items, + handle_type_alias_type=handle_type_alias_type, + handle_recursive=handle_recursive, + ) ) else: # Must preserve original aliases when possible. @@ -3721,7 +4185,7 @@ def find_unpack_in_list(items: Sequence[Type]) -> int | None: return unpack_index -def flatten_nested_tuples(types: Sequence[Type]) -> list[Type]: +def flatten_nested_tuples(types: Iterable[Type]) -> list[Type]: """Recursively flatten TupleTypes nested with Unpack. For example this will transform @@ -3738,7 +4202,21 @@ def flatten_nested_tuples(types: Sequence[Type]) -> list[Type]: if not isinstance(p_type, TupleType): res.append(typ) continue - res.extend(flatten_nested_tuples(p_type.items)) + if isinstance(typ.type, TypeAliasType): + items = [] + for item in p_type.items: + if ( + isinstance(item, ProperType) + and isinstance(item, Instance) + or isinstance(item, TypeAliasType) + ): + if len(item.args) == 0: + item = item.copy_modified() + item.set_line(typ) + items.append(item) + else: + items = p_type.items + res.extend(flatten_nested_tuples(items)) return res @@ -3798,6 +4276,149 @@ def type_vars_as_args(type_vars: Sequence[TypeVarLikeType]) -> tuple[Type, ...]: return tuple(args) +# See docstring for mypy/cache.py for reserved tag ranges. +# Instance-related tags. +INSTANCE: Final[Tag] = 80 +INSTANCE_SIMPLE: Final[Tag] = 81 +INSTANCE_GENERIC: Final[Tag] = 82 +INSTANCE_STR: Final[Tag] = 83 +INSTANCE_FUNCTION: Final[Tag] = 84 +INSTANCE_INT: Final[Tag] = 85 +INSTANCE_BOOL: Final[Tag] = 86 +INSTANCE_OBJECT: Final[Tag] = 87 + +# Other type tags. +TYPE_ALIAS_TYPE: Final[Tag] = 100 +TYPE_VAR_TYPE: Final[Tag] = 101 +PARAM_SPEC_TYPE: Final[Tag] = 102 +TYPE_VAR_TUPLE_TYPE: Final[Tag] = 103 +UNBOUND_TYPE: Final[Tag] = 104 +UNPACK_TYPE: Final[Tag] = 105 +ANY_TYPE: Final[Tag] = 106 +UNINHABITED_TYPE: Final[Tag] = 107 +NONE_TYPE: Final[Tag] = 108 +DELETED_TYPE: Final[Tag] = 109 +CALLABLE_TYPE: Final[Tag] = 110 +OVERLOADED: Final[Tag] = 111 +TUPLE_TYPE: Final[Tag] = 112 +TYPED_DICT_TYPE: Final[Tag] = 113 +LITERAL_TYPE: Final[Tag] = 114 +UNION_TYPE: Final[Tag] = 115 +TYPE_TYPE: Final[Tag] = 116 +PARAMETERS: Final[Tag] = 117 + + +def read_type(data: ReadBuffer, tag: Tag | None = None) -> Type: + if tag is None: + tag = read_tag(data) + # The branches here are ordered manually by type "popularity". + if tag == INSTANCE: + return Instance.read(data) + if tag == ANY_TYPE: + return AnyType.read(data) + if tag == TYPE_VAR_TYPE: + return TypeVarType.read(data) + if tag == CALLABLE_TYPE: + return CallableType.read(data) + if tag == NONE_TYPE: + return NoneType.read(data) + if tag == UNION_TYPE: + return UnionType.read(data) + if tag == LITERAL_TYPE: + return LiteralType.read(data) + if tag == TYPE_ALIAS_TYPE: + return TypeAliasType.read(data) + if tag == TUPLE_TYPE: + return TupleType.read(data) + if tag == TYPED_DICT_TYPE: + return TypedDictType.read(data) + if tag == TYPE_TYPE: + return TypeType.read(data) + if tag == OVERLOADED: + return Overloaded.read(data) + if tag == PARAM_SPEC_TYPE: + return ParamSpecType.read(data) + if tag == TYPE_VAR_TUPLE_TYPE: + return TypeVarTupleType.read(data) + if tag == UNPACK_TYPE: + return UnpackType.read(data) + if tag == PARAMETERS: + return Parameters.read(data) + if tag == UNINHABITED_TYPE: + return UninhabitedType.read(data) + if tag == UNBOUND_TYPE: + return UnboundType.read(data) + if tag == DELETED_TYPE: + return DeletedType.read(data) + assert False, f"Unknown type tag {tag}" + + +def read_function_like(data: ReadBuffer, tag: Tag) -> FunctionLike: + if tag == CALLABLE_TYPE: + return CallableType.read(data) + if tag == OVERLOADED: + return Overloaded.read(data) + assert False, f"Invalid type tag for FunctionLike {tag}" + + +def read_type_var_likes(data: ReadBuffer) -> list[TypeVarLikeType]: + """Specialized version of read_type_list() for lists of type variables.""" + assert read_tag(data) == LIST_GEN + ret: list[TypeVarLikeType] = [] + for _ in range(read_int_bare(data)): + tag = read_tag(data) + if tag == TYPE_VAR_TYPE: + ret.append(TypeVarType.read(data)) + elif tag == PARAM_SPEC_TYPE: + ret.append(ParamSpecType.read(data)) + elif tag == TYPE_VAR_TUPLE_TYPE: + ret.append(TypeVarTupleType.read(data)) + else: + assert False, f"Invalid type tag for TypeVarLikeType {tag}" + return ret + + +def read_type_opt(data: ReadBuffer) -> Type | None: + tag = read_tag(data) + if tag == LITERAL_NONE: + return None + return read_type(data, tag) + + +def write_type_opt(data: WriteBuffer, value: Type | None) -> None: + if value is not None: + value.write(data) + else: + write_tag(data, LITERAL_NONE) + + +def read_type_list(data: ReadBuffer) -> list[Type]: + assert read_tag(data) == LIST_GEN + size = read_int_bare(data) + return [read_type(data) for _ in range(size)] + + +def write_type_list(data: WriteBuffer, value: Sequence[Type]) -> None: + write_tag(data, LIST_GEN) + write_int_bare(data, len(value)) + for item in value: + item.write(data) + + +def read_type_map(data: ReadBuffer) -> dict[str, Type]: + assert read_tag(data) == DICT_STR_GEN + size = read_int_bare(data) + return {read_str_bare(data): read_type(data) for _ in range(size)} + + +def write_type_map(data: WriteBuffer, value: dict[str, Type]) -> None: + write_tag(data, DICT_STR_GEN) + write_int_bare(data, len(value)) + for key in sorted(value): + write_str_bare(data, key) + value[key].write(data) + + # This cyclic import is unfortunate, but to avoid it we would need to move away all uses # of get_proper_type() from types.py. Majority of them have been removed, but few remaining # are quite tricky to get rid of, but ultimately we want to do it at some point. diff --git a/mypy/types_utils.py b/mypy/types_utils.py index 1cd56eae5835c..124d024e8c1e1 100644 --- a/mypy/types_utils.py +++ b/mypy/types_utils.py @@ -8,13 +8,15 @@ from __future__ import annotations -from typing import Callable, Iterable, cast +from collections.abc import Iterable +from typing import Callable, cast from mypy.nodes import ARG_STAR, ARG_STAR2, FuncItem, TypeAlias from mypy.types import ( AnyType, CallableType, Instance, + LiteralType, NoneType, Overloaded, ParamSpecType, @@ -75,21 +77,33 @@ def is_invalid_recursive_alias(seen_nodes: set[TypeAlias], target: Type) -> bool return False -def is_bad_type_type_item(item: Type) -> bool: +def get_bad_type_type_item(item: Type) -> str | None: """Prohibit types like Type[Type[...]]. Such types are explicitly prohibited by PEP 484. Also, they cause problems with recursive types like T = Type[T], because internal representation of TypeType item is normalized (i.e. always a proper type). + + Also forbids `Type[Literal[...]]`, because typing spec does not allow it. """ + # TODO: what else cannot be present in `type[...]`? item = get_proper_type(item) if isinstance(item, TypeType): - return True + return "Type[...]" + if isinstance(item, LiteralType): + return "Literal[...]" if isinstance(item, UnionType): - return any( - isinstance(get_proper_type(i), TypeType) for i in flatten_nested_unions(item.items) - ) - return False + items = [ + bad_item + for typ in flatten_nested_unions(item.items) + if (bad_item := get_bad_type_type_item(typ)) is not None + ] + if not items: + return None + if len(items) == 1: + return items[0] + return f"Union[{', '.join(items)}]" + return None def is_union_with_any(tp: Type) -> bool: diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index dfed62f694fc9..6fcf0161790d6 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -20,37 +20,53 @@ __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- +_compression: 3.5-3.13 +_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-3.12 +_multibytecodec: 3.0- _operator: 3.4- _osx_support: 3.0- +_pickle: 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- @@ -60,8 +76,10 @@ _warnings: 3.0- _weakref: 3.0- _weakrefset: 3.0- _winapi: 3.3- +_zstd: 3.14- abc: 3.0- aifc: 3.0-3.12 +annotationlib: 3.14- antigravity: 3.0- argparse: 3.0- array: 3.0- @@ -70,12 +88,14 @@ asynchat: 3.0-3.11 asyncio: 3.4- asyncio.exceptions: 3.8- asyncio.format_helpers: 3.7- +asyncio.graph: 3.14- asyncio.mixins: 3.10- asyncio.runners: 3.7- asyncio.staggered: 3.8- asyncio.taskgroups: 3.11- asyncio.threads: 3.9- asyncio.timeouts: 3.11- +asyncio.tools: 3.14- asyncio.trsock: 3.8- asyncore: 3.0-3.11 atexit: 3.0- @@ -101,7 +121,10 @@ collections: 3.0- collections.abc: 3.3- colorsys: 3.0- compileall: 3.0- +compression: 3.14- concurrent: 3.2- +concurrent.futures.interpreter: 3.14- +concurrent.interpreters: 3.14- configparser: 3.0- contextlib: 3.0- contextvars: 3.7- @@ -122,9 +145,13 @@ distutils: 3.0-3.11 distutils.command.bdist_msi: 3.0-3.10 distutils.command.bdist_wininst: 3.0-3.9 doctest: 3.0- -dummy_threading: 3.0-3.8 email: 3.0- encodings: 3.0- +encodings.cp1125: 3.4- +encodings.cp273: 3.4- +encodings.cp858: 3.2- +encodings.koi8_t: 3.5- +encodings.kz1048: 3.5- ensurepip: 3.0- enum: 3.4- errno: 3.0- @@ -156,6 +183,8 @@ 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- @@ -204,6 +233,7 @@ os: 3.0- ossaudiodev: 3.0-3.12 parser: 3.0-3.9 pathlib: 3.4- +pathlib.types: 3.14- pdb: 3.0- pickle: 3.0- pickletools: 3.0- @@ -256,6 +286,7 @@ ssl: 3.0- stat: 3.0- statistics: 3.4- string: 3.0- +string.templatelib: 3.14- stringprep: 3.0- struct: 3.0- subprocess: 3.0- diff --git a/mypy/typeshed/stdlib/__main__.pyi b/mypy/typeshed/stdlib/__main__.pyi index e27843e533821..5b0f74feb261b 100644 --- a/mypy/typeshed/stdlib/__main__.pyi +++ b/mypy/typeshed/stdlib/__main__.pyi @@ -1,3 +1 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... +def __getattr__(name: str): ... # incomplete module diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index 1dbceac428c1e..d8d5a1829991e 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,1676 +1,145 @@ import sys -import typing_extensions -from typing import Any, ClassVar, Generic, Literal, TypedDict, overload -from typing_extensions import Self, 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 Final -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]] - - if sys.version_info >= (3, 14): - def __replace__(self) -> Self: ... - -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: ... - - if sys.version_info >= (3, 14): - def __replace__(self, *, lineno: int = ..., tag: str = ...) -> 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 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 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: ... - -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: ... - -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, 12): + from ast import ( + ParamSpec as ParamSpec, + TypeAlias as TypeAlias, + 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: ... - - 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 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 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 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: ... - -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, 14): - def __replace__( - self, - *, - lower: expr | None = ..., - upper: expr | None = ..., - step: expr | None = ..., - **kwargs: Unpack[_SliceAttributes], - ) -> Self: ... - -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: ... - - 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 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: ... - - 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: ... + 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: ... - - if sys.version_info >= (3, 14): - def __replace__( - self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes] - ) -> 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 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 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: ... + from ast import ( + Match as Match, + 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, + ) + +PyCF_ALLOW_TOP_LEVEL_AWAIT: Final = 8192 +PyCF_ONLY_AST: Final = 1024 +PyCF_TYPE_COMMENTS: Final = 4096 - if sys.version_info >= (3, 14): - def __replace__(self, *, 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 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 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: ... - -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 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: ... +if sys.version_info >= (3, 13): + PyCF_OPTIMIZED_AST: Final = 33792 diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi new file mode 100644 index 0000000000000..f43178e4d7258 --- /dev/null +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -0,0 +1,112 @@ +import sys +from asyncio.events import AbstractEventLoop +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable +from contextvars import Context +from types import FrameType, GenericAlias +from typing import Any, Literal, TextIO, TypeVar +from typing_extensions import Self, TypeAlias, disjoint_base + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_TaskYieldType: TypeAlias = Future[object] | None + +@disjoint_base +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) -> 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: ... + def cancel(self, msg: Any | None = None) -> 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: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +if sys.version_info >= (3, 12): + _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co] +else: + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _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. +@disjoint_base +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 | None = None, + name: str | None = 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 | None = None, + name: str | None = None, + context: Context | None = None, + ) -> None: ... + else: + def __init__( + self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop | None = None, name: str | None = 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: ... + + 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: ... + +if sys.version_info >= (3, 14): + def future_discard_from_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ... + def future_add_to_awaited_by(future: Future[Any], waiter: Future[Any], /) -> None: ... + def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... diff --git a/mypy/typeshed/stdlib/_blake2.pyi b/mypy/typeshed/stdlib/_blake2.pyi new file mode 100644 index 0000000000000..a6c3869fb8513 --- /dev/null +++ b/mypy/typeshed/stdlib/_blake2.pyi @@ -0,0 +1,119 @@ +import sys +from _typeshed import ReadableBuffer +from typing import ClassVar, Final, final +from typing_extensions import Self + +BLAKE2B_MAX_DIGEST_SIZE: Final = 64 +BLAKE2B_MAX_KEY_SIZE: Final = 64 +BLAKE2B_PERSON_SIZE: Final = 16 +BLAKE2B_SALT_SIZE: Final = 16 +BLAKE2S_MAX_DIGEST_SIZE: Final = 32 +BLAKE2S_MAX_KEY_SIZE: Final = 32 +BLAKE2S_PERSON_SIZE: Final = 8 +BLAKE2S_SALT_SIZE: Final = 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, 13): + def __new__( + cls, + 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, + string: ReadableBuffer | None = None, + ) -> Self: ... + else: + def __new__( + cls, + 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, + ) -> Self: ... + + 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, 13): + def __new__( + cls, + 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, + string: ReadableBuffer | None = None, + ) -> Self: ... + else: + def __new__( + cls, + 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, + ) -> Self: ... + + 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..fdad932ca22e6 --- /dev/null +++ b/mypy/typeshed/stdlib/_bz2.pyi @@ -0,0 +1,24 @@ +import sys +from _typeshed import ReadableBuffer +from typing import final +from typing_extensions import Self + +@final +class BZ2Compressor: + if sys.version_info >= (3, 12): + def __new__(cls, compresslevel: int = 9, /) -> Self: ... + else: + 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..89f97edb9ba81 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: ... @@ -78,26 +81,12 @@ def escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> t def escape_encode(data: bytes, errors: str | None = None, /) -> tuple[bytes, int]: ... def latin_1_decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... def latin_1_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... - -if sys.version_info >= (3, 9): - def raw_unicode_escape_decode( - data: str | ReadableBuffer, errors: str | None = None, final: bool = True, / - ) -> tuple[str, int]: ... - -else: - def raw_unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... - +def raw_unicode_escape_decode( + data: str | ReadableBuffer, errors: str | None = None, final: bool = True, / +) -> tuple[str, int]: ... def raw_unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... def readbuffer_encode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[bytes, int]: ... - -if sys.version_info >= (3, 9): - def unicode_escape_decode( - data: str | ReadableBuffer, errors: str | None = None, final: bool = True, / - ) -> tuple[str, int]: ... - -else: - def unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... - +def unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ... def unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... def utf_16_be_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... def utf_16_be_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index 8b1ac9c7eb8bb..319577c9284bc 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -1,13 +1,15 @@ import sys from abc import abstractmethod from types import MappingProxyType -from typing import ( # noqa: Y022,Y038 +from typing import ( # noqa: Y022,Y038,UP035,Y057 AbstractSet as Set, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, AsyncIterator as AsyncIterator, Awaitable as Awaitable, + ByteString as ByteString, Callable as Callable, + ClassVar, Collection as Collection, Container as Container, Coroutine as Coroutine, @@ -58,12 +60,8 @@ __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"] @@ -73,6 +71,8 @@ _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]: ... + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 13): def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ... if sys.version_info >= (3, 10): @@ -81,6 +81,7 @@ class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented @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]: ... @@ -88,6 +89,8 @@ 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]]: ... + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 13): def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ... if sys.version_info >= (3, 10): @@ -97,5 +100,6 @@ class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented if sys.version_info >= (3, 12): @runtime_checkable class Buffer(Protocol): + __slots__ = () @abstractmethod def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/mypy/typeshed/stdlib/_compat_pickle.pyi b/mypy/typeshed/stdlib/_compat_pickle.pyi index 50fb22442cc92..32c0b542d9913 100644 --- a/mypy/typeshed/stdlib/_compat_pickle.pyi +++ b/mypy/typeshed/stdlib/_compat_pickle.pyi @@ -1,8 +1,10 @@ -IMPORT_MAPPING: dict[str, str] -NAME_MAPPING: dict[tuple[str, str], tuple[str, str]] -PYTHON2_EXCEPTIONS: tuple[str, ...] -MULTIPROCESSING_EXCEPTIONS: tuple[str, ...] -REVERSE_IMPORT_MAPPING: dict[str, str] -REVERSE_NAME_MAPPING: dict[tuple[str, str], tuple[str, str]] -PYTHON3_OSERROR_EXCEPTIONS: tuple[str, ...] -PYTHON3_IMPORTERROR_EXCEPTIONS: tuple[str, ...] +from typing import Final + +IMPORT_MAPPING: Final[dict[str, str]] +NAME_MAPPING: Final[dict[tuple[str, str], tuple[str, str]]] +PYTHON2_EXCEPTIONS: Final[tuple[str, ...]] +MULTIPROCESSING_EXCEPTIONS: Final[tuple[str, ...]] +REVERSE_IMPORT_MAPPING: Final[dict[str, str]] +REVERSE_NAME_MAPPING: Final[dict[tuple[str, str], tuple[str, str]]] +PYTHON3_OSERROR_EXCEPTIONS: Final[tuple[str, ...]] +PYTHON3_IMPORTERROR_EXCEPTIONS: Final[tuple[str, ...]] diff --git a/mypy/typeshed/stdlib/_compression.pyi b/mypy/typeshed/stdlib/_compression.pyi index a41a8142cc3ac..6015bcb13f1cc 100644 --- a/mypy/typeshed/stdlib/_compression.pyi +++ b/mypy/typeshed/stdlib/_compression.pyi @@ -1,24 +1,38 @@ -from _typeshed import WriteableBuffer +# _compression is replaced by compression._common._streams on Python 3.14+ (PEP-784) + +from _typeshed import ReadableBuffer, WriteableBuffer from collections.abc import Callable from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase -from typing import Any, Protocol +from typing import Any, Protocol, type_check_only BUFFER_SIZE = DEFAULT_BUFFER_SIZE +@type_check_only class _Reader(Protocol): def read(self, n: int, /) -> bytes: ... def seekable(self) -> bool: ... def seek(self, n: int, /) -> Any: ... +@type_check_only +class _Decompressor(Protocol): + def decompress(self, data: ReadableBuffer, /, max_length: int = ...) -> bytes: ... + @property + def unused_data(self) -> bytes: ... + @property + def eof(self) -> bool: ... + # `zlib._Decompress` does not have next property, but `DecompressReader` calls it: + # @property + # def needs_input(self) -> bool: ... + class BaseStream(BufferedIOBase): ... class DecompressReader(RawIOBase): def __init__( self, fp: _Reader, - decomp_factory: Callable[..., object], + decomp_factory: Callable[..., _Decompressor], trailing_error: type[Exception] | tuple[type[Exception], ...] = (), - **decomp_args: Any, + **decomp_args: Any, # These are passed to decomp_factory. ) -> None: ... def readinto(self, b: WriteableBuffer) -> int: ... def read(self, size: int = -1) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_contextvars.pyi b/mypy/typeshed/stdlib/_contextvars.pyi new file mode 100644 index 0000000000000..0ddeca7882cd1 --- /dev/null +++ b/mypy/typeshed/stdlib/_contextvars.pyi @@ -0,0 +1,64 @@ +import sys +from collections.abc import Callable, Iterator, Mapping +from types import GenericAlias, TracebackType +from typing import Any, ClassVar, Generic, TypeVar, final, overload +from typing_extensions import ParamSpec, Self + +_T = TypeVar("_T") +_D = TypeVar("_D") +_P = ParamSpec("_P") + +@final +class ContextVar(Generic[_T]): + @overload + def __new__(cls, name: str) -> Self: ... + @overload + def __new__(cls, name: str, *, default: _T) -> Self: ... + 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: ... + 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] + __hash__: ClassVar[None] # type: ignore[assignment] + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + if sys.version_info >= (3, 14): + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / + ) -> None: ... + +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: ... + __hash__: ClassVar[None] # type: ignore[assignment] + 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 9bb5d27f6e352..ea90766afee66 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,8 +1,9 @@ +import csv import sys from _typeshed import SupportsWrite -from collections.abc import Iterable, Iterator -from typing import Any, Final -from typing_extensions import TypeAlias +from collections.abc import Iterable +from typing import Any, Final, Literal, type_check_only +from typing_extensions import Self, TypeAlias, disjoint_base __version__: Final[str] @@ -14,12 +15,16 @@ if sys.version_info >= (3, 12): 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) -_QuotingType: TypeAlias = int +if sys.version_info >= (3, 12): + _QuotingType: TypeAlias = Literal[0, 1, 2, 3, 4, 5] +else: + _QuotingType: TypeAlias = Literal[0, 1, 2, 3] class Error(Exception): ... +_DialectLike: TypeAlias = str | Dialect | csv.Dialect | type[Dialect | csv.Dialect] + +@disjoint_base class Dialect: delimiter: str quotechar: str | None @@ -29,24 +34,66 @@ class Dialect: lineterminator: str quoting: _QuotingType strict: bool - def __init__(self) -> None: ... + def __new__( + cls, + dialect: _DialectLike | None = 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, + ) -> Self: ... + +if sys.version_info >= (3, 10): + # This class calls itself _csv.reader. + @disjoint_base + 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. + @disjoint_base + 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], + fileobj: SupportsWrite[str], + /, dialect: _DialectLike = "excel", *, delimiter: str = ",", @@ -59,7 +106,8 @@ def writer( strict: bool = False, ) -> _writer: ... def reader( - csvfile: Iterable[str], + iterable: Iterable[str], + /, dialect: _DialectLike = "excel", *, delimiter: str = ",", @@ -73,7 +121,8 @@ def reader( ) -> _reader: ... def register_dialect( name: str, - dialect: type[Dialect] = ..., + /, + dialect: type[Dialect | csv.Dialect] | str = "excel", *, delimiter: str = ",", quotechar: str | None = '"', diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index 0fe7521d77492..082a31f705622 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -1,29 +1,28 @@ +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 types import GenericAlias +from typing import Any, ClassVar, Final, Generic, TypeVar, final, overload, type_check_only from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 9): - from types import GenericAlias - _T = TypeVar("_T") _CT = TypeVar("_CT", bound=_CData) -FUNCFLAG_CDECL: int -FUNCFLAG_PYTHONAPI: int -FUNCFLAG_USE_ERRNO: int -FUNCFLAG_USE_LASTERROR: int -RTLD_GLOBAL: int -RTLD_LOCAL: int +FUNCFLAG_CDECL: Final = 0x1 +FUNCFLAG_PYTHONAPI: Final = 0x4 +FUNCFLAG_USE_ERRNO: Final = 0x8 +FUNCFLAG_USE_LASTERROR: Final = 0x10 +RTLD_GLOBAL: Final[int] +RTLD_LOCAL: Final[int] if sys.version_info >= (3, 11): - CTYPES_MAX_ARGCOUNT: int + CTYPES_MAX_ARGCOUNT: Final[int] if sys.version_info >= (3, 12): - SIZEOF_TIME_T: int + SIZEOF_TIME_T: Final[int] if sys.platform == "win32": # Description, Source, HelpFile, HelpContext, scode @@ -38,8 +37,8 @@ if sys.platform == "win32": def CopyComPointer(src: _PointerLike, dst: _PointerLike | _CArgObject) -> int: ... - FUNCFLAG_HRESULT: int - FUNCFLAG_STDCALL: int + FUNCFLAG_HRESULT: Final = 0x2 + FUNCFLAG_STDCALL: Final = 0x0 def FormatError(code: int = ...) -> str: ... def get_last_error() -> int: ... @@ -47,46 +46,84 @@ 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] # pyright: ignore[reportGeneralTypeIssues] - def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] +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: ... + +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 -class _CData(metaclass=_CDataMeta): +# 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, value: 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: ... + if sys.version_info >= (3, 14): + __pointer_type__: type -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] +@type_check_only class _CanCastTo(_CData): ... + +@type_check_only 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: _CTypeBaseType, /) -> 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 @@ -99,65 +136,154 @@ 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(obj: _CT, /) -> _Pointer[_CT]: ... +if sys.version_info < (3, 14): + @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: ... +if sys.version_info >= (3, 14): + def byref(obj: _CData | _CDataType, offset: int = 0, /) -> _CArgObject: ... + +else: + def byref(obj: _CData | _CDataType, offset: int = 0) -> _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] @overload - def __init__(self) -> None: ... + def __new__(cls) -> Self: ... @overload - def __init__(self, address: int, /) -> None: ... + def __new__(cls, address: int, /) -> Self: ... @overload - def __init__(self, callable: Callable[..., Any], /) -> None: ... + def __new__(cls, callable: Callable[..., Any], /) -> Self: ... @overload - def __init__(self, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> None: ... + def __new__(cls, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> Self: ... if sys.platform == "win32": @overload - def __init__( - self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., / - ) -> None: ... + def __new__( + cls, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., / + ) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... _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: ... @@ -170,7 +296,7 @@ class Array(_CData, Generic[_CT]): def _type_(self, value: type[_CT]) -> None: ... raw: bytes # Note: only available if _CT == c_char value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise - # TODO These methods cannot be annotated correctly at the moment. + # TODO: These methods cannot be annotated correctly at the moment. # All of these "Any"s stand for the array's element type, but it's not possible to use _CT # here, because of a special feature of ctypes. # By default, when accessing an element of an Array[_CT], the returned object has type _CT. @@ -195,12 +321,17 @@ class Array(_CData, Generic[_CT]): # Can't inherit from Sized because the metaclass conflict between # Sized and _CData prevents using _CDataMeta. def __len__(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __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 resize(obj: _CData | _CDataType, size: int, /) -> None: ... def set_errno(value: int, /) -> int: ... -def sizeof(obj_or_type: _CData | type[_CData], /) -> 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 b68c8925a041c..d4e4d48f4e20f 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 _typeshed import ReadOnlyBuffer, SupportsRead, SupportsWrite +from curses import _ncurses_version +from typing import Any, Final, final, overload from typing_extensions import TypeAlias # NOTE: This module is ordinarily only available on Unix, but the windows-curses @@ -10,269 +11,270 @@ from typing_extensions import TypeAlias _ChType: TypeAlias = str | bytes | int # ACS codes are only initialized after initscr is called -ACS_BBSS: int -ACS_BLOCK: int -ACS_BOARD: int -ACS_BSBS: int -ACS_BSSB: int -ACS_BSSS: int -ACS_BTEE: int -ACS_BULLET: int -ACS_CKBOARD: int -ACS_DARROW: int -ACS_DEGREE: int -ACS_DIAMOND: int -ACS_GEQUAL: int -ACS_HLINE: int -ACS_LANTERN: int -ACS_LARROW: int -ACS_LEQUAL: int -ACS_LLCORNER: int -ACS_LRCORNER: int -ACS_LTEE: int -ACS_NEQUAL: int -ACS_PI: int -ACS_PLMINUS: int -ACS_PLUS: int -ACS_RARROW: int -ACS_RTEE: int -ACS_S1: int -ACS_S3: int -ACS_S7: int -ACS_S9: int -ACS_SBBS: int -ACS_SBSB: int -ACS_SBSS: int -ACS_SSBB: int -ACS_SSBS: int -ACS_SSSB: int -ACS_SSSS: int -ACS_STERLING: int -ACS_TTEE: int -ACS_UARROW: int -ACS_ULCORNER: int -ACS_URCORNER: int -ACS_VLINE: int -ALL_MOUSE_EVENTS: int -A_ALTCHARSET: int -A_ATTRIBUTES: int -A_BLINK: int -A_BOLD: int -A_CHARTEXT: int -A_COLOR: int -A_DIM: int -A_HORIZONTAL: int -A_INVIS: int -A_ITALIC: int -A_LEFT: int -A_LOW: int -A_NORMAL: int -A_PROTECT: int -A_REVERSE: int -A_RIGHT: int -A_STANDOUT: int -A_TOP: int -A_UNDERLINE: int -A_VERTICAL: int -BUTTON1_CLICKED: int -BUTTON1_DOUBLE_CLICKED: int -BUTTON1_PRESSED: int -BUTTON1_RELEASED: int -BUTTON1_TRIPLE_CLICKED: int -BUTTON2_CLICKED: int -BUTTON2_DOUBLE_CLICKED: int -BUTTON2_PRESSED: int -BUTTON2_RELEASED: int -BUTTON2_TRIPLE_CLICKED: int -BUTTON3_CLICKED: int -BUTTON3_DOUBLE_CLICKED: int -BUTTON3_PRESSED: int -BUTTON3_RELEASED: int -BUTTON3_TRIPLE_CLICKED: int -BUTTON4_CLICKED: int -BUTTON4_DOUBLE_CLICKED: int -BUTTON4_PRESSED: int -BUTTON4_RELEASED: int -BUTTON4_TRIPLE_CLICKED: int -# Darwin ncurses doesn't provide BUTTON5_* constants -if sys.version_info >= (3, 10) and sys.platform != "darwin": - BUTTON5_PRESSED: int - BUTTON5_RELEASED: int - BUTTON5_CLICKED: int - BUTTON5_DOUBLE_CLICKED: int - BUTTON5_TRIPLE_CLICKED: int -BUTTON_ALT: int -BUTTON_CTRL: int -BUTTON_SHIFT: int -COLOR_BLACK: int -COLOR_BLUE: int -COLOR_CYAN: int -COLOR_GREEN: int -COLOR_MAGENTA: int -COLOR_RED: int -COLOR_WHITE: int -COLOR_YELLOW: int -ERR: int -KEY_A1: int -KEY_A3: int -KEY_B2: int -KEY_BACKSPACE: int -KEY_BEG: int -KEY_BREAK: int -KEY_BTAB: int -KEY_C1: int -KEY_C3: int -KEY_CANCEL: int -KEY_CATAB: int -KEY_CLEAR: int -KEY_CLOSE: int -KEY_COMMAND: int -KEY_COPY: int -KEY_CREATE: int -KEY_CTAB: int -KEY_DC: int -KEY_DL: int -KEY_DOWN: int -KEY_EIC: int -KEY_END: int -KEY_ENTER: int -KEY_EOL: int -KEY_EOS: int -KEY_EXIT: int -KEY_F0: int -KEY_F1: int -KEY_F10: int -KEY_F11: int -KEY_F12: int -KEY_F13: int -KEY_F14: int -KEY_F15: int -KEY_F16: int -KEY_F17: int -KEY_F18: int -KEY_F19: int -KEY_F2: int -KEY_F20: int -KEY_F21: int -KEY_F22: int -KEY_F23: int -KEY_F24: int -KEY_F25: int -KEY_F26: int -KEY_F27: int -KEY_F28: int -KEY_F29: int -KEY_F3: int -KEY_F30: int -KEY_F31: int -KEY_F32: int -KEY_F33: int -KEY_F34: int -KEY_F35: int -KEY_F36: int -KEY_F37: int -KEY_F38: int -KEY_F39: int -KEY_F4: int -KEY_F40: int -KEY_F41: int -KEY_F42: int -KEY_F43: int -KEY_F44: int -KEY_F45: int -KEY_F46: int -KEY_F47: int -KEY_F48: int -KEY_F49: int -KEY_F5: int -KEY_F50: int -KEY_F51: int -KEY_F52: int -KEY_F53: int -KEY_F54: int -KEY_F55: int -KEY_F56: int -KEY_F57: int -KEY_F58: int -KEY_F59: int -KEY_F6: int -KEY_F60: int -KEY_F61: int -KEY_F62: int -KEY_F63: int -KEY_F7: int -KEY_F8: int -KEY_F9: int -KEY_FIND: int -KEY_HELP: int -KEY_HOME: int -KEY_IC: int -KEY_IL: int -KEY_LEFT: int -KEY_LL: int -KEY_MARK: int -KEY_MAX: int -KEY_MESSAGE: int -KEY_MIN: int -KEY_MOUSE: int -KEY_MOVE: int -KEY_NEXT: int -KEY_NPAGE: int -KEY_OPEN: int -KEY_OPTIONS: int -KEY_PPAGE: int -KEY_PREVIOUS: int -KEY_PRINT: int -KEY_REDO: int -KEY_REFERENCE: int -KEY_REFRESH: int -KEY_REPLACE: int -KEY_RESET: int -KEY_RESIZE: int -KEY_RESTART: int -KEY_RESUME: int -KEY_RIGHT: int -KEY_SAVE: int -KEY_SBEG: int -KEY_SCANCEL: int -KEY_SCOMMAND: int -KEY_SCOPY: int -KEY_SCREATE: int -KEY_SDC: int -KEY_SDL: int -KEY_SELECT: int -KEY_SEND: int -KEY_SEOL: int -KEY_SEXIT: int -KEY_SF: int -KEY_SFIND: int -KEY_SHELP: int -KEY_SHOME: int -KEY_SIC: int -KEY_SLEFT: int -KEY_SMESSAGE: int -KEY_SMOVE: int -KEY_SNEXT: int -KEY_SOPTIONS: int -KEY_SPREVIOUS: int -KEY_SPRINT: int -KEY_SR: int -KEY_SREDO: int -KEY_SREPLACE: int -KEY_SRESET: int -KEY_SRIGHT: int -KEY_SRSUME: int -KEY_SSAVE: int -KEY_SSUSPEND: int -KEY_STAB: int -KEY_SUNDO: int -KEY_SUSPEND: int -KEY_UNDO: int -KEY_UP: int -OK: int -REPORT_MOUSE_POSITION: int +ACS_BBSS: Final[int] +ACS_BLOCK: Final[int] +ACS_BOARD: Final[int] +ACS_BSBS: Final[int] +ACS_BSSB: Final[int] +ACS_BSSS: Final[int] +ACS_BTEE: Final[int] +ACS_BULLET: Final[int] +ACS_CKBOARD: Final[int] +ACS_DARROW: Final[int] +ACS_DEGREE: Final[int] +ACS_DIAMOND: Final[int] +ACS_GEQUAL: Final[int] +ACS_HLINE: Final[int] +ACS_LANTERN: Final[int] +ACS_LARROW: Final[int] +ACS_LEQUAL: Final[int] +ACS_LLCORNER: Final[int] +ACS_LRCORNER: Final[int] +ACS_LTEE: Final[int] +ACS_NEQUAL: Final[int] +ACS_PI: Final[int] +ACS_PLMINUS: Final[int] +ACS_PLUS: Final[int] +ACS_RARROW: Final[int] +ACS_RTEE: Final[int] +ACS_S1: Final[int] +ACS_S3: Final[int] +ACS_S7: Final[int] +ACS_S9: Final[int] +ACS_SBBS: Final[int] +ACS_SBSB: Final[int] +ACS_SBSS: Final[int] +ACS_SSBB: Final[int] +ACS_SSBS: Final[int] +ACS_SSSB: Final[int] +ACS_SSSS: Final[int] +ACS_STERLING: Final[int] +ACS_TTEE: Final[int] +ACS_UARROW: Final[int] +ACS_ULCORNER: Final[int] +ACS_URCORNER: Final[int] +ACS_VLINE: Final[int] +ALL_MOUSE_EVENTS: Final[int] +A_ALTCHARSET: Final[int] +A_ATTRIBUTES: Final[int] +A_BLINK: Final[int] +A_BOLD: Final[int] +A_CHARTEXT: Final[int] +A_COLOR: Final[int] +A_DIM: Final[int] +A_HORIZONTAL: Final[int] +A_INVIS: Final[int] +A_ITALIC: Final[int] +A_LEFT: Final[int] +A_LOW: Final[int] +A_NORMAL: Final[int] +A_PROTECT: Final[int] +A_REVERSE: Final[int] +A_RIGHT: Final[int] +A_STANDOUT: Final[int] +A_TOP: Final[int] +A_UNDERLINE: Final[int] +A_VERTICAL: Final[int] +BUTTON1_CLICKED: Final[int] +BUTTON1_DOUBLE_CLICKED: Final[int] +BUTTON1_PRESSED: Final[int] +BUTTON1_RELEASED: Final[int] +BUTTON1_TRIPLE_CLICKED: Final[int] +BUTTON2_CLICKED: Final[int] +BUTTON2_DOUBLE_CLICKED: Final[int] +BUTTON2_PRESSED: Final[int] +BUTTON2_RELEASED: Final[int] +BUTTON2_TRIPLE_CLICKED: Final[int] +BUTTON3_CLICKED: Final[int] +BUTTON3_DOUBLE_CLICKED: Final[int] +BUTTON3_PRESSED: Final[int] +BUTTON3_RELEASED: Final[int] +BUTTON3_TRIPLE_CLICKED: Final[int] +BUTTON4_CLICKED: Final[int] +BUTTON4_DOUBLE_CLICKED: Final[int] +BUTTON4_PRESSED: Final[int] +BUTTON4_RELEASED: Final[int] +BUTTON4_TRIPLE_CLICKED: Final[int] +# Darwin ncurses doesn't provide BUTTON5_* constants prior to 3.12.10 and 3.13.3 +if sys.version_info >= (3, 10): + if sys.version_info >= (3, 12) or sys.platform != "darwin": + BUTTON5_PRESSED: Final[int] + BUTTON5_RELEASED: Final[int] + BUTTON5_CLICKED: Final[int] + BUTTON5_DOUBLE_CLICKED: Final[int] + BUTTON5_TRIPLE_CLICKED: Final[int] +BUTTON_ALT: Final[int] +BUTTON_CTRL: Final[int] +BUTTON_SHIFT: Final[int] +COLOR_BLACK: Final[int] +COLOR_BLUE: Final[int] +COLOR_CYAN: Final[int] +COLOR_GREEN: Final[int] +COLOR_MAGENTA: Final[int] +COLOR_RED: Final[int] +COLOR_WHITE: Final[int] +COLOR_YELLOW: Final[int] +ERR: Final[int] +KEY_A1: Final[int] +KEY_A3: Final[int] +KEY_B2: Final[int] +KEY_BACKSPACE: Final[int] +KEY_BEG: Final[int] +KEY_BREAK: Final[int] +KEY_BTAB: Final[int] +KEY_C1: Final[int] +KEY_C3: Final[int] +KEY_CANCEL: Final[int] +KEY_CATAB: Final[int] +KEY_CLEAR: Final[int] +KEY_CLOSE: Final[int] +KEY_COMMAND: Final[int] +KEY_COPY: Final[int] +KEY_CREATE: Final[int] +KEY_CTAB: Final[int] +KEY_DC: Final[int] +KEY_DL: Final[int] +KEY_DOWN: Final[int] +KEY_EIC: Final[int] +KEY_END: Final[int] +KEY_ENTER: Final[int] +KEY_EOL: Final[int] +KEY_EOS: Final[int] +KEY_EXIT: Final[int] +KEY_F0: Final[int] +KEY_F1: Final[int] +KEY_F10: Final[int] +KEY_F11: Final[int] +KEY_F12: Final[int] +KEY_F13: Final[int] +KEY_F14: Final[int] +KEY_F15: Final[int] +KEY_F16: Final[int] +KEY_F17: Final[int] +KEY_F18: Final[int] +KEY_F19: Final[int] +KEY_F2: Final[int] +KEY_F20: Final[int] +KEY_F21: Final[int] +KEY_F22: Final[int] +KEY_F23: Final[int] +KEY_F24: Final[int] +KEY_F25: Final[int] +KEY_F26: Final[int] +KEY_F27: Final[int] +KEY_F28: Final[int] +KEY_F29: Final[int] +KEY_F3: Final[int] +KEY_F30: Final[int] +KEY_F31: Final[int] +KEY_F32: Final[int] +KEY_F33: Final[int] +KEY_F34: Final[int] +KEY_F35: Final[int] +KEY_F36: Final[int] +KEY_F37: Final[int] +KEY_F38: Final[int] +KEY_F39: Final[int] +KEY_F4: Final[int] +KEY_F40: Final[int] +KEY_F41: Final[int] +KEY_F42: Final[int] +KEY_F43: Final[int] +KEY_F44: Final[int] +KEY_F45: Final[int] +KEY_F46: Final[int] +KEY_F47: Final[int] +KEY_F48: Final[int] +KEY_F49: Final[int] +KEY_F5: Final[int] +KEY_F50: Final[int] +KEY_F51: Final[int] +KEY_F52: Final[int] +KEY_F53: Final[int] +KEY_F54: Final[int] +KEY_F55: Final[int] +KEY_F56: Final[int] +KEY_F57: Final[int] +KEY_F58: Final[int] +KEY_F59: Final[int] +KEY_F6: Final[int] +KEY_F60: Final[int] +KEY_F61: Final[int] +KEY_F62: Final[int] +KEY_F63: Final[int] +KEY_F7: Final[int] +KEY_F8: Final[int] +KEY_F9: Final[int] +KEY_FIND: Final[int] +KEY_HELP: Final[int] +KEY_HOME: Final[int] +KEY_IC: Final[int] +KEY_IL: Final[int] +KEY_LEFT: Final[int] +KEY_LL: Final[int] +KEY_MARK: Final[int] +KEY_MAX: Final[int] +KEY_MESSAGE: Final[int] +KEY_MIN: Final[int] +KEY_MOUSE: Final[int] +KEY_MOVE: Final[int] +KEY_NEXT: Final[int] +KEY_NPAGE: Final[int] +KEY_OPEN: Final[int] +KEY_OPTIONS: Final[int] +KEY_PPAGE: Final[int] +KEY_PREVIOUS: Final[int] +KEY_PRINT: Final[int] +KEY_REDO: Final[int] +KEY_REFERENCE: Final[int] +KEY_REFRESH: Final[int] +KEY_REPLACE: Final[int] +KEY_RESET: Final[int] +KEY_RESIZE: Final[int] +KEY_RESTART: Final[int] +KEY_RESUME: Final[int] +KEY_RIGHT: Final[int] +KEY_SAVE: Final[int] +KEY_SBEG: Final[int] +KEY_SCANCEL: Final[int] +KEY_SCOMMAND: Final[int] +KEY_SCOPY: Final[int] +KEY_SCREATE: Final[int] +KEY_SDC: Final[int] +KEY_SDL: Final[int] +KEY_SELECT: Final[int] +KEY_SEND: Final[int] +KEY_SEOL: Final[int] +KEY_SEXIT: Final[int] +KEY_SF: Final[int] +KEY_SFIND: Final[int] +KEY_SHELP: Final[int] +KEY_SHOME: Final[int] +KEY_SIC: Final[int] +KEY_SLEFT: Final[int] +KEY_SMESSAGE: Final[int] +KEY_SMOVE: Final[int] +KEY_SNEXT: Final[int] +KEY_SOPTIONS: Final[int] +KEY_SPREVIOUS: Final[int] +KEY_SPRINT: Final[int] +KEY_SR: Final[int] +KEY_SREDO: Final[int] +KEY_SREPLACE: Final[int] +KEY_SRESET: Final[int] +KEY_SRIGHT: Final[int] +KEY_SRSUME: Final[int] +KEY_SSAVE: Final[int] +KEY_SSUSPEND: Final[int] +KEY_STAB: Final[int] +KEY_SUNDO: Final[int] +KEY_SUSPEND: Final[int] +KEY_UNDO: Final[int] +KEY_UP: Final[int] +OK: Final[int] +REPORT_MOUSE_POSITION: Final[int] _C_API: Any -version: bytes +version: Final[bytes] def baudrate() -> int: ... def beep() -> None: ... @@ -291,26 +293,26 @@ def erasechar() -> bytes: ... def filter() -> None: ... def flash() -> None: ... def flushinp() -> None: ... - -if sys.version_info >= (3, 9): - def get_escdelay() -> int: ... - def get_tabsize() -> int: ... - +def get_escdelay() -> int: ... +def get_tabsize() -> int: ... def getmouse() -> tuple[int, int, int, int, int]: ... def getsyx() -> tuple[int, int]: ... -def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ... +def getwin(file: SupportsRead[bytes], /) -> window: ... def halfdelay(tenths: int, /) -> None: ... def has_colors() -> bool: ... if sys.version_info >= (3, 10): def has_extended_color_support() -> bool: ... +if sys.version_info >= (3, 14): + def assume_default_colors(fg: int, bg: int, /) -> None: ... + def has_ic() -> bool: ... def has_il() -> bool: ... def has_key(key: int, /) -> bool: ... def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ... def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ... -def initscr() -> _CursesWindow: ... +def initscr() -> window: ... def intrflush(flag: bool, /) -> None: ... def is_term_resized(nlines: int, ncols: int, /) -> bool: ... def isendwin() -> bool: ... @@ -321,8 +323,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 = 0, begin_x: int = 0, /) -> window: ... def nl(flag: bool = True, /) -> None: ... def nocbreak() -> None: ... def noecho() -> None: ... @@ -340,11 +342,8 @@ def resetty() -> None: ... def resize_term(nlines: int, ncols: int, /) -> None: ... def resizeterm(nlines: int, ncols: int, /) -> None: ... def savetty() -> None: ... - -if sys.version_info >= (3, 9): - def set_escdelay(ms: int, /) -> None: ... - def set_tabsize(size: int, /) -> None: ... - +def set_escdelay(ms: int, /) -> None: ... +def set_tabsize(size: int, /) -> None: ... def setsyx(y: int, x: int, /) -> None: ... def setupterm(term: str | None = None, fd: int = -1) -> None: ... def start_color() -> None: ... @@ -378,7 +377,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: ... @@ -395,8 +394,8 @@ class _CursesWindow: def attroff(self, attr: int, /) -> None: ... def attron(self, attr: int, /) -> None: ... def attrset(self, attr: int, /) -> None: ... - def bkgd(self, ch: _ChType, attr: int = ..., /) -> None: ... - def bkgdset(self, ch: _ChType, attr: int = ..., /) -> None: ... + def bkgd(self, ch: _ChType, attr: int = 0, /) -> None: ... + def bkgdset(self, ch: _ChType, attr: int = 0, /) -> None: ... def border( self, ls: _ChType = ..., @@ -411,7 +410,7 @@ class _CursesWindow: @overload def box(self) -> None: ... @overload - def box(self, vertch: _ChType = ..., horch: _ChType = ...) -> None: ... + def box(self, vertch: _ChType = 0, horch: _ChType = 0) -> None: ... @overload def chgat(self, attr: int) -> None: ... @overload @@ -431,10 +430,10 @@ 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 echochar(self, ch: _ChType, attr: int = ..., /) -> None: ... + def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... + def echochar(self, ch: _ChType, attr: int = 0, /) -> None: ... def enclose(self, y: int, x: int, /) -> bool: ... def erase(self) -> None: ... def getbegyx(self) -> tuple[int, int]: ... @@ -488,9 +487,9 @@ class _CursesWindow: @overload def insstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ... @overload - def instr(self, n: int = ...) -> bytes: ... + def instr(self, n: int = 2047) -> bytes: ... @overload - def instr(self, y: int, x: int, n: int = ...) -> bytes: ... + def instr(self, y: int, x: int, n: int = 2047) -> bytes: ... def is_linetouched(self, line: int, /) -> bool: ... def is_wintouched(self) -> bool: ... def keypad(self, yes: bool, /) -> None: ... @@ -505,18 +504,18 @@ 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 putwin(self, file: SupportsWrite[bytes], /) -> None: ... def redrawln(self, beg: int, num: int, /) -> None: ... def redrawwin(self) -> None: ... @overload @@ -524,24 +523,24 @@ class _CursesWindow: @overload def refresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... def resize(self, nlines: int, ncols: int) -> None: ... - def scroll(self, lines: int = ...) -> None: ... + def scroll(self, lines: int = 1) -> None: ... def scrollok(self, flag: bool) -> None: ... def setscrreg(self, top: int, bottom: int, /) -> None: ... def standend(self) -> None: ... def standout(self) -> None: ... @overload - def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + 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: ... def timeout(self, delay: int) -> None: ... - def touchline(self, start: int, count: int, changed: bool = ...) -> None: ... + def touchline(self, start: int, count: int, changed: bool = True) -> None: ... def touchwin(self) -> None: ... def untouchwin(self) -> None: ... @overload @@ -549,10 +548,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..a552a151ddf14 --- /dev/null +++ b/mypy/typeshed/stdlib/_curses_panel.pyi @@ -0,0 +1,27 @@ +from _curses import window +from typing import Final, final + +__version__: Final[str] +version: Final[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..222c3ffcb246b --- /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 Final, 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: Final[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 = b"", /) -> 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 937a04ac37998..3cfe8944dfaf4 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -1,51 +1,48 @@ -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: 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] +ROUND_DOWN: Final = "ROUND_DOWN" +ROUND_HALF_UP: Final = "ROUND_HALF_UP" +ROUND_HALF_EVEN: Final = "ROUND_HALF_EVEN" +ROUND_CEILING: Final = "ROUND_CEILING" +ROUND_FLOOR: Final = "ROUND_FLOOR" +ROUND_UP: Final = "ROUND_UP" +ROUND_HALF_DOWN: Final = "ROUND_HALF_DOWN" +ROUND_05UP: Final = "ROUND_05UP" HAVE_CONTEXTVAR: Final[bool] HAVE_THREADS: Final[bool] MAX_EMAX: Final[int] MAX_PREC: Final[int] MIN_EMIN: Final[int] MIN_ETINY: Final[int] - -class DecimalException(ArithmeticError): ... -class Clamped(DecimalException): ... -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): ... +if sys.version_info >= (3, 14): + IEEE_CONTEXT_MAX_BITS: Final[int] def setcontext(context: Context, /) -> None: ... def getcontext() -> Context: ... @@ -54,227 +51,21 @@ if sys.version_info >= (3, 11): def localcontext( ctx: Context | None = None, *, - prec: int | None = ..., - rounding: str | None = ..., - Emin: int | None = ..., - Emax: int | None = ..., - capitals: int | None = ..., - clamp: int | None = ..., - traps: dict[_TrapType, bool] | None = ..., - flags: dict[_TrapType, bool] | None = ..., + prec: int | None = None, + rounding: str | None = None, + Emin: int | None = None, + Emax: int | None = None, + capitals: int | None = None, + clamp: int | None = None, + traps: dict[_TrapType, bool] | None = None, + flags: dict[_TrapType, bool] | None = None, ) -> _ContextManager: ... else: def localcontext(ctx: Context | None = None) -> _ContextManager: ... -class Decimal: - def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... - @classmethod - def from_float(cls, f: float, /) -> Self: ... - def __bool__(self) -> bool: ... - def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __hash__(self) -> int: ... - def as_tuple(self) -> DecimalTuple: ... - def as_integer_ratio(self) -> tuple[int, int]: ... - def to_eng_string(self, context: Context | None = None) -> str: ... - def __abs__(self) -> Decimal: ... - def __add__(self, value: _Decimal, /) -> Decimal: ... - def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def __eq__(self, value: object, /) -> bool: ... - def __floordiv__(self, value: _Decimal, /) -> Decimal: ... - def __ge__(self, value: _ComparableNum, /) -> bool: ... - def __gt__(self, value: _ComparableNum, /) -> bool: ... - def __le__(self, value: _ComparableNum, /) -> bool: ... - def __lt__(self, value: _ComparableNum, /) -> bool: ... - def __mod__(self, value: _Decimal, /) -> Decimal: ... - def __mul__(self, value: _Decimal, /) -> Decimal: ... - def __neg__(self) -> Decimal: ... - def __pos__(self) -> Decimal: ... - def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ... - def __radd__(self, value: _Decimal, /) -> Decimal: ... - def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ... - def __rmod__(self, value: _Decimal, /) -> Decimal: ... - def __rmul__(self, value: _Decimal, /) -> Decimal: ... - def __rsub__(self, value: _Decimal, /) -> Decimal: ... - def __rtruediv__(self, value: _Decimal, /) -> Decimal: ... - def __sub__(self, value: _Decimal, /) -> Decimal: ... - def __truediv__(self, value: _Decimal, /) -> Decimal: ... - def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __float__(self) -> float: ... - def __int__(self) -> int: ... - def __trunc__(self) -> int: ... - @property - def real(self) -> Decimal: ... - @property - def imag(self) -> Decimal: ... - def conjugate(self) -> Decimal: ... - def __complex__(self) -> complex: ... - @overload - def __round__(self) -> int: ... - @overload - def __round__(self, ndigits: int, /) -> Decimal: ... - def __floor__(self) -> int: ... - def __ceil__(self) -> int: ... - def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ... - def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ... - def normalize(self, context: Context | None = None) -> Decimal: ... - def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ... - def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def sqrt(self, context: Context | None = None) -> Decimal: ... - def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def adjusted(self) -> int: ... - def canonical(self) -> Decimal: ... - def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def copy_abs(self) -> Decimal: ... - def copy_negate(self) -> Decimal: ... - def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def exp(self, context: Context | None = None) -> Decimal: ... - def is_canonical(self) -> bool: ... - def is_finite(self) -> bool: ... - def is_infinite(self) -> bool: ... - def is_nan(self) -> bool: ... - def is_normal(self, context: Context | None = None) -> bool: ... - def is_qnan(self) -> bool: ... - def is_signed(self) -> bool: ... - def is_snan(self) -> bool: ... - def is_subnormal(self, context: Context | None = None) -> bool: ... - def is_zero(self) -> bool: ... - def ln(self, context: Context | None = None) -> Decimal: ... - def log10(self, context: Context | None = None) -> Decimal: ... - def logb(self, context: Context | None = None) -> Decimal: ... - def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def logical_invert(self, context: Context | None = None) -> Decimal: ... - def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def next_minus(self, context: Context | None = None) -> Decimal: ... - def next_plus(self, context: Context | None = None) -> Decimal: ... - def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def number_class(self, context: Context | None = None) -> str: ... - def radix(self) -> Decimal: ... - def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... - def __copy__(self) -> Self: ... - def __deepcopy__(self, memo: Any, /) -> Self: ... - def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... - -class _ContextManager: - new_context: Context - saved_context: Context - def __init__(self, new_context: Context) -> None: ... - def __enter__(self) -> Context: ... - def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... - -_TrapType: TypeAlias = type[DecimalException] - -class Context: - # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime, - # even settable attributes like `prec` and `rounding`, - # but that's inexpressable in the stub. - # Type checkers either ignore it or misinterpret it - # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub - prec: int - rounding: str - Emin: int - Emax: int - capitals: int - clamp: int - traps: dict[_TrapType, bool] - flags: dict[_TrapType, bool] - def __init__( - self, - prec: int | None = ..., - rounding: str | None = ..., - Emin: int | None = ..., - Emax: int | None = ..., - capitals: int | None = ..., - clamp: int | None = ..., - flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - _ignored_flags: list[_TrapType] | None = ..., - ) -> None: ... - def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... - def clear_flags(self) -> None: ... - def clear_traps(self) -> None: ... - def copy(self) -> Context: ... - def __copy__(self) -> Context: ... - # see https://github.com/python/cpython/issues/94107 - __hash__: ClassVar[None] # type: ignore[assignment] - def Etiny(self) -> int: ... - def Etop(self) -> int: ... - def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ... - def create_decimal_from_float(self, f: float, /) -> Decimal: ... - def abs(self, x: _Decimal, /) -> Decimal: ... - def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def canonical(self, x: Decimal, /) -> Decimal: ... - def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def copy_abs(self, x: _Decimal, /) -> Decimal: ... - def copy_decimal(self, x: _Decimal, /) -> Decimal: ... - def copy_negate(self, x: _Decimal, /) -> Decimal: ... - def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def exp(self, x: _Decimal, /) -> Decimal: ... - def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ... - def is_canonical(self, x: _Decimal, /) -> bool: ... - def is_finite(self, x: _Decimal, /) -> bool: ... - def is_infinite(self, x: _Decimal, /) -> bool: ... - def is_nan(self, x: _Decimal, /) -> bool: ... - def is_normal(self, x: _Decimal, /) -> bool: ... - def is_qnan(self, x: _Decimal, /) -> bool: ... - def is_signed(self, x: _Decimal, /) -> bool: ... - def is_snan(self, x: _Decimal, /) -> bool: ... - def is_subnormal(self, x: _Decimal, /) -> bool: ... - def is_zero(self, x: _Decimal, /) -> bool: ... - def ln(self, x: _Decimal, /) -> Decimal: ... - def log10(self, x: _Decimal, /) -> Decimal: ... - def logb(self, x: _Decimal, /) -> Decimal: ... - def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def logical_invert(self, x: _Decimal, /) -> Decimal: ... - def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def minus(self, x: _Decimal, /) -> Decimal: ... - def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def next_minus(self, x: _Decimal, /) -> Decimal: ... - def next_plus(self, x: _Decimal, /) -> Decimal: ... - def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def normalize(self, x: _Decimal, /) -> Decimal: ... - def number_class(self, x: _Decimal, /) -> str: ... - def plus(self, x: _Decimal, /) -> Decimal: ... - def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ... - def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def radix(self) -> Decimal: ... - def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ... - def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def sqrt(self, x: _Decimal, /) -> Decimal: ... - def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def to_eng_string(self, x: _Decimal, /) -> str: ... - def to_sci_string(self, x: _Decimal, /) -> str: ... - def to_integral_exact(self, x: _Decimal, /) -> Decimal: ... - def to_integral_value(self, x: _Decimal, /) -> Decimal: ... - def to_integral(self, x: _Decimal, /) -> Decimal: ... +if sys.version_info >= (3, 14): + def IEEEContext(bits: int, /) -> Context: ... DefaultContext: Context BasicContext: Context diff --git a/mypy/typeshed/stdlib/_dummy_thread.pyi b/mypy/typeshed/stdlib/_dummy_thread.pyi deleted file mode 100644 index 1182e53c66c30..0000000000000 --- a/mypy/typeshed/stdlib/_dummy_thread.pyi +++ /dev/null @@ -1,33 +0,0 @@ -from collections.abc import Callable -from types import TracebackType -from typing import Any, NoReturn, overload -from typing_extensions import TypeVarTuple, Unpack - -__all__ = ["error", "start_new_thread", "exit", "get_ident", "allocate_lock", "interrupt_main", "LockType", "RLock"] - -_Ts = TypeVarTuple("_Ts") - -TIMEOUT_MAX: int -error = RuntimeError - -@overload -def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> None: ... -@overload -def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> None: ... -def exit() -> NoReturn: ... -def get_ident() -> int: ... -def allocate_lock() -> LockType: ... -def stack_size(size: int | None = None) -> int: ... - -class LockType: - locked_status: bool - def acquire(self, waitflag: bool | None = None, timeout: int = -1) -> bool: ... - def __enter__(self, waitflag: bool | None = None, timeout: int = -1) -> bool: ... - def __exit__(self, typ: type[BaseException] | None, val: BaseException | None, tb: TracebackType | None) -> None: ... - def release(self) -> bool: ... - def locked(self) -> bool: ... - -class RLock(LockType): - def release(self) -> None: ... # type: ignore[override] - -def interrupt_main() -> None: ... diff --git a/mypy/typeshed/stdlib/_dummy_threading.pyi b/mypy/typeshed/stdlib/_dummy_threading.pyi deleted file mode 100644 index 21d1d1921c0eb..0000000000000 --- a/mypy/typeshed/stdlib/_dummy_threading.pyi +++ /dev/null @@ -1,164 +0,0 @@ -import sys -from _thread import _excepthook, _ExceptHookArgs -from _typeshed import ProfileFunction, TraceFunction -from collections.abc import Callable, Iterable, Mapping -from types import TracebackType -from typing import Any, TypeVar - -_T = TypeVar("_T") - -__all__ = [ - "get_ident", - "active_count", - "Condition", - "current_thread", - "enumerate", - "main_thread", - "TIMEOUT_MAX", - "Event", - "Lock", - "RLock", - "Semaphore", - "BoundedSemaphore", - "Thread", - "Barrier", - "BrokenBarrierError", - "Timer", - "ThreadError", - "setprofile", - "settrace", - "local", - "stack_size", - "ExceptHookArgs", - "excepthook", -] - -def active_count() -> int: ... -def current_thread() -> Thread: ... -def currentThread() -> Thread: ... -def get_ident() -> int: ... -def enumerate() -> list[Thread]: ... -def main_thread() -> Thread: ... -def settrace(func: TraceFunction) -> None: ... -def setprofile(func: ProfileFunction | None) -> None: ... -def stack_size(size: int | None = None) -> int: ... - -TIMEOUT_MAX: float - -class ThreadError(Exception): ... - -class local: - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - -class Thread: - name: str - daemon: bool - @property - def ident(self) -> int | None: ... - def __init__( - self, - group: None = None, - target: Callable[..., object] | None = None, - name: str | None = None, - args: Iterable[Any] = (), - kwargs: Mapping[str, Any] | None = None, - *, - daemon: bool | None = None, - ) -> None: ... - def start(self) -> None: ... - def run(self) -> None: ... - def join(self, timeout: float | None = None) -> None: ... - def getName(self) -> str: ... - def setName(self, name: str) -> None: ... - @property - def native_id(self) -> int | None: ... # only available on some platforms - def is_alive(self) -> bool: ... - if sys.version_info < (3, 9): - def isAlive(self) -> bool: ... - - def isDaemon(self) -> bool: ... - def setDaemon(self, daemonic: bool) -> None: ... - -class _DummyThread(Thread): ... - -class Lock: - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... - def release(self) -> None: ... - def locked(self) -> bool: ... - -class _RLock: - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... - def release(self) -> None: ... - -RLock = _RLock - -class Condition: - def __init__(self, lock: Lock | _RLock | None = None) -> None: ... - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... - def release(self) -> None: ... - def wait(self, timeout: float | None = None) -> bool: ... - def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ... - def notify(self, n: int = 1) -> None: ... - def notify_all(self) -> None: ... - def notifyAll(self) -> None: ... - -class Semaphore: - def __init__(self, value: int = 1) -> None: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> bool | None: ... - def acquire(self, blocking: bool = True, timeout: float | None = None) -> bool: ... - def __enter__(self, blocking: bool = True, timeout: float | None = None) -> bool: ... - if sys.version_info >= (3, 9): - def release(self, n: int = ...) -> None: ... - else: - def release(self) -> None: ... - -class BoundedSemaphore(Semaphore): ... - -class Event: - def is_set(self) -> bool: ... - def set(self) -> None: ... - def clear(self) -> None: ... - def wait(self, timeout: float | None = None) -> bool: ... - -excepthook = _excepthook -ExceptHookArgs = _ExceptHookArgs - -class Timer(Thread): - def __init__( - self, - interval: float, - function: Callable[..., object], - args: Iterable[Any] | None = None, - kwargs: Mapping[str, Any] | None = None, - ) -> None: ... - def cancel(self) -> None: ... - -class Barrier: - @property - def parties(self) -> int: ... - @property - def n_waiting(self) -> int: ... - @property - def broken(self) -> bool: ... - def __init__(self, parties: int, action: Callable[[], None] | None = None, timeout: float | None = None) -> None: ... - def wait(self, timeout: float | None = None) -> int: ... - def reset(self) -> None: ... - def abort(self) -> None: ... - -class BrokenBarrierError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/_frozen_importlib.pyi b/mypy/typeshed/stdlib/_frozen_importlib.pyi new file mode 100644 index 0000000000000..58db64a016f34 --- /dev/null +++ b/mypy/typeshed/stdlib/_frozen_importlib.pyi @@ -0,0 +1,124 @@ +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, ClassVar +from typing_extensions import deprecated + +# 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] | None = (), + 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: ... + __hash__: ClassVar[None] # type: ignore[assignment] + +class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): + # MetaPathFinder + if sys.version_info < (3, 12): + @classmethod + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.") + 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 + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "The module spec is now used by the import machinery to generate a module repr." + ) + 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 + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.") + 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 + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "The module spec is now used by the import machinery to generate a module repr." + ) + 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..4778be3af1f39 --- /dev/null +++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi @@ -0,0 +1,200 @@ +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, Final, 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: Final[bytes] + +def cache_from_source(path: StrPath, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... +def source_from_cache(path: StrPath) -> 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: ... +@deprecated( + "Deprecated since Python 3.6. Use site configuration instead. " + "Future versions of Python may not enable this finder by default." +) +class WindowsRegistryFinder(importlib.abc.MetaPathFinder): + if sys.version_info < (3, 12): + @classmethod + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.") + 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 + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.") + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + +SOURCE_SUFFIXES: Final[list[str]] +DEBUG_BYTECODE_SUFFIXES: Final = [".pyc"] +OPTIMIZED_BYTECODE_SUFFIXES: Final = [".pyc"] +BYTECODE_SUFFIXES: Final = [".pyc"] +EXTENSION_SUFFIXES: Final[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: bytes | 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, fullname: str | None = None) -> str: ... + def load_module(self, fullname: 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]: ... + def source_to_code( # type: ignore[override] # incompatible with InspectLoader.source_to_code + self, + data: ReadableBuffer | str | _ast.Module | _ast.Expression | _ast.Interactive, + path: bytes | StrPath, + *, + _optimize: int = -1, + ) -> types.CodeType: ... + +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, fullname: 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("Deprecated since Python 3.10; will be removed in Python 3.15. 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( + "Deprecated since Python 3.4; removed in Python 3.12. " + "The module spec is now used by the import machinery to generate a module repr." + ) + 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: ... + if sys.version_info >= (3, 10): + @deprecated("Deprecated since Python 3.10; will be removed in Python 3.15. Use `exec_module()` instead.") + def load_module(self, fullname: str) -> types.ModuleType: ... + @staticmethod + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "The module spec is now used by the import machinery to generate a module repr." + ) + def module_repr(module: types.ModuleType) -> str: ... + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... + else: + def load_module(self, fullname: str) -> types.ModuleType: ... + @classmethod + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "The module spec is now used by the import machinery to generate a module repr." + ) + 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..2cb5fba29dfa1 --- /dev/null +++ b/mypy/typeshed/stdlib/_gdbm.pyi @@ -0,0 +1,48 @@ +import sys +from _typeshed import ReadOnlyBuffer, StrOrBytesPath +from types import TracebackType +from typing import TypeVar, 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 + + open_flags: str + + class error(OSError): ... + # Actual typename gdbm, not exposed by the implementation + @type_check_only + 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..03c1eef3be3ff --- /dev/null +++ b/mypy/typeshed/stdlib/_hashlib.pyi @@ -0,0 +1,127 @@ +import sys +from _typeshed import ReadableBuffer +from collections.abc import Callable +from types import ModuleType +from typing import AnyStr, Protocol, final, overload, type_check_only +from typing_extensions import Self, TypeAlias, disjoint_base + +_DigestMod: TypeAlias = str | Callable[[], _HashObject] | ModuleType | None + +openssl_md_meth_names: frozenset[str] + +@type_check_only +class _HashObject(Protocol): + @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: ... + +@disjoint_base +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): ... + +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: ... + +if sys.version_info >= (3, 13): + def new( + name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_md5( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha1( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha224( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha256( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha384( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha512( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha3_224( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha3_256( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha3_384( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_sha3_512( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASH: ... + def openssl_shake_128( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASHXOF: ... + def openssl_shake_256( + data: ReadableBuffer = b"", *, usedforsecurity: bool = True, string: ReadableBuffer | None = None + ) -> HASHXOF: ... + +else: + 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: ... + +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/_heapq.pyi b/mypy/typeshed/stdlib/_heapq.pyi index 9f731bf91eefd..4d7d6aba32418 100644 --- a/mypy/typeshed/stdlib/_heapq.pyi +++ b/mypy/typeshed/stdlib/_heapq.pyi @@ -1,11 +1,18 @@ -from typing import Any, Final, TypeVar - -_T = TypeVar("_T") +import sys +from _typeshed import SupportsRichComparisonT as _T # All type variable use in this module requires comparability. +from typing import Final __about__: Final[str] -def heapify(heap: list[Any], /) -> None: ... +def heapify(heap: list[_T], /) -> None: ... def heappop(heap: list[_T], /) -> _T: ... def heappush(heap: list[_T], item: _T, /) -> None: ... def heappushpop(heap: list[_T], item: _T, /) -> _T: ... def heapreplace(heap: list[_T], item: _T, /) -> _T: ... + +if sys.version_info >= (3, 14): + def heapify_max(heap: list[_T], /) -> None: ... + def heappop_max(heap: list[_T], /) -> _T: ... + def heappush_max(heap: list[_T], item: _T, /) -> None: ... + def heappushpop_max(heap: list[_T], item: _T, /) -> _T: ... + def heapreplace_max(heap: list[_T], item: _T, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/_imp.pyi b/mypy/typeshed/stdlib/_imp.pyi index de3549a91da59..c12c26d08ba2a 100644 --- a/mypy/typeshed/stdlib/_imp.pyi +++ b/mypy/typeshed/stdlib/_imp.pyi @@ -5,6 +5,8 @@ from importlib.machinery import ModuleSpec from typing import Any check_hash_based_pycs: str +if sys.version_info >= (3, 14): + pyc_magic_number_token: int def source_hash(key: int, source: ReadableBuffer) -> bytes: ... def create_builtin(spec: ModuleSpec, /) -> types.ModuleType: ... diff --git a/mypy/typeshed/stdlib/_interpchannels.pyi b/mypy/typeshed/stdlib/_interpchannels.pyi index c03496044df06..a631a6f16616b 100644 --- a/mypy/typeshed/stdlib/_interpchannels.pyi +++ b/mypy/typeshed/stdlib/_interpchannels.pyi @@ -17,15 +17,15 @@ class ChannelID: 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 __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: ... + 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]): diff --git a/mypy/typeshed/stdlib/_interpqueues.pyi b/mypy/typeshed/stdlib/_interpqueues.pyi index db5e4cff5068a..c9323b106f3dc 100644 --- a/mypy/typeshed/stdlib/_interpqueues.pyi +++ b/mypy/typeshed/stdlib/_interpqueues.pyi @@ -1,16 +1,19 @@ -from typing import Any, SupportsIndex +from typing import Any, Literal, SupportsIndex +from typing_extensions import TypeAlias + +_UnboundOp: TypeAlias = Literal[1, 2, 3] class QueueError(RuntimeError): ... class QueueNotFoundError(QueueError): ... def bind(qid: SupportsIndex) -> None: ... -def create(maxsize: SupportsIndex, fmt: SupportsIndex) -> int: ... +def create(maxsize: SupportsIndex, fmt: SupportsIndex, unboundop: _UnboundOp) -> int: ... def destroy(qid: SupportsIndex) -> None: ... -def get(qid: SupportsIndex) -> tuple[Any, int]: ... +def get(qid: SupportsIndex) -> tuple[Any, int, _UnboundOp | None]: ... def get_count(qid: SupportsIndex) -> int: ... def get_maxsize(qid: SupportsIndex) -> int: ... -def get_queue_defaults(qid: SupportsIndex) -> tuple[int]: ... +def get_queue_defaults(qid: SupportsIndex) -> tuple[int, _UnboundOp]: ... def is_full(qid: SupportsIndex) -> bool: ... -def list_all() -> list[tuple[int, int]]: ... -def put(qid: SupportsIndex, obj: Any, fmt: SupportsIndex) -> None: ... +def list_all() -> list[tuple[int, int, _UnboundOp]]: ... +def put(qid: SupportsIndex, obj: Any, fmt: SupportsIndex, unboundop: _UnboundOp) -> None: ... def release(qid: SupportsIndex) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi index 75f661a7e8e18..8e097efad618a 100644 --- a/mypy/typeshed/stdlib/_interpreters.pyi +++ b/mypy/typeshed/stdlib/_interpreters.pyi @@ -1,47 +1,58 @@ import types -from collections.abc import Callable, Mapping -from typing import Final, Literal, SupportsIndex -from typing_extensions import TypeAlias +from collections.abc import Callable +from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload +from typing_extensions import TypeAlias, disjoint_base + +_R = TypeVar("_R") _Configs: TypeAlias = Literal["default", "isolated", "legacy", "empty", ""] +_SharedDict: TypeAlias = dict[str, Any] # many objects can be shared class InterpreterError(Exception): ... class InterpreterNotFoundError(InterpreterError): ... -class NotShareableError(Exception): ... +class NotShareableError(ValueError): ... +@disjoint_base 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 list_all(*, require_ready: bool = False) -> list[tuple[int, _Whence]]: ... +def get_current() -> tuple[int, _Whence]: ... +def get_main() -> tuple[int, _Whence]: ... 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 whence(id: SupportsIndex) -> _Whence: ... +def exec( + id: SupportsIndex, code: str | types.CodeType | Callable[[], object], shared: _SharedDict = {}, *, restrict: bool = False +) -> None | types.SimpleNamespace: ... def call( id: SupportsIndex, - callable: Callable[..., object], - args: tuple[object, ...] | None = None, - kwargs: dict[str, object] | None = None, + callable: Callable[..., _R], + args: tuple[Any, ...] = (), + kwargs: dict[str, Any] = {}, *, + preserve_exc: bool = False, restrict: bool = False, -) -> object: ... +) -> tuple[_R, types.SimpleNamespace]: ... def run_string( - id: SupportsIndex, script: str | types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False + id: SupportsIndex, script: str | types.CodeType | Callable[[], object], shared: _SharedDict = {}, *, restrict: bool = False ) -> None: ... def run_func( - id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False + id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: _SharedDict = {}, *, restrict: bool = False ) -> None: ... -def set___main___attrs(id: SupportsIndex, updates: Mapping[str, object], *, restrict: bool = False) -> None: ... +def set___main___attrs(id: SupportsIndex, updates: _SharedDict, *, 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: ... +@overload +def capture_exception(exc: BaseException) -> types.SimpleNamespace: ... +@overload +def capture_exception(exc: None = None) -> types.SimpleNamespace | None: ... +_Whence: TypeAlias = Literal[0, 1, 2, 3, 4, 5] WHENCE_UNKNOWN: Final = 0 WHENCE_RUNTIME: Final = 1 WHENCE_LEGACY_CAPI: Final = 2 diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi new file mode 100644 index 0000000000000..2d2a60e4dddf1 --- /dev/null +++ b/mypy/typeshed/stdlib/_io.pyi @@ -0,0 +1,301 @@ +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, disjoint_base + +_T = TypeVar("_T") + +if sys.version_info >= (3, 14): + DEFAULT_BUFFER_SIZE: Final = 131072 +else: + DEFAULT_BUFFER_SIZE: Final = 8192 + +open = builtins.open + +def open_code(path: str) -> IO[bytes]: ... + +BlockingIOError = builtins.BlockingIOError + +if sys.version_info >= (3, 12): + @disjoint_base + 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 + +else: + 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: ... + +@disjoint_base +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: ... + +@disjoint_base +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: ... + +@type_check_only +class _BufferedReaderStream(Protocol): + def read(self, n: int = ..., /) -> bytes: ... + # Optional: def readall(self) -> bytes: ... + def readinto(self, b: memoryview, /) -> int | None: ... + def seek(self, pos: int, whence: int, /) -> int: ... + def tell(self) -> int: ... + def truncate(self, size: int, /) -> int: ... + def flush(self) -> object: ... + def close(self) -> object: ... + @property + def closed(self) -> bool: ... + def readable(self) -> bool: ... + def seekable(self) -> bool: ... + + # The following methods just pass through to the underlying stream. Since + # not all streams support them, they are marked as optional here, and will + # raise an AttributeError if called on a stream that does not support them. + + # @property + # def name(self) -> Any: ... # Type is inconsistent between the various I/O types. + # @property + # def mode(self) -> str: ... + # def fileno(self) -> int: ... + # def isatty(self) -> bool: ... + +_BufferedReaderStreamT = TypeVar("_BufferedReaderStreamT", bound=_BufferedReaderStream, default=_BufferedReaderStream) + +@disjoint_base +class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_BufferedReaderStreamT]): # type: ignore[misc] # incompatible definitions of methods in the base classes + raw: _BufferedReaderStreamT + if sys.version_info >= (3, 14): + def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 131072) -> None: ... + else: + def __init__(self, raw: _BufferedReaderStreamT, 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: ... + +@disjoint_base +class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes + raw: RawIOBase + if sys.version_info >= (3, 14): + def __init__(self, raw: RawIOBase, buffer_size: int = 131072) -> None: ... + else: + 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: ... + +@disjoint_base +class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + mode: str + name: Any + raw: RawIOBase + if sys.version_info >= (3, 14): + def __init__(self, raw: RawIOBase, buffer_size: int = 131072) -> None: ... + else: + 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: ... + +@disjoint_base +class BufferedRWPair(BufferedIOBase, _BufferedIOBase, Generic[_BufferedReaderStreamT]): + if sys.version_info >= (3, 14): + def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 131072, /) -> None: ... + else: + def __init__(self, reader: _BufferedReaderStreamT, 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. + @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) + +@disjoint_base +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: ... + +@disjoint_base +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: ... + +@disjoint_base +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..4a77e5be594ab 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -1,5 +1,6 @@ from collections.abc import Callable from typing import Any, final +from typing_extensions import Self @final class make_encoder: @@ -10,7 +11,7 @@ class make_encoder: @property def key_separator(self) -> str: ... @property - def indent(self) -> int | None: ... + def indent(self) -> str | None: ... @property def markers(self) -> dict[int, Any] | None: ... @property @@ -19,18 +20,18 @@ class make_encoder: def encoder(self) -> Callable[[str], str]: ... @property def item_separator(self) -> str: ... - def __init__( - self, + def __new__( + cls, markers: dict[int, Any] | None, default: Callable[[Any], Any], encoder: Callable[[str], str], - indent: int | None, + indent: str | None, key_separator: str, item_separator: str, sort_keys: bool, skipkeys: bool, allow_nan: bool, - ) -> None: ... + ) -> Self: ... def __call__(self, obj: object, _current_indent_level: int) -> Any: ... @final @@ -42,8 +43,9 @@ class make_scanner: parse_float: Any strict: bool # TODO: 'context' needs the attrs above (ducktype), but not __call__. - def __init__(self, context: make_scanner) -> None: ... + def __new__(cls, context: make_scanner) -> Self: ... 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]: ... +def scanstring(string: str, end: int, strict: bool = True) -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/_lsprof.pyi b/mypy/typeshed/stdlib/_lsprof.pyi index 8a6934162c929..4f6d98b8ffb61 100644 --- a/mypy/typeshed/stdlib/_lsprof.pyi +++ b/mypy/typeshed/stdlib/_lsprof.pyi @@ -3,7 +3,9 @@ from _typeshed import structseq from collections.abc import Callable from types import CodeType from typing import Any, Final, final +from typing_extensions import disjoint_base +@disjoint_base class Profiler: def __init__( self, timer: Callable[[], float] | None = None, timeunit: float = 0.0, subcalls: bool = True, builtins: bool = True diff --git a/mypy/typeshed/stdlib/_lzma.pyi b/mypy/typeshed/stdlib/_lzma.pyi new file mode 100644 index 0000000000000..b38dce9fadedf --- /dev/null +++ b/mypy/typeshed/stdlib/_lzma.pyi @@ -0,0 +1,71 @@ +import sys +from _typeshed import ReadableBuffer +from collections.abc import Mapping, Sequence +from typing import Any, Final, final +from typing_extensions import Self, 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: Final[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: Final[int] # v big number + +@final +class LZMADecompressor: + if sys.version_info >= (3, 12): + def __new__(cls, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = None) -> Self: ... + else: + def __init__(self, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = 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: + if sys.version_info >= (3, 12): + def __new__( + cls, format: int = 1, check: int = -1, preset: int | None = None, filters: _FilterChain | None = None + ) -> Self: ... + else: + def __init__( + self, format: int = 1, check: int = -1, preset: int | None = None, filters: _FilterChain | None = 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/_markupbase.pyi b/mypy/typeshed/stdlib/_markupbase.pyi index 62bad25e5cccc..597bd09b700b0 100644 --- a/mypy/typeshed/stdlib/_markupbase.pyi +++ b/mypy/typeshed/stdlib/_markupbase.pyi @@ -5,9 +5,9 @@ class ParserBase: def reset(self) -> None: ... def getpos(self) -> tuple[int, int]: ... def unknown_decl(self, data: str) -> None: ... - def parse_comment(self, i: int, report: int = 1) -> int: ... # undocumented + def parse_comment(self, i: int, report: bool = True) -> int: ... # undocumented def parse_declaration(self, i: int) -> int: ... # undocumented - def parse_marked_section(self, i: int, report: int = 1) -> int: ... # undocumented + def parse_marked_section(self, i: int, report: bool = True) -> int: ... # undocumented def updatepos(self, i: int, j: int) -> int: ... # undocumented if sys.version_info < (3, 10): # Removed from ParserBase: https://bugs.python.org/issue31844 diff --git a/mypy/typeshed/stdlib/_msi.pyi b/mypy/typeshed/stdlib/_msi.pyi index 779fda3b67fe9..edceed51bf9db 100644 --- a/mypy/typeshed/stdlib/_msi.pyi +++ b/mypy/typeshed/stdlib/_msi.pyi @@ -1,8 +1,10 @@ import sys +from typing import Final, type_check_only if sys.platform == "win32": class MSIError(Exception): ... # Actual typename View, not exposed by the implementation + @type_check_only class _View: def Execute(self, params: _Record | None = ...) -> None: ... def GetColumnInfo(self, kind: int) -> _Record: ... @@ -14,6 +16,7 @@ if sys.platform == "win32": __init__: None # type: ignore[assignment] # Actual typename SummaryInformation, not exposed by the implementation + @type_check_only class _SummaryInformation: def GetProperty(self, field: int) -> int | bytes | None: ... def GetPropertyCount(self) -> int: ... @@ -24,6 +27,7 @@ if sys.platform == "win32": __init__: None # type: ignore[assignment] # Actual typename Database, not exposed by the implementation + @type_check_only class _Database: def OpenView(self, sql: str) -> _View: ... def Commit(self) -> None: ... @@ -34,6 +38,7 @@ if sys.platform == "win32": __init__: None # type: ignore[assignment] # Actual typename Record, not exposed by the implementation + @type_check_only class _Record: def GetFieldCount(self) -> int: ... def GetInteger(self, field: int) -> int: ... @@ -51,42 +56,42 @@ if sys.platform == "win32": def OpenDatabase(path: str, persist: int, /) -> _Database: ... def CreateRecord(count: int, /) -> _Record: ... - MSICOLINFO_NAMES: int - MSICOLINFO_TYPES: int - MSIDBOPEN_CREATE: int - MSIDBOPEN_CREATEDIRECT: int - MSIDBOPEN_DIRECT: int - MSIDBOPEN_PATCHFILE: int - MSIDBOPEN_READONLY: int - MSIDBOPEN_TRANSACT: int - MSIMODIFY_ASSIGN: int - MSIMODIFY_DELETE: int - MSIMODIFY_INSERT: int - MSIMODIFY_INSERT_TEMPORARY: int - MSIMODIFY_MERGE: int - MSIMODIFY_REFRESH: int - MSIMODIFY_REPLACE: int - MSIMODIFY_SEEK: int - MSIMODIFY_UPDATE: int - MSIMODIFY_VALIDATE: int - MSIMODIFY_VALIDATE_DELETE: int - MSIMODIFY_VALIDATE_FIELD: int - MSIMODIFY_VALIDATE_NEW: int + MSICOLINFO_NAMES: Final[int] + MSICOLINFO_TYPES: Final[int] + MSIDBOPEN_CREATE: Final[int] + MSIDBOPEN_CREATEDIRECT: Final[int] + MSIDBOPEN_DIRECT: Final[int] + MSIDBOPEN_PATCHFILE: Final[int] + MSIDBOPEN_READONLY: Final[int] + MSIDBOPEN_TRANSACT: Final[int] + MSIMODIFY_ASSIGN: Final[int] + MSIMODIFY_DELETE: Final[int] + MSIMODIFY_INSERT: Final[int] + MSIMODIFY_INSERT_TEMPORARY: Final[int] + MSIMODIFY_MERGE: Final[int] + MSIMODIFY_REFRESH: Final[int] + MSIMODIFY_REPLACE: Final[int] + MSIMODIFY_SEEK: Final[int] + MSIMODIFY_UPDATE: Final[int] + MSIMODIFY_VALIDATE: Final[int] + MSIMODIFY_VALIDATE_DELETE: Final[int] + MSIMODIFY_VALIDATE_FIELD: Final[int] + MSIMODIFY_VALIDATE_NEW: Final[int] - PID_APPNAME: int - PID_AUTHOR: int - PID_CHARCOUNT: int - PID_CODEPAGE: int - PID_COMMENTS: int - PID_CREATE_DTM: int - PID_KEYWORDS: int - PID_LASTAUTHOR: int - PID_LASTPRINTED: int - PID_LASTSAVE_DTM: int - PID_PAGECOUNT: int - PID_REVNUMBER: int - PID_SECURITY: int - PID_SUBJECT: int - PID_TEMPLATE: int - PID_TITLE: int - PID_WORDCOUNT: int + PID_APPNAME: Final[int] + PID_AUTHOR: Final[int] + PID_CHARCOUNT: Final[int] + PID_CODEPAGE: Final[int] + PID_COMMENTS: Final[int] + PID_CREATE_DTM: Final[int] + PID_KEYWORDS: Final[int] + PID_LASTAUTHOR: Final[int] + PID_LASTPRINTED: Final[int] + PID_LASTSAVE_DTM: Final[int] + PID_PAGECOUNT: Final[int] + PID_REVNUMBER: Final[int] + PID_SECURITY: Final[int] + PID_SUBJECT: Final[int] + PID_TEMPLATE: Final[int] + PID_TITLE: Final[int] + PID_WORDCOUNT: Final[int] diff --git a/mypy/typeshed/stdlib/_multibytecodec.pyi b/mypy/typeshed/stdlib/_multibytecodec.pyi new file mode 100644 index 0000000000000..abe58cb64f319 --- /dev/null +++ b/mypy/typeshed/stdlib/_multibytecodec.pyi @@ -0,0 +1,49 @@ +from _typeshed import ReadableBuffer +from codecs import _ReadableStream, _WritableStream +from collections.abc import Iterable +from typing import final, type_check_only +from typing_extensions import disjoint_base + +# 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: ... + +@disjoint_base +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: ... + +@disjoint_base +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: ... + +@disjoint_base +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: ... + +@disjoint_base +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 1b0083f4e2748..cb1c1bcfc4aab 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -1,43 +1,48 @@ 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, TypeIs, TypeVarTuple, Unpack +from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller +from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload, type_check_only +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, # the numpy.array comparison dunders return another numpy.array. +@type_check_only class _SupportsDunderLT(Protocol): def __lt__(self, other: Any, /) -> Any: ... +@type_check_only class _SupportsDunderGT(Protocol): def __gt__(self, other: Any, /) -> Any: ... +@type_check_only class _SupportsDunderLE(Protocol): def __le__(self, other: Any, /) -> Any: ... +@type_check_only class _SupportsDunderGE(Protocol): def __ge__(self, other: Any, /) -> Any: ... _SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT +@type_check_only class _SupportsInversion(Protocol[_T_co]): def __invert__(self) -> _T_co: ... +@type_check_only class _SupportsNeg(Protocol[_T_co]): def __neg__(self) -> _T_co: ... +@type_check_only class _SupportsPos(Protocol[_T_co]): def __pos__(self) -> _T_co: ... @@ -92,40 +97,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: ... diff --git a/mypy/typeshed/stdlib/_pickle.pyi b/mypy/typeshed/stdlib/_pickle.pyi new file mode 100644 index 0000000000000..544f787172d6f --- /dev/null +++ b/mypy/typeshed/stdlib/_pickle.pyi @@ -0,0 +1,107 @@ +from _typeshed import ReadableBuffer, SupportsWrite +from collections.abc import Callable, Iterable, Iterator, Mapping +from pickle import PickleBuffer as PickleBuffer +from typing import Any, Protocol, type_check_only +from typing_extensions import TypeAlias, disjoint_base + +@type_check_only +class _ReadableFileobj(Protocol): + def read(self, n: int, /) -> bytes: ... + def readline(self) -> bytes: ... + +_BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None + +_ReducedType: TypeAlias = ( + str + | tuple[Callable[..., Any], tuple[Any, ...]] + | tuple[Callable[..., Any], tuple[Any, ...], Any] + | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None] + | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None, Iterator[Any] | None] +) + +def dump( + obj: Any, + file: SupportsWrite[bytes], + protocol: int | None = None, + *, + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, +) -> None: ... +def dumps( + obj: Any, protocol: int | None = None, *, fix_imports: bool = True, buffer_callback: _BufferCallback = None +) -> bytes: ... +def load( + file: _ReadableFileobj, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), +) -> Any: ... +def loads( + data: ReadableBuffer, + /, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), +) -> Any: ... + +class PickleError(Exception): ... +class PicklingError(PickleError): ... +class UnpicklingError(PickleError): ... + +@type_check_only +class PicklerMemoProxy: + def clear(self, /) -> None: ... + def copy(self, /) -> dict[int, tuple[int, Any]]: ... + +@disjoint_base +class Pickler: + fast: bool + dispatch_table: Mapping[type, Callable[[Any], _ReducedType]] + reducer_override: Callable[[Any], Any] + bin: bool # undocumented + def __init__( + self, + file: SupportsWrite[bytes], + protocol: int | None = None, + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, + ) -> None: ... + @property + def memo(self) -> PicklerMemoProxy: ... + @memo.setter + def memo(self, value: PicklerMemoProxy | dict[int, tuple[int, Any]]) -> None: ... + def dump(self, obj: Any, /) -> None: ... + def clear_memo(self) -> None: ... + + # this method has no default implementation for Python < 3.13 + def persistent_id(self, obj: Any, /) -> Any: ... + +@type_check_only +class UnpicklerMemoProxy: + def clear(self, /) -> None: ... + def copy(self, /) -> dict[int, tuple[int, Any]]: ... + +@disjoint_base +class Unpickler: + def __init__( + self, + file: _ReadableFileobj, + *, + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), + ) -> None: ... + @property + def memo(self) -> UnpicklerMemoProxy: ... + @memo.setter + def memo(self, value: UnpicklerMemoProxy | dict[int, tuple[int, Any]]) -> None: ... + def load(self) -> Any: ... + def find_class(self, module_name: str, global_name: str, /) -> Any: ... + + # this method has no default implementation for Python < 3.13 + def persistent_load(self, pid: Any, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index 3df56d9a3d03a..dd74e316e8990 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -4,30 +4,56 @@ from collections.abc import Callable, Sequence from typing import SupportsIndex if sys.platform != "win32": - def cloexec_pipe() -> tuple[int, int]: ... - def fork_exec( - args: Sequence[StrOrBytesPath] | None, - executable_list: Sequence[bytes], - close_fds: bool, - pass_fds: tuple[int, ...], - cwd: str, - env: Sequence[bytes] | None, - p2cread: int, - p2cwrite: int, - c2pread: int, - c2pwrite: int, - errread: int, - errwrite: int, - errpipe_read: int, - errpipe_write: int, - restore_signals: int, - call_setsid: int, - pgid_to_set: int, - gid: SupportsIndex | None, - extra_groups: list[int] | None, - uid: SupportsIndex | None, - child_umask: int, - preexec_fn: Callable[[], None], - allow_vfork: bool, - /, - ) -> int: ... + if sys.version_info >= (3, 14): + def fork_exec( + args: Sequence[StrOrBytesPath] | None, + executable_list: Sequence[bytes], + close_fds: bool, + pass_fds: tuple[int, ...], + cwd: str, + env: Sequence[bytes] | None, + p2cread: int, + p2cwrite: int, + c2pread: int, + c2pwrite: int, + errread: int, + errwrite: int, + errpipe_read: int, + errpipe_write: int, + restore_signals: int, + call_setsid: int, + pgid_to_set: int, + gid: SupportsIndex | None, + extra_groups: list[int] | None, + uid: SupportsIndex | None, + child_umask: int, + preexec_fn: Callable[[], None], + /, + ) -> int: ... + else: + def fork_exec( + args: Sequence[StrOrBytesPath] | None, + executable_list: Sequence[bytes], + close_fds: bool, + pass_fds: tuple[int, ...], + cwd: str, + env: Sequence[bytes] | None, + p2cread: int, + p2cwrite: int, + c2pread: int, + c2pwrite: int, + errread: int, + errwrite: int, + errpipe_read: int, + errpipe_write: int, + restore_signals: bool, + call_setsid: bool, + pgid_to_set: int, + gid: SupportsIndex | None, + extra_groups: list[int] | None, + uid: SupportsIndex | None, + child_umask: int, + preexec_fn: Callable[[], None], + allow_vfork: bool, + /, + ) -> int: ... diff --git a/mypy/typeshed/stdlib/_pydecimal.pyi b/mypy/typeshed/stdlib/_pydecimal.pyi index faff626ac0bae..a6723f749da6d 100644 --- a/mypy/typeshed/stdlib/_pydecimal.pyi +++ b/mypy/typeshed/stdlib/_pydecimal.pyi @@ -1,5 +1,6 @@ # This is a slight lie, the implementations aren't exactly identical # However, in all likelihood, the differences are inconsequential +import sys from _decimal import * __all__ = [ @@ -41,3 +42,6 @@ __all__ = [ "HAVE_THREADS", "HAVE_CONTEXTVAR", ] + +if sys.version_info >= (3, 14): + __all__ += ["IEEEContext", "IEEE_CONTEXT_MAX_BITS"] diff --git a/mypy/typeshed/stdlib/_queue.pyi b/mypy/typeshed/stdlib/_queue.pyi new file mode 100644 index 0000000000000..edd484a9a71a4 --- /dev/null +++ b/mypy/typeshed/stdlib/_queue.pyi @@ -0,0 +1,18 @@ +from types import GenericAlias +from typing import Any, Generic, TypeVar +from typing_extensions import disjoint_base + +_T = TypeVar("_T") + +class Empty(Exception): ... + +@disjoint_base +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: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/_random.pyi b/mypy/typeshed/stdlib/_random.pyi index 4082344ade8e1..ac00fdfb7272b 100644 --- a/mypy/typeshed/stdlib/_random.pyi +++ b/mypy/typeshed/stdlib/_random.pyi @@ -1,10 +1,16 @@ -from typing_extensions import TypeAlias +import sys +from typing_extensions import Self, TypeAlias, disjoint_base # Actually Tuple[(int,) * 625] _State: TypeAlias = tuple[int, ...] +@disjoint_base class Random: - def __init__(self, seed: object = ...) -> None: ... + if sys.version_info >= (3, 10): + def __init__(self, seed: object = ..., /) -> None: ... + else: + def __new__(self, seed: object = ..., /) -> Self: ... + def seed(self, n: object = None, /) -> None: ... def getstate(self) -> _State: ... def setstate(self, state: _State, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_sitebuiltins.pyi b/mypy/typeshed/stdlib/_sitebuiltins.pyi index 49e88a196825e..eb6c811294216 100644 --- a/mypy/typeshed/stdlib/_sitebuiltins.pyi +++ b/mypy/typeshed/stdlib/_sitebuiltins.pyi @@ -1,3 +1,4 @@ +import sys from collections.abc import Iterable from typing import ClassVar, Literal, NoReturn @@ -5,7 +6,7 @@ class Quitter: name: str eof: str def __init__(self, name: str, eof: str) -> None: ... - def __call__(self, code: int | None = None) -> NoReturn: ... + def __call__(self, code: sys._ExitCode = None) -> NoReturn: ... class _Printer: MAXLINES: ClassVar[Literal[23]] @@ -13,4 +14,4 @@ class _Printer: def __call__(self) -> None: ... class _Helper: - def __call__(self, request: object) -> None: ... + def __call__(self, request: object = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index affa8d63ecfab..cdad886b3415e 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 typing import Any, SupportsIndex, overload -from typing_extensions import TypeAlias +from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout +from typing import Any, Final, SupportsIndex, overload +from typing_extensions import CapsuleType, TypeAlias, disjoint_base _CMSG: TypeAlias = tuple[int, int, bytes] _CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer] @@ -18,23 +19,23 @@ _RetAddress: TypeAlias = Any # https://docs.python.org/3/library/socket.html#constants if sys.platform != "win32": - AF_UNIX: int + AF_UNIX: Final[int] -AF_INET: int -AF_INET6: int +AF_INET: Final[int] +AF_INET6: Final[int] -AF_UNSPEC: int +AF_UNSPEC: Final[int] -SOCK_STREAM: int -SOCK_DGRAM: int -SOCK_RAW: int -SOCK_RDM: int -SOCK_SEQPACKET: int +SOCK_STREAM: Final[int] +SOCK_DGRAM: Final[int] +SOCK_RAW: Final[int] +SOCK_RDM: Final[int] +SOCK_SEQPACKET: Final[int] if sys.platform == "linux": # Availability: Linux >= 2.6.27 - SOCK_CLOEXEC: int - SOCK_NONBLOCK: int + SOCK_CLOEXEC: Final[int] + SOCK_NONBLOCK: Final[int] # -------------------- # Many constants of these forms, documented in the Unix documentation on @@ -55,268 +56,329 @@ if sys.platform == "linux": # TCP_* # -------------------- -SO_ACCEPTCONN: int -SO_BROADCAST: int -SO_DEBUG: int -SO_DONTROUTE: int -SO_ERROR: int -SO_KEEPALIVE: int -SO_LINGER: int -SO_OOBINLINE: int -SO_RCVBUF: int -SO_RCVLOWAT: int -SO_RCVTIMEO: int -SO_REUSEADDR: int -SO_SNDBUF: int -SO_SNDLOWAT: int -SO_SNDTIMEO: int -SO_TYPE: int -SO_USELOOPBACK: int +SO_ACCEPTCONN: Final[int] +SO_BROADCAST: Final[int] +SO_DEBUG: Final[int] +SO_DONTROUTE: Final[int] +SO_ERROR: Final[int] +SO_KEEPALIVE: Final[int] +SO_LINGER: Final[int] +SO_OOBINLINE: Final[int] +SO_RCVBUF: Final[int] +SO_RCVLOWAT: Final[int] +SO_RCVTIMEO: Final[int] +SO_REUSEADDR: Final[int] +SO_SNDBUF: Final[int] +SO_SNDLOWAT: Final[int] +SO_SNDTIMEO: Final[int] +SO_TYPE: Final[int] +if sys.platform != "linux": + SO_USELOOPBACK: Final[int] if sys.platform == "win32": - SO_EXCLUSIVEADDRUSE: int + SO_EXCLUSIVEADDRUSE: Final[int] if sys.platform != "win32": - SO_REUSEPORT: int + SO_REUSEPORT: Final[int] + if sys.platform != "darwin" or sys.version_info >= (3, 13): + SO_BINDTODEVICE: Final[int] + if sys.platform != "win32" and sys.platform != "darwin": - SO_BINDTODEVICE: int - SO_DOMAIN: int - SO_MARK: int - SO_PASSCRED: int - SO_PASSSEC: int - SO_PEERCRED: int - SO_PEERSEC: int - SO_PRIORITY: int - SO_PROTOCOL: int - SO_SETFIB: int - -SOMAXCONN: int - -MSG_CTRUNC: int -MSG_DONTROUTE: int -MSG_OOB: int -MSG_PEEK: int -MSG_TRUNC: int -MSG_WAITALL: int + SO_DOMAIN: Final[int] + SO_MARK: Final[int] + SO_PASSCRED: Final[int] + SO_PASSSEC: Final[int] + SO_PEERCRED: Final[int] + SO_PEERSEC: Final[int] + SO_PRIORITY: Final[int] + SO_PROTOCOL: Final[int] +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + SO_SETFIB: Final[int] +if sys.platform == "linux" and sys.version_info >= (3, 13): + SO_BINDTOIFINDEX: Final[int] + +SOMAXCONN: Final[int] + +MSG_CTRUNC: Final[int] +MSG_DONTROUTE: Final[int] +MSG_OOB: Final[int] +MSG_PEEK: Final[int] +MSG_TRUNC: Final[int] +MSG_WAITALL: Final[int] if sys.platform != "win32": - MSG_DONTWAIT: int - MSG_EOF: int - MSG_EOR: int - MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not + MSG_DONTWAIT: Final[int] + MSG_EOR: Final[int] + MSG_NOSIGNAL: Final[int] # Sometimes this exists on darwin, sometimes not if sys.platform != "darwin": - MSG_BCAST: int - MSG_ERRQUEUE: int - MSG_MCAST: int + MSG_ERRQUEUE: Final[int] +if sys.platform == "win32": + MSG_BCAST: Final[int] + MSG_MCAST: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG: int - MSG_CMSG_CLOEXEC: int - MSG_CONFIRM: int - MSG_ETAG: int - MSG_FASTOPEN: int - MSG_MORE: int - MSG_NOTIFICATION: int - -SOL_IP: int -SOL_SOCKET: int -SOL_TCP: int -SOL_UDP: int + MSG_CMSG_CLOEXEC: Final[int] + MSG_CONFIRM: Final[int] + MSG_FASTOPEN: Final[int] + MSG_MORE: Final[int] +if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF: Final[int] +if sys.platform != "win32" and sys.platform != "linux" and sys.platform != "darwin": + MSG_NOTIFICATION: Final[int] + MSG_BTAG: Final[int] # Not FreeBSD either + MSG_ETAG: Final[int] # Not FreeBSD either + +SOL_IP: Final[int] +SOL_SOCKET: Final[int] +SOL_TCP: Final[int] +SOL_UDP: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - SOL_ATALK: int - SOL_AX25: int - SOL_HCI: int - SOL_IPX: int - SOL_NETROM: int - SOL_ROSE: int + # Defined in socket.h for Linux, but these aren't always present for + # some reason. + SOL_ATALK: Final[int] + SOL_AX25: Final[int] + SOL_HCI: Final[int] + SOL_IPX: Final[int] + SOL_NETROM: Final[int] + SOL_ROSE: Final[int] if sys.platform != "win32": - SCM_CREDS: int - SCM_RIGHTS: int + SCM_RIGHTS: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - SCM_CREDENTIALS: int - -IPPROTO_ICMP: int -IPPROTO_IP: int -IPPROTO_RAW: int -IPPROTO_TCP: int -IPPROTO_UDP: int -IPPROTO_AH: int -IPPROTO_DSTOPTS: int -IPPROTO_EGP: int -IPPROTO_ESP: int -IPPROTO_FRAGMENT: int -IPPROTO_GGP: int -IPPROTO_HOPOPTS: int -IPPROTO_ICMPV6: int -IPPROTO_IDP: int -IPPROTO_IGMP: int -IPPROTO_IPV4: int -IPPROTO_IPV6: int -IPPROTO_MAX: int -IPPROTO_ND: int -IPPROTO_NONE: int -IPPROTO_PIM: int -IPPROTO_PUP: int -IPPROTO_ROUTING: int -IPPROTO_SCTP: int -if sys.platform != "darwin": - IPPROTO_CBT: int - IPPROTO_ICLFXBM: int - IPPROTO_IGP: int - IPPROTO_L2TP: int - IPPROTO_PGM: int - IPPROTO_RDP: int - IPPROTO_ST: int + SCM_CREDENTIALS: Final[int] +if sys.platform != "win32" and sys.platform != "linux": + SCM_CREDS: Final[int] + +IPPROTO_ICMP: Final[int] +IPPROTO_IP: Final[int] +IPPROTO_RAW: Final[int] +IPPROTO_TCP: Final[int] +IPPROTO_UDP: Final[int] +IPPROTO_AH: Final[int] +IPPROTO_DSTOPTS: Final[int] +IPPROTO_EGP: Final[int] +IPPROTO_ESP: Final[int] +IPPROTO_FRAGMENT: Final[int] +IPPROTO_HOPOPTS: Final[int] +IPPROTO_ICMPV6: Final[int] +IPPROTO_IDP: Final[int] +IPPROTO_IGMP: Final[int] +IPPROTO_IPV6: Final[int] +IPPROTO_NONE: Final[int] +IPPROTO_PIM: Final[int] +IPPROTO_PUP: Final[int] +IPPROTO_ROUTING: Final[int] +IPPROTO_SCTP: Final[int] +if sys.platform != "linux": + IPPROTO_GGP: Final[int] + IPPROTO_IPV4: Final[int] + IPPROTO_MAX: Final[int] + IPPROTO_ND: Final[int] +if sys.platform == "win32": + IPPROTO_CBT: Final[int] + IPPROTO_ICLFXBM: Final[int] + IPPROTO_IGP: Final[int] + IPPROTO_L2TP: Final[int] + IPPROTO_PGM: Final[int] + IPPROTO_RDP: Final[int] + IPPROTO_ST: Final[int] if sys.platform != "win32": - IPPROTO_EON: int - IPPROTO_GRE: int - IPPROTO_HELLO: int - IPPROTO_IPCOMP: int - IPPROTO_IPIP: int - IPPROTO_RSVP: int - IPPROTO_TP: int - IPPROTO_XTP: int -if sys.platform != "win32" and sys.platform != "darwin": - IPPROTO_BIP: int - IPPROTO_MOBILE: int - IPPROTO_VRRP: int -if sys.version_info >= (3, 9) and sys.platform == "linux": + IPPROTO_GRE: Final[int] + IPPROTO_IPIP: Final[int] + IPPROTO_RSVP: Final[int] + IPPROTO_TP: Final[int] +if sys.platform != "win32" and sys.platform != "linux": + IPPROTO_EON: Final[int] + IPPROTO_HELLO: Final[int] + IPPROTO_IPCOMP: Final[int] + IPPROTO_XTP: Final[int] +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + IPPROTO_BIP: Final[int] # Not FreeBSD either + IPPROTO_MOBILE: Final[int] # Not FreeBSD either + IPPROTO_VRRP: Final[int] # Not FreeBSD either +if sys.platform == "linux": # Availability: Linux >= 2.6.20, FreeBSD >= 10.1 - IPPROTO_UDPLITE: int + IPPROTO_UDPLITE: Final[int] if sys.version_info >= (3, 10) and sys.platform == "linux": - IPPROTO_MPTCP: int - -IPPORT_RESERVED: int -IPPORT_USERRESERVED: int - -INADDR_ALLHOSTS_GROUP: int -INADDR_ANY: int -INADDR_BROADCAST: int -INADDR_LOOPBACK: int -INADDR_MAX_LOCAL_GROUP: int -INADDR_NONE: int -INADDR_UNSPEC_GROUP: int - -IP_ADD_MEMBERSHIP: int -IP_DROP_MEMBERSHIP: int -IP_HDRINCL: int -IP_MULTICAST_IF: int -IP_MULTICAST_LOOP: int -IP_MULTICAST_TTL: int -IP_OPTIONS: int -IP_RECVDSTADDR: int + IPPROTO_MPTCP: Final[int] + +IPPORT_RESERVED: Final[int] +IPPORT_USERRESERVED: Final[int] + +INADDR_ALLHOSTS_GROUP: Final[int] +INADDR_ANY: Final[int] +INADDR_BROADCAST: Final[int] +INADDR_LOOPBACK: Final[int] +INADDR_MAX_LOCAL_GROUP: Final[int] +INADDR_NONE: Final[int] +INADDR_UNSPEC_GROUP: Final[int] + +IP_ADD_MEMBERSHIP: Final[int] +IP_DROP_MEMBERSHIP: Final[int] +IP_HDRINCL: Final[int] +IP_MULTICAST_IF: Final[int] +IP_MULTICAST_LOOP: Final[int] +IP_MULTICAST_TTL: Final[int] +IP_OPTIONS: Final[int] +if sys.platform != "linux": + IP_RECVDSTADDR: Final[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 + IP_RECVTOS: Final[int] +IP_TOS: Final[int] +IP_TTL: Final[int] if sys.platform != "win32": - IP_DEFAULT_MULTICAST_LOOP: int - IP_DEFAULT_MULTICAST_TTL: int - IP_MAX_MEMBERSHIPS: int - IP_RECVOPTS: int - IP_RECVRETOPTS: int - IP_RETOPTS: int + IP_DEFAULT_MULTICAST_LOOP: Final[int] + IP_DEFAULT_MULTICAST_TTL: Final[int] + IP_MAX_MEMBERSHIPS: Final[int] + IP_RECVOPTS: Final[int] + IP_RECVRETOPTS: Final[int] + IP_RETOPTS: Final[int] +if sys.version_info >= (3, 13) and sys.platform == "linux": + CAN_RAW_ERR_FILTER: Final[int] +if sys.version_info >= (3, 14): + IP_RECVTTL: Final[int] + + if sys.platform == "win32" or sys.platform == "linux": + IPV6_RECVERR: Final[int] + IP_RECVERR: Final[int] + SO_ORIGINAL_DST: Final[int] + + if sys.platform == "win32": + SOL_RFCOMM: Final[int] + SO_BTH_ENCRYPT: Final[int] + SO_BTH_MTU: Final[int] + SO_BTH_MTU_MAX: Final[int] + SO_BTH_MTU_MIN: Final[int] + TCP_QUICKACK: Final[int] + + if sys.platform == "linux": + IP_FREEBIND: Final[int] + IP_RECVORIGDSTADDR: Final[int] + VMADDR_CID_LOCAL: Final[int] + if sys.platform != "win32" and sys.platform != "darwin": - IP_TRANSPARENT: int - IP_BIND_ADDRESS_NO_PORT: int + IP_TRANSPARENT: Final[int] +if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11): + IP_BIND_ADDRESS_NO_PORT: Final[int] if sys.version_info >= (3, 12): - IP_ADD_SOURCE_MEMBERSHIP: int - IP_BLOCK_SOURCE: int - IP_DROP_SOURCE_MEMBERSHIP: int - IP_PKTINFO: int - IP_UNBLOCK_SOURCE: int - -IPV6_CHECKSUM: int -IPV6_JOIN_GROUP: int -IPV6_LEAVE_GROUP: int -IPV6_MULTICAST_HOPS: int -IPV6_MULTICAST_IF: int -IPV6_MULTICAST_LOOP: int -IPV6_RECVTCLASS: int -IPV6_TCLASS: int -IPV6_UNICAST_HOPS: int -IPV6_V6ONLY: int -if sys.version_info >= (3, 9) or sys.platform != "darwin": - IPV6_DONTFRAG: int - IPV6_HOPLIMIT: int - IPV6_HOPOPTS: int - IPV6_PKTINFO: int - IPV6_RECVRTHDR: int - IPV6_RTHDR: int -if sys.platform != "win32": - IPV6_RTHDR_TYPE_0: int - if sys.version_info >= (3, 9) or sys.platform != "darwin": - IPV6_DSTOPTS: int - IPV6_NEXTHOP: int - IPV6_PATHMTU: int - IPV6_RECVDSTOPTS: int - IPV6_RECVHOPLIMIT: int - IPV6_RECVHOPOPTS: int - IPV6_RECVPATHMTU: int - IPV6_RECVPKTINFO: int - IPV6_RTHDRDSTOPTS: int - IPV6_USE_MIN_MTU: int - -EAI_AGAIN: int -EAI_BADFLAGS: int -EAI_FAIL: int -EAI_FAMILY: int -EAI_MEMORY: int -EAI_NODATA: int -EAI_NONAME: int -EAI_SERVICE: int -EAI_SOCKTYPE: int + IP_ADD_SOURCE_MEMBERSHIP: Final[int] + IP_BLOCK_SOURCE: Final[int] + IP_DROP_SOURCE_MEMBERSHIP: Final[int] + IP_PKTINFO: Final[int] + IP_UNBLOCK_SOURCE: Final[int] + +IPV6_CHECKSUM: Final[int] +IPV6_JOIN_GROUP: Final[int] +IPV6_LEAVE_GROUP: Final[int] +IPV6_MULTICAST_HOPS: Final[int] +IPV6_MULTICAST_IF: Final[int] +IPV6_MULTICAST_LOOP: Final[int] +IPV6_RECVTCLASS: Final[int] +IPV6_TCLASS: Final[int] +IPV6_UNICAST_HOPS: Final[int] +IPV6_V6ONLY: Final[int] +IPV6_DONTFRAG: Final[int] +IPV6_HOPLIMIT: Final[int] +IPV6_HOPOPTS: Final[int] +IPV6_PKTINFO: Final[int] +IPV6_RECVRTHDR: Final[int] +IPV6_RTHDR: Final[int] if sys.platform != "win32": - EAI_ADDRFAMILY: int - EAI_BADHINTS: int - EAI_MAX: int - EAI_OVERFLOW: int - EAI_PROTOCOL: int - EAI_SYSTEM: int - -AI_ADDRCONFIG: int -AI_ALL: int -AI_CANONNAME: int -AI_NUMERICHOST: int -AI_NUMERICSERV: int -AI_PASSIVE: int -AI_V4MAPPED: int + IPV6_RTHDR_TYPE_0: Final[int] + IPV6_DSTOPTS: Final[int] + IPV6_NEXTHOP: Final[int] + IPV6_PATHMTU: Final[int] + IPV6_RECVDSTOPTS: Final[int] + IPV6_RECVHOPLIMIT: Final[int] + IPV6_RECVHOPOPTS: Final[int] + IPV6_RECVPATHMTU: Final[int] + IPV6_RECVPKTINFO: Final[int] + IPV6_RTHDRDSTOPTS: Final[int] + +if sys.platform != "win32" and sys.platform != "linux": + IPV6_USE_MIN_MTU: Final[int] + +EAI_AGAIN: Final[int] +EAI_BADFLAGS: Final[int] +EAI_FAIL: Final[int] +EAI_FAMILY: Final[int] +EAI_MEMORY: Final[int] +EAI_NODATA: Final[int] +EAI_NONAME: Final[int] +EAI_SERVICE: Final[int] +EAI_SOCKTYPE: Final[int] if sys.platform != "win32": - AI_DEFAULT: int - AI_MASK: int - AI_V4MAPPED_CFG: int - -NI_DGRAM: int -NI_MAXHOST: int -NI_MAXSERV: int -NI_NAMEREQD: int -NI_NOFQDN: int -NI_NUMERICHOST: int -NI_NUMERICSERV: int - -TCP_FASTOPEN: int -TCP_KEEPCNT: int -TCP_KEEPINTVL: int -TCP_MAXSEG: int -TCP_NODELAY: int + EAI_ADDRFAMILY: Final[int] + EAI_OVERFLOW: Final[int] + EAI_SYSTEM: Final[int] +if sys.platform != "win32" and sys.platform != "linux": + EAI_BADHINTS: Final[int] + EAI_MAX: Final[int] + EAI_PROTOCOL: Final[int] + +AI_ADDRCONFIG: Final[int] +AI_ALL: Final[int] +AI_CANONNAME: Final[int] +AI_NUMERICHOST: Final[int] +AI_NUMERICSERV: Final[int] +AI_PASSIVE: Final[int] +AI_V4MAPPED: Final[int] +if sys.platform != "win32" and sys.platform != "linux": + AI_DEFAULT: Final[int] + AI_MASK: Final[int] + AI_V4MAPPED_CFG: Final[int] + +NI_DGRAM: Final[int] +NI_MAXHOST: Final[int] +NI_MAXSERV: Final[int] +NI_NAMEREQD: Final[int] +NI_NOFQDN: Final[int] +NI_NUMERICHOST: Final[int] +NI_NUMERICSERV: Final[int] +if sys.platform == "linux" and sys.version_info >= (3, 13): + NI_IDN: Final[int] + +TCP_FASTOPEN: Final[int] +TCP_KEEPCNT: Final[int] +TCP_KEEPINTVL: Final[int] +TCP_MAXSEG: Final[int] +TCP_NODELAY: Final[int] if sys.platform != "win32": - TCP_NOTSENT_LOWAT: int + TCP_NOTSENT_LOWAT: Final[int] if sys.platform != "darwin": - TCP_KEEPIDLE: int + TCP_KEEPIDLE: Final[int] if sys.version_info >= (3, 10) and sys.platform == "darwin": - TCP_KEEPALIVE: int + TCP_KEEPALIVE: Final[int] if sys.version_info >= (3, 11) and sys.platform == "darwin": - TCP_CONNECTION_INFO: int + TCP_CONNECTION_INFO: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - TCP_CONGESTION: int - TCP_CORK: int - TCP_DEFER_ACCEPT: int - TCP_INFO: int - TCP_LINGER2: int - TCP_QUICKACK: int - TCP_SYNCNT: int - TCP_USER_TIMEOUT: int - TCP_WINDOW_CLAMP: int + TCP_CONGESTION: Final[int] + TCP_CORK: Final[int] + TCP_DEFER_ACCEPT: Final[int] + TCP_INFO: Final[int] + TCP_LINGER2: Final[int] + TCP_QUICKACK: Final[int] + TCP_SYNCNT: Final[int] + TCP_USER_TIMEOUT: Final[int] + TCP_WINDOW_CLAMP: Final[int] +if sys.platform == "linux" and sys.version_info >= (3, 12): + TCP_CC_INFO: Final[int] + TCP_FASTOPEN_CONNECT: Final[int] + TCP_FASTOPEN_KEY: Final[int] + TCP_FASTOPEN_NO_COOKIE: Final[int] + TCP_INQ: Final[int] + TCP_MD5SIG: Final[int] + TCP_MD5SIG_EXT: Final[int] + TCP_QUEUE_SEQ: Final[int] + TCP_REPAIR: Final[int] + TCP_REPAIR_OPTIONS: Final[int] + TCP_REPAIR_QUEUE: Final[int] + TCP_REPAIR_WINDOW: Final[int] + TCP_SAVED_SYN: Final[int] + TCP_SAVE_SYN: Final[int] + TCP_THIN_DUPACK: Final[int] + TCP_THIN_LINEAR_TIMEOUTS: Final[int] + TCP_TIMESTAMP: Final[int] + TCP_TX_DELAY: Final[int] + TCP_ULP: Final[int] + TCP_ZEROCOPY_RECEIVE: Final[int] # -------------------- # Specifically documented constants @@ -324,251 +386,250 @@ if sys.platform != "win32" and sys.platform != "darwin": if sys.platform == "linux": # Availability: Linux >= 2.6.25, NetBSD >= 8 - AF_CAN: int - PF_CAN: int - SOL_CAN_BASE: int - SOL_CAN_RAW: int - CAN_EFF_FLAG: int - CAN_EFF_MASK: int - CAN_ERR_FLAG: int - CAN_ERR_MASK: int - CAN_RAW: int - CAN_RAW_ERR_FILTER: int - CAN_RAW_FILTER: int - CAN_RAW_LOOPBACK: int - CAN_RAW_RECV_OWN_MSGS: int - CAN_RTR_FLAG: int - CAN_SFF_MASK: int + AF_CAN: Final[int] + PF_CAN: Final[int] + SOL_CAN_BASE: Final[int] + SOL_CAN_RAW: Final[int] + CAN_EFF_FLAG: Final[int] + CAN_EFF_MASK: Final[int] + CAN_ERR_FLAG: Final[int] + CAN_ERR_MASK: Final[int] + CAN_RAW: Final[int] + CAN_RAW_FILTER: Final[int] + CAN_RAW_LOOPBACK: Final[int] + CAN_RAW_RECV_OWN_MSGS: Final[int] + CAN_RTR_FLAG: Final[int] + CAN_SFF_MASK: Final[int] + if sys.version_info < (3, 11): + CAN_RAW_ERR_FILTER: Final[int] if sys.platform == "linux": # Availability: Linux >= 2.6.25 - CAN_BCM: int - CAN_BCM_TX_SETUP: int - CAN_BCM_TX_DELETE: int - CAN_BCM_TX_READ: int - CAN_BCM_TX_SEND: int - CAN_BCM_RX_SETUP: int - CAN_BCM_RX_DELETE: int - CAN_BCM_RX_READ: int - CAN_BCM_TX_STATUS: int - CAN_BCM_TX_EXPIRED: int - CAN_BCM_RX_STATUS: int - CAN_BCM_RX_TIMEOUT: int - CAN_BCM_RX_CHANGED: int - CAN_BCM_SETTIMER: int - CAN_BCM_STARTTIMER: int - CAN_BCM_TX_COUNTEVT: int - CAN_BCM_TX_ANNOUNCE: int - CAN_BCM_TX_CP_CAN_ID: int - CAN_BCM_RX_FILTER_ID: int - CAN_BCM_RX_CHECK_DLC: int - CAN_BCM_RX_NO_AUTOTIMER: int - CAN_BCM_RX_ANNOUNCE_RESUME: int - CAN_BCM_TX_RESET_MULTI_IDX: int - CAN_BCM_RX_RTR_FRAME: int - CAN_BCM_CAN_FD_FRAME: int + CAN_BCM: Final[int] + CAN_BCM_TX_SETUP: Final[int] + CAN_BCM_TX_DELETE: Final[int] + CAN_BCM_TX_READ: Final[int] + CAN_BCM_TX_SEND: Final[int] + CAN_BCM_RX_SETUP: Final[int] + CAN_BCM_RX_DELETE: Final[int] + CAN_BCM_RX_READ: Final[int] + CAN_BCM_TX_STATUS: Final[int] + CAN_BCM_TX_EXPIRED: Final[int] + CAN_BCM_RX_STATUS: Final[int] + CAN_BCM_RX_TIMEOUT: Final[int] + CAN_BCM_RX_CHANGED: Final[int] + CAN_BCM_SETTIMER: Final[int] + CAN_BCM_STARTTIMER: Final[int] + CAN_BCM_TX_COUNTEVT: Final[int] + CAN_BCM_TX_ANNOUNCE: Final[int] + CAN_BCM_TX_CP_CAN_ID: Final[int] + CAN_BCM_RX_FILTER_ID: Final[int] + CAN_BCM_RX_CHECK_DLC: Final[int] + CAN_BCM_RX_NO_AUTOTIMER: Final[int] + CAN_BCM_RX_ANNOUNCE_RESUME: Final[int] + CAN_BCM_TX_RESET_MULTI_IDX: Final[int] + CAN_BCM_RX_RTR_FRAME: Final[int] + CAN_BCM_CAN_FD_FRAME: Final[int] if sys.platform == "linux": # Availability: Linux >= 3.6 - CAN_RAW_FD_FRAMES: int - -if sys.platform == "linux" and sys.version_info >= (3, 9): + CAN_RAW_FD_FRAMES: Final[int] # Availability: Linux >= 4.1 - CAN_RAW_JOIN_FILTERS: int - -if sys.platform == "linux": + CAN_RAW_JOIN_FILTERS: Final[int] # Availability: Linux >= 2.6.25 - CAN_ISOTP: int - -if sys.platform == "linux" and sys.version_info >= (3, 9): + CAN_ISOTP: Final[int] # Availability: Linux >= 5.4 - CAN_J1939: int - - J1939_MAX_UNICAST_ADDR: int - J1939_IDLE_ADDR: int - J1939_NO_ADDR: int - J1939_NO_NAME: int - J1939_PGN_REQUEST: int - J1939_PGN_ADDRESS_CLAIMED: int - J1939_PGN_ADDRESS_COMMANDED: int - J1939_PGN_PDU1_MAX: int - J1939_PGN_MAX: int - J1939_NO_PGN: int - - SO_J1939_FILTER: int - SO_J1939_PROMISC: int - SO_J1939_SEND_PRIO: int - SO_J1939_ERRQUEUE: int - - SCM_J1939_DEST_ADDR: int - SCM_J1939_DEST_NAME: int - SCM_J1939_PRIO: int - SCM_J1939_ERRQUEUE: int - - J1939_NLA_PAD: int - J1939_NLA_BYTES_ACKED: int - J1939_EE_INFO_NONE: int - J1939_EE_INFO_TX_ABORT: int - J1939_FILTER_MAX: int + CAN_J1939: Final[int] + + J1939_MAX_UNICAST_ADDR: Final[int] + J1939_IDLE_ADDR: Final[int] + J1939_NO_ADDR: Final[int] + J1939_NO_NAME: Final[int] + J1939_PGN_REQUEST: Final[int] + J1939_PGN_ADDRESS_CLAIMED: Final[int] + J1939_PGN_ADDRESS_COMMANDED: Final[int] + J1939_PGN_PDU1_MAX: Final[int] + J1939_PGN_MAX: Final[int] + J1939_NO_PGN: Final[int] + + SO_J1939_FILTER: Final[int] + SO_J1939_PROMISC: Final[int] + SO_J1939_SEND_PRIO: Final[int] + SO_J1939_ERRQUEUE: Final[int] + + SCM_J1939_DEST_ADDR: Final[int] + SCM_J1939_DEST_NAME: Final[int] + SCM_J1939_PRIO: Final[int] + SCM_J1939_ERRQUEUE: Final[int] + + J1939_NLA_PAD: Final[int] + J1939_NLA_BYTES_ACKED: Final[int] + J1939_EE_INFO_NONE: Final[int] + J1939_EE_INFO_TX_ABORT: Final[int] + J1939_FILTER_MAX: Final[int] if sys.version_info >= (3, 12) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": # Availability: FreeBSD >= 14.0 - AF_DIVERT: int - PF_DIVERT: int + AF_DIVERT: Final[int] + PF_DIVERT: Final[int] if sys.platform == "linux": # Availability: Linux >= 2.2 - AF_PACKET: int - PF_PACKET: int - PACKET_BROADCAST: int - PACKET_FASTROUTE: int - PACKET_HOST: int - PACKET_LOOPBACK: int - PACKET_MULTICAST: int - PACKET_OTHERHOST: int - PACKET_OUTGOING: int + AF_PACKET: Final[int] + PF_PACKET: Final[int] + PACKET_BROADCAST: Final[int] + PACKET_FASTROUTE: Final[int] + PACKET_HOST: Final[int] + PACKET_LOOPBACK: Final[int] + PACKET_MULTICAST: Final[int] + PACKET_OTHERHOST: Final[int] + PACKET_OUTGOING: Final[int] if sys.version_info >= (3, 12) and sys.platform == "linux": - ETH_P_ALL: int + ETH_P_ALL: Final[int] if sys.platform == "linux": # Availability: Linux >= 2.6.30 - AF_RDS: int - PF_RDS: int - SOL_RDS: int - RDS_CANCEL_SENT_TO: int - RDS_CMSG_RDMA_ARGS: int - RDS_CMSG_RDMA_DEST: int - RDS_CMSG_RDMA_MAP: int - RDS_CMSG_RDMA_STATUS: int - RDS_CMSG_RDMA_UPDATE: int - RDS_CONG_MONITOR: int - RDS_FREE_MR: int - RDS_GET_MR: int - RDS_GET_MR_FOR_DEST: int - RDS_RDMA_DONTWAIT: int - RDS_RDMA_FENCE: int - RDS_RDMA_INVALIDATE: int - RDS_RDMA_NOTIFY_ME: int - RDS_RDMA_READWRITE: int - RDS_RDMA_SILENT: int - RDS_RDMA_USE_ONCE: int - RDS_RECVERR: int + AF_RDS: Final[int] + PF_RDS: Final[int] + SOL_RDS: Final[int] + # These are present in include/linux/rds.h but don't always show up + # here. + RDS_CANCEL_SENT_TO: Final[int] + RDS_CMSG_RDMA_ARGS: Final[int] + RDS_CMSG_RDMA_DEST: Final[int] + RDS_CMSG_RDMA_MAP: Final[int] + RDS_CMSG_RDMA_STATUS: Final[int] + RDS_CONG_MONITOR: Final[int] + RDS_FREE_MR: Final[int] + RDS_GET_MR: Final[int] + RDS_GET_MR_FOR_DEST: Final[int] + RDS_RDMA_DONTWAIT: Final[int] + RDS_RDMA_FENCE: Final[int] + RDS_RDMA_INVALIDATE: Final[int] + RDS_RDMA_NOTIFY_ME: Final[int] + RDS_RDMA_READWRITE: Final[int] + RDS_RDMA_SILENT: Final[int] + RDS_RDMA_USE_ONCE: Final[int] + RDS_RECVERR: Final[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: Final[int] if sys.platform == "win32": - SIO_RCVALL: int - SIO_KEEPALIVE_VALS: int - SIO_LOOPBACK_FAST_PATH: int - RCVALL_MAX: int - RCVALL_OFF: int - RCVALL_ON: int - RCVALL_SOCKETLEVELONLY: int + SIO_RCVALL: Final[int] + SIO_KEEPALIVE_VALS: Final[int] + SIO_LOOPBACK_FAST_PATH: Final[int] + RCVALL_MAX: Final[int] + RCVALL_OFF: Final[int] + RCVALL_ON: Final[int] + RCVALL_SOCKETLEVELONLY: Final[int] if sys.platform == "linux": - AF_TIPC: int - SOL_TIPC: int - TIPC_ADDR_ID: int - TIPC_ADDR_NAME: int - TIPC_ADDR_NAMESEQ: int - TIPC_CFG_SRV: int - TIPC_CLUSTER_SCOPE: int - TIPC_CONN_TIMEOUT: int - TIPC_CRITICAL_IMPORTANCE: int - TIPC_DEST_DROPPABLE: int - TIPC_HIGH_IMPORTANCE: int - TIPC_IMPORTANCE: int - TIPC_LOW_IMPORTANCE: int - TIPC_MEDIUM_IMPORTANCE: int - TIPC_NODE_SCOPE: int - TIPC_PUBLISHED: int - TIPC_SRC_DROPPABLE: int - TIPC_SUBSCR_TIMEOUT: int - TIPC_SUB_CANCEL: int - TIPC_SUB_PORTS: int - TIPC_SUB_SERVICE: int - TIPC_TOP_SRV: int - TIPC_WAIT_FOREVER: int - TIPC_WITHDRAWN: int - TIPC_ZONE_SCOPE: int + AF_TIPC: Final[int] + SOL_TIPC: Final[int] + TIPC_ADDR_ID: Final[int] + TIPC_ADDR_NAME: Final[int] + TIPC_ADDR_NAMESEQ: Final[int] + TIPC_CFG_SRV: Final[int] + TIPC_CLUSTER_SCOPE: Final[int] + TIPC_CONN_TIMEOUT: Final[int] + TIPC_CRITICAL_IMPORTANCE: Final[int] + TIPC_DEST_DROPPABLE: Final[int] + TIPC_HIGH_IMPORTANCE: Final[int] + TIPC_IMPORTANCE: Final[int] + TIPC_LOW_IMPORTANCE: Final[int] + TIPC_MEDIUM_IMPORTANCE: Final[int] + TIPC_NODE_SCOPE: Final[int] + TIPC_PUBLISHED: Final[int] + TIPC_SRC_DROPPABLE: Final[int] + TIPC_SUBSCR_TIMEOUT: Final[int] + TIPC_SUB_CANCEL: Final[int] + TIPC_SUB_PORTS: Final[int] + TIPC_SUB_SERVICE: Final[int] + TIPC_TOP_SRV: Final[int] + TIPC_WAIT_FOREVER: Final[int] + TIPC_WITHDRAWN: Final[int] + TIPC_ZONE_SCOPE: Final[int] if sys.platform == "linux": # Availability: Linux >= 2.6.38 - AF_ALG: int - SOL_ALG: int - ALG_OP_DECRYPT: int - ALG_OP_ENCRYPT: int - ALG_OP_SIGN: int - ALG_OP_VERIFY: int - ALG_SET_AEAD_ASSOCLEN: int - ALG_SET_AEAD_AUTHSIZE: int - ALG_SET_IV: int - ALG_SET_KEY: int - ALG_SET_OP: int - ALG_SET_PUBKEY: int + AF_ALG: Final[int] + SOL_ALG: Final[int] + ALG_OP_DECRYPT: Final[int] + ALG_OP_ENCRYPT: Final[int] + ALG_OP_SIGN: Final[int] + ALG_OP_VERIFY: Final[int] + ALG_SET_AEAD_ASSOCLEN: Final[int] + ALG_SET_AEAD_AUTHSIZE: Final[int] + ALG_SET_IV: Final[int] + ALG_SET_KEY: Final[int] + ALG_SET_OP: Final[int] + ALG_SET_PUBKEY: Final[int] if sys.platform == "linux": # Availability: Linux >= 4.8 (or maybe 3.9, CPython docs are confusing) - AF_VSOCK: int - IOCTL_VM_SOCKETS_GET_LOCAL_CID: int - VMADDR_CID_ANY: int - VMADDR_CID_HOST: int - VMADDR_PORT_ANY: int - SO_VM_SOCKETS_BUFFER_MAX_SIZE: int - SO_VM_SOCKETS_BUFFER_SIZE: int - SO_VM_SOCKETS_BUFFER_MIN_SIZE: int - VM_SOCKETS_INVALID_VERSION: int # undocumented - -if sys.platform != "win32" or sys.version_info >= (3, 9): - # Documented as only available on BSD, macOS, but empirically sometimes - # available on Windows - AF_LINK: int + AF_VSOCK: Final[int] + IOCTL_VM_SOCKETS_GET_LOCAL_CID: Final = 0x7B9 + VMADDR_CID_ANY: Final = 0xFFFFFFFF + VMADDR_CID_HOST: Final = 2 + VMADDR_PORT_ANY: Final = 0xFFFFFFFF + SO_VM_SOCKETS_BUFFER_MAX_SIZE: Final = 2 + SO_VM_SOCKETS_BUFFER_SIZE: Final = 0 + SO_VM_SOCKETS_BUFFER_MIN_SIZE: Final = 1 + VM_SOCKETS_INVALID_VERSION: Final = 0xFFFFFFFF # undocumented + +# Documented as only available on BSD, macOS, but empirically sometimes +# available on Windows +if sys.platform != "linux": + AF_LINK: Final[int] has_ipv6: bool -if sys.platform != "darwin": - if sys.platform != "win32" or sys.version_info >= (3, 9): - BDADDR_ANY: str - BDADDR_LOCAL: str +if sys.platform != "darwin" and sys.platform != "linux": + BDADDR_ANY: Final = "00:00:00:00:00:00" + BDADDR_LOCAL: Final = "00:00:00:FF:FF:FF" -if sys.platform != "win32" and sys.platform != "darwin": - HCI_FILTER: int # not in NetBSD or DragonFlyBSD - HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD - HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + HCI_FILTER: Final[int] # not in NetBSD or DragonFlyBSD + HCI_TIME_STAMP: Final[int] # not in FreeBSD, NetBSD, or DragonFlyBSD + HCI_DATA_DIR: Final[int] # not in FreeBSD, NetBSD, or DragonFlyBSD if sys.platform == "linux": - AF_QIPCRTR: int # Availability: Linux >= 4.7 + AF_QIPCRTR: Final[int] # Availability: Linux >= 4.7 if sys.version_info >= (3, 11) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": # FreeBSD - SCM_CREDS2: int - LOCAL_CREDS: int - LOCAL_CREDS_PERSISTENT: int + SCM_CREDS2: Final[int] + LOCAL_CREDS: Final[int] + LOCAL_CREDS_PERSISTENT: Final[int] if sys.version_info >= (3, 11) and sys.platform == "linux": - SO_INCOMING_CPU: int # Availability: Linux >= 3.9 + SO_INCOMING_CPU: Final[int] # Availability: Linux >= 3.9 if sys.version_info >= (3, 12) and sys.platform == "win32": # Availability: Windows - AF_HYPERV: int - HV_PROTOCOL_RAW: int - HVSOCKET_CONNECT_TIMEOUT: int - HVSOCKET_CONNECT_TIMEOUT_MAX: int - HVSOCKET_CONNECTED_SUSPEND: int - HVSOCKET_ADDRESS_FLAG_PASSTHRU: int - HV_GUID_ZERO: str - HV_GUID_WILDCARD: str - HV_GUID_BROADCAST: str - HV_GUID_CHILDREN: str - HV_GUID_LOOPBACK: str - HV_GUID_PARENT: str + AF_HYPERV: Final[int] + HV_PROTOCOL_RAW: Final[int] + HVSOCKET_CONNECT_TIMEOUT: Final[int] + HVSOCKET_CONNECT_TIMEOUT_MAX: Final[int] + HVSOCKET_CONNECTED_SUSPEND: Final[int] + HVSOCKET_ADDRESS_FLAG_PASSTHRU: Final[int] + HV_GUID_ZERO: Final = "00000000-0000-0000-0000-000000000000" + HV_GUID_WILDCARD: Final = "00000000-0000-0000-0000-000000000000" + HV_GUID_BROADCAST: Final = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" + HV_GUID_CHILDREN: Final = "90DB8B89-0D35-4F79-8CE9-49EA0AC8B7CD" + HV_GUID_LOOPBACK: Final = "E0E16197-DD56-4A10-9195-5EE7A155A838" + HV_GUID_PARENT: Final = "A42E7CDA-D03F-480C-9CC2-A4DE20ABB878" if sys.version_info >= (3, 12): if sys.platform != "win32": # Availability: Linux, FreeBSD, macOS - ETHERTYPE_ARP: int - ETHERTYPE_IP: int - ETHERTYPE_IPV6: int - ETHERTYPE_VLAN: int + ETHERTYPE_ARP: Final[int] + ETHERTYPE_IP: Final[int] + ETHERTYPE_IPV6: Final[int] + ETHERTYPE_VLAN: Final[int] # -------------------- # Semi-documented constants @@ -578,108 +639,99 @@ 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 + AF_NETLINK: Final[int] + NETLINK_CRYPTO: Final[int] + NETLINK_DNRTMSG: Final[int] + NETLINK_FIREWALL: Final[int] + NETLINK_IP6_FW: Final[int] + NETLINK_NFLOG: Final[int] + NETLINK_ROUTE: Final[int] + NETLINK_USERSOCK: Final[int] + NETLINK_XFRM: Final[int] + # Technically still supported by CPython + # NETLINK_ARPD: Final[int] # linux 2.0 to 2.6.12 (EOL August 2005) + # NETLINK_ROUTE6: Final[int] # linux 2.2 to 2.6.12 (EOL August 2005) + # NETLINK_SKIP: Final[int] # linux 2.0 to 2.6.12 (EOL August 2005) + # NETLINK_TAPBASE: Final[int] # linux 2.2 to 2.6.12 (EOL August 2005) + # NETLINK_TCPDIAG: Final[int] # linux 2.6.0 to 2.6.13 (EOL December 2005) + # NETLINK_W1: Final[int] # linux 2.6.13 to 2.6.17 (EOL October 2006) if sys.platform == "darwin": - PF_SYSTEM: int - SYSPROTO_CONTROL: int + PF_SYSTEM: Final[int] + SYSPROTO_CONTROL: Final[int] -if sys.platform != "darwin": - if sys.version_info >= (3, 9) or sys.platform != "win32": - AF_BLUETOOTH: int +if sys.platform != "darwin" and sys.platform != "linux": + AF_BLUETOOTH: Final[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.version_info >= (3, 9) or sys.platform != "win32": - BTPROTO_RFCOMM: int + BTPROTO_HCI: Final[int] + BTPROTO_L2CAP: Final[int] + BTPROTO_SCO: Final[int] # not in FreeBSD +if sys.platform != "darwin" and sys.platform != "linux": + BTPROTO_RFCOMM: Final[int] -if sys.version_info >= (3, 9) and sys.platform == "linux": - UDPLITE_RECV_CSCOV: int - UDPLITE_SEND_CSCOV: int +if sys.platform == "linux": + UDPLITE_RECV_CSCOV: Final[int] + UDPLITE_SEND_CSCOV: Final[int] # -------------------- # Documented under socket.shutdown # -------------------- -SHUT_RD: int -SHUT_RDWR: int -SHUT_WR: int +SHUT_RD: Final[int] +SHUT_RDWR: Final[int] +SHUT_WR: Final[int] # -------------------- # Undocumented constants # -------------------- # Undocumented address families -AF_APPLETALK: int -AF_DECnet: int -AF_IPX: int -AF_SNA: int +AF_APPLETALK: Final[int] +AF_DECnet: Final[int] +AF_IPX: Final[int] +AF_SNA: Final[int] if sys.platform != "win32": - AF_ROUTE: int - AF_SYSTEM: int + AF_ROUTE: Final[int] + +if sys.platform == "darwin": + AF_SYSTEM: Final[int] if sys.platform != "darwin": - AF_IRDA: int + AF_IRDA: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5: int - AF_ASH: int - AF_ATMPVC: int - AF_ATMSVC: int - AF_AX25: int - AF_BRIDGE: int - AF_ECONET: int - AF_KEY: int - AF_LLC: int - AF_NETBEUI: int - AF_NETROM: int - AF_PPPOX: int - AF_ROSE: int - AF_SECURITY: int - AF_WANPIPE: int - AF_X25: int + AF_ASH: Final[int] + AF_ATMPVC: Final[int] + AF_ATMSVC: Final[int] + AF_AX25: Final[int] + AF_BRIDGE: Final[int] + AF_ECONET: Final[int] + AF_KEY: Final[int] + AF_LLC: Final[int] + AF_NETBEUI: Final[int] + AF_NETROM: Final[int] + AF_PPPOX: Final[int] + AF_ROSE: Final[int] + AF_SECURITY: Final[int] + AF_WANPIPE: Final[int] + AF_X25: Final[int] # Miscellaneous undocumented -if sys.platform != "win32": - LOCAL_PEERCRED: int +if sys.platform != "win32" and sys.platform != "linux": + LOCAL_PEERCRED: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - IPX_TYPE: int - -# ===== Exceptions ===== - -error = OSError - -class herror(error): ... -class gaierror(error): ... - -if sys.version_info >= (3, 10): - timeout = TimeoutError -else: - class timeout(error): ... + # Defined in linux socket.h, but this isn't always present for + # some reason. + IPX_TYPE: Final[int] # ===== Classes ===== +@disjoint_base class socket: @property def family(self) -> int: ... @@ -687,14 +739,15 @@ 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 = ... + self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = None ) -> None: ... else: - def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = ...) -> None: ... + def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = None) -> None: ... def bind(self, address: _Address, /) -> None: ... def close(self) -> None: ... @@ -714,18 +767,18 @@ class socket: def ioctl(self, control: int, option: int | tuple[int, int, int] | bool, /) -> None: ... def listen(self, backlog: int = ..., /) -> None: ... - def recv(self, bufsize: int, flags: int = ..., /) -> bytes: ... - def recvfrom(self, bufsize: int, flags: int = ..., /) -> tuple[bytes, _RetAddress]: ... + def recv(self, bufsize: int, flags: int = 0, /) -> bytes: ... + def recvfrom(self, bufsize: int, flags: int = 0, /) -> tuple[bytes, _RetAddress]: ... if sys.platform != "win32": - def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ... + def recvmsg(self, bufsize: int, ancbufsize: int = 0, flags: int = 0, /) -> tuple[bytes, list[_CMSG], int, Any]: ... def recvmsg_into( - self, buffers: Iterable[WriteableBuffer], ancbufsize: int = ..., flags: int = ..., / + self, buffers: Iterable[WriteableBuffer], ancbufsize: int = 0, flags: int = 0, / ) -> tuple[int, list[_CMSG], int, Any]: ... - def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ... - def recv_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> int: ... - def send(self, data: ReadableBuffer, flags: int = ..., /) -> int: ... - def sendall(self, data: ReadableBuffer, flags: int = ..., /) -> None: ... + def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int = 0, flags: int = 0) -> tuple[int, _RetAddress]: ... + def recv_into(self, buffer: WriteableBuffer, nbytes: int = 0, flags: int = 0) -> int: ... + def send(self, data: ReadableBuffer, flags: int = 0, /) -> int: ... + def sendall(self, data: ReadableBuffer, flags: int = 0, /) -> None: ... @overload def sendto(self, data: ReadableBuffer, address: _Address, /) -> int: ... @overload @@ -735,13 +788,13 @@ class socket: self, buffers: Iterable[ReadableBuffer], ancdata: Iterable[_CMSGArg] = ..., - flags: int = ..., - address: _Address | None = ..., + flags: int = 0, + address: _Address | None = None, /, ) -> int: ... if sys.platform == "linux": def sendmsg_afalg( - self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ... + self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = 0 ) -> int: ... def setblocking(self, flag: bool, /) -> None: ... @@ -764,18 +817,13 @@ def dup(fd: SupportsIndex, /) -> int: ... # the 5th tuple item is an address def getaddrinfo( - host: bytes | str | None, - port: bytes | str | int | None, - family: int = ..., - type: int = ..., - proto: int = ..., - flags: int = ..., -) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ... + host: bytes | str | None, port: bytes | str | int | None, family: int = ..., type: int = 0, proto: int = 0, flags: int = 0 +) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]]]: ... def gethostbyname(hostname: str, /) -> str: ... def gethostbyname_ex(hostname: str, /) -> tuple[str, list[str], list[str]]: ... def gethostname() -> str: ... def gethostbyaddr(ip_address: str, /) -> tuple[str, list[str], list[str]]: ... -def getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int, /) -> tuple[str, str]: ... +def getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], flags: int, /) -> tuple[str, str]: ... def getprotobyname(protocolname: str, /) -> int: ... def getservbyname(servicename: str, protocolname: str = ..., /) -> int: ... def getservbyport(port: int, protocolname: str = ..., /) -> str: ... @@ -788,16 +836,23 @@ 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: ... def CMSG_LEN(length: int, /) -> int: ... def CMSG_SPACE(length: int, /) -> int: ... - def socketpair(family: int = ..., type: int = ..., proto: int = ..., /) -> tuple[socket, socket]: ... + def socketpair(family: int = ..., type: int = ..., proto: int = 0, /) -> tuple[socket, socket]: ... def if_nameindex() -> list[tuple[int, str]]: ... def if_nametoindex(oname: str, /) -> int: ... -def if_indextoname(index: int, /) -> str: ... -CAPI: object +if sys.version_info >= (3, 14): + def if_indextoname(if_index: int, /) -> str: ... + +else: + def if_indextoname(index: int, /) -> str: ... + +CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/_sqlite3.pyi b/mypy/typeshed/stdlib/_sqlite3.pyi new file mode 100644 index 0000000000000..50006dcf4032d --- /dev/null +++ b/mypy/typeshed/stdlib/_sqlite3.pyi @@ -0,0 +1,313 @@ +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, + _IsolationLevel, +) +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 = 2 +PARSE_DECLTYPES: Final = 1 +SQLITE_ALTER_TABLE: Final = 26 +SQLITE_ANALYZE: Final = 28 +SQLITE_ATTACH: Final = 24 +SQLITE_CREATE_INDEX: Final = 1 +SQLITE_CREATE_TABLE: Final = 2 +SQLITE_CREATE_TEMP_INDEX: Final = 3 +SQLITE_CREATE_TEMP_TABLE: Final = 4 +SQLITE_CREATE_TEMP_TRIGGER: Final = 5 +SQLITE_CREATE_TEMP_VIEW: Final = 6 +SQLITE_CREATE_TRIGGER: Final = 7 +SQLITE_CREATE_VIEW: Final = 8 +SQLITE_CREATE_VTABLE: Final = 29 +SQLITE_DELETE: Final = 9 +SQLITE_DENY: Final = 1 +SQLITE_DETACH: Final = 25 +SQLITE_DONE: Final = 101 +SQLITE_DROP_INDEX: Final = 10 +SQLITE_DROP_TABLE: Final = 11 +SQLITE_DROP_TEMP_INDEX: Final = 12 +SQLITE_DROP_TEMP_TABLE: Final = 13 +SQLITE_DROP_TEMP_TRIGGER: Final = 14 +SQLITE_DROP_TEMP_VIEW: Final = 15 +SQLITE_DROP_TRIGGER: Final = 16 +SQLITE_DROP_VIEW: Final = 17 +SQLITE_DROP_VTABLE: Final = 30 +SQLITE_FUNCTION: Final = 31 +SQLITE_IGNORE: Final = 2 +SQLITE_INSERT: Final = 18 +SQLITE_OK: Final = 0 +SQLITE_PRAGMA: Final = 19 +SQLITE_READ: Final = 20 +SQLITE_RECURSIVE: Final = 33 +SQLITE_REINDEX: Final = 27 +SQLITE_SAVEPOINT: Final = 32 +SQLITE_SELECT: Final = 21 +SQLITE_TRANSACTION: Final = 22 +SQLITE_UPDATE: Final = 23 +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 = -1 + SQLITE_DBCONFIG_DEFENSIVE: Final = 1010 + SQLITE_DBCONFIG_DQS_DDL: Final = 1014 + SQLITE_DBCONFIG_DQS_DML: Final = 1013 + SQLITE_DBCONFIG_ENABLE_FKEY: Final = 1002 + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final = 1004 + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final = 1005 + SQLITE_DBCONFIG_ENABLE_QPSG: Final = 1007 + SQLITE_DBCONFIG_ENABLE_TRIGGER: Final = 1003 + SQLITE_DBCONFIG_ENABLE_VIEW: Final = 1015 + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final = 1012 + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final = 1016 + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final = 1006 + SQLITE_DBCONFIG_RESET_DATABASE: Final = 1009 + SQLITE_DBCONFIG_TRIGGER_EQP: Final = 1008 + SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final = 1017 + SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final = 1011 + +if sys.version_info >= (3, 11): + SQLITE_ABORT: Final = 4 + SQLITE_ABORT_ROLLBACK: Final = 516 + SQLITE_AUTH: Final = 23 + SQLITE_AUTH_USER: Final = 279 + SQLITE_BUSY: Final = 5 + SQLITE_BUSY_RECOVERY: Final = 261 + SQLITE_BUSY_SNAPSHOT: Final = 517 + SQLITE_BUSY_TIMEOUT: Final = 773 + SQLITE_CANTOPEN: Final = 14 + SQLITE_CANTOPEN_CONVPATH: Final = 1038 + SQLITE_CANTOPEN_DIRTYWAL: Final = 1294 + SQLITE_CANTOPEN_FULLPATH: Final = 782 + SQLITE_CANTOPEN_ISDIR: Final = 526 + SQLITE_CANTOPEN_NOTEMPDIR: Final = 270 + SQLITE_CANTOPEN_SYMLINK: Final = 1550 + SQLITE_CONSTRAINT: Final = 19 + SQLITE_CONSTRAINT_CHECK: Final = 275 + SQLITE_CONSTRAINT_COMMITHOOK: Final = 531 + SQLITE_CONSTRAINT_FOREIGNKEY: Final = 787 + SQLITE_CONSTRAINT_FUNCTION: Final = 1043 + SQLITE_CONSTRAINT_NOTNULL: Final = 1299 + SQLITE_CONSTRAINT_PINNED: Final = 2835 + SQLITE_CONSTRAINT_PRIMARYKEY: Final = 1555 + SQLITE_CONSTRAINT_ROWID: Final = 2579 + SQLITE_CONSTRAINT_TRIGGER: Final = 1811 + SQLITE_CONSTRAINT_UNIQUE: Final = 2067 + SQLITE_CONSTRAINT_VTAB: Final = 2323 + SQLITE_CORRUPT: Final = 11 + SQLITE_CORRUPT_INDEX: Final = 779 + SQLITE_CORRUPT_SEQUENCE: Final = 523 + SQLITE_CORRUPT_VTAB: Final = 267 + SQLITE_EMPTY: Final = 16 + SQLITE_ERROR: Final = 1 + SQLITE_ERROR_MISSING_COLLSEQ: Final = 257 + SQLITE_ERROR_RETRY: Final = 513 + SQLITE_ERROR_SNAPSHOT: Final = 769 + SQLITE_FORMAT: Final = 24 + SQLITE_FULL: Final = 13 + SQLITE_INTERNAL: Final = 2 + SQLITE_INTERRUPT: Final = 9 + SQLITE_IOERR: Final = 10 + SQLITE_IOERR_ACCESS: Final = 3338 + SQLITE_IOERR_AUTH: Final = 7178 + SQLITE_IOERR_BEGIN_ATOMIC: Final = 7434 + SQLITE_IOERR_BLOCKED: Final = 2826 + SQLITE_IOERR_CHECKRESERVEDLOCK: Final = 3594 + SQLITE_IOERR_CLOSE: Final = 4106 + SQLITE_IOERR_COMMIT_ATOMIC: Final = 7690 + SQLITE_IOERR_CONVPATH: Final = 6666 + SQLITE_IOERR_CORRUPTFS: Final = 8458 + SQLITE_IOERR_DATA: Final = 8202 + SQLITE_IOERR_DELETE: Final = 2570 + SQLITE_IOERR_DELETE_NOENT: Final = 5898 + SQLITE_IOERR_DIR_CLOSE: Final = 4362 + SQLITE_IOERR_DIR_FSYNC: Final = 1290 + SQLITE_IOERR_FSTAT: Final = 1802 + SQLITE_IOERR_FSYNC: Final = 1034 + SQLITE_IOERR_GETTEMPPATH: Final = 6410 + SQLITE_IOERR_LOCK: Final = 3850 + SQLITE_IOERR_MMAP: Final = 6154 + SQLITE_IOERR_NOMEM: Final = 3082 + SQLITE_IOERR_RDLOCK: Final = 2314 + SQLITE_IOERR_READ: Final = 266 + SQLITE_IOERR_ROLLBACK_ATOMIC: Final = 7946 + SQLITE_IOERR_SEEK: Final = 5642 + SQLITE_IOERR_SHMLOCK: Final = 5130 + SQLITE_IOERR_SHMMAP: Final = 5386 + SQLITE_IOERR_SHMOPEN: Final = 4618 + SQLITE_IOERR_SHMSIZE: Final = 4874 + SQLITE_IOERR_SHORT_READ: Final = 522 + SQLITE_IOERR_TRUNCATE: Final = 1546 + SQLITE_IOERR_UNLOCK: Final = 2058 + SQLITE_IOERR_VNODE: Final = 6922 + SQLITE_IOERR_WRITE: Final = 778 + SQLITE_LIMIT_ATTACHED: Final = 7 + SQLITE_LIMIT_COLUMN: Final = 22 + SQLITE_LIMIT_COMPOUND_SELECT: Final = 4 + SQLITE_LIMIT_EXPR_DEPTH: Final = 3 + SQLITE_LIMIT_FUNCTION_ARG: Final = 6 + SQLITE_LIMIT_LENGTH: Final = 0 + SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final = 8 + SQLITE_LIMIT_SQL_LENGTH: Final = 1 + SQLITE_LIMIT_TRIGGER_DEPTH: Final = 10 + SQLITE_LIMIT_VARIABLE_NUMBER: Final = 9 + SQLITE_LIMIT_VDBE_OP: Final = 5 + SQLITE_LIMIT_WORKER_THREADS: Final = 11 + SQLITE_LOCKED: Final = 6 + SQLITE_LOCKED_SHAREDCACHE: Final = 262 + SQLITE_LOCKED_VTAB: Final = 518 + SQLITE_MISMATCH: Final = 20 + SQLITE_MISUSE: Final = 21 + SQLITE_NOLFS: Final = 22 + SQLITE_NOMEM: Final = 7 + SQLITE_NOTADB: Final = 26 + SQLITE_NOTFOUND: Final = 12 + SQLITE_NOTICE: Final = 27 + SQLITE_NOTICE_RECOVER_ROLLBACK: Final = 539 + SQLITE_NOTICE_RECOVER_WAL: Final = 283 + SQLITE_OK_LOAD_PERMANENTLY: Final = 256 + SQLITE_OK_SYMLINK: Final = 512 + SQLITE_PERM: Final = 3 + SQLITE_PROTOCOL: Final = 15 + SQLITE_RANGE: Final = 25 + SQLITE_READONLY: Final = 8 + SQLITE_READONLY_CANTINIT: Final = 1288 + SQLITE_READONLY_CANTLOCK: Final = 520 + SQLITE_READONLY_DBMOVED: Final = 1032 + SQLITE_READONLY_DIRECTORY: Final = 1544 + SQLITE_READONLY_RECOVERY: Final = 264 + SQLITE_READONLY_ROLLBACK: Final = 776 + SQLITE_ROW: Final = 100 + SQLITE_SCHEMA: Final = 17 + SQLITE_TOOBIG: Final = 18 + SQLITE_WARNING: Final = 28 + SQLITE_WARNING_AUTOINDEX: Final = 284 + threadsafety: Literal[0, 1, 3] + +# 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: _IsolationLevel = "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: _IsolationLevel, + 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: _IsolationLevel = "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: _IsolationLevel = "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: _IsolationLevel, + 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: _IsolationLevel = "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..73a43f29c8c5f --- /dev/null +++ b/mypy/typeshed/stdlib/_ssl.pyi @@ -0,0 +1,295 @@ +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, ClassVar, Final, Literal, TypedDict, final, overload, type_check_only +from typing_extensions import NotRequired, Self, TypeAlias, deprecated, disjoint_base + +_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] + +@type_check_only +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 + +@type_check_only +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): + @deprecated("Deprecated since Python 3.6; removed in Python 3.12. Use `ssl.RAND_bytes()` instead.") + 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]: ... +@disjoint_base +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: + __hash__: ClassVar[None] # type: ignore[assignment] + @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: Final[str] + +# SSL error numbers +SSL_ERROR_ZERO_RETURN: Final = 6 +SSL_ERROR_WANT_READ: Final = 2 +SSL_ERROR_WANT_WRITE: Final = 3 +SSL_ERROR_WANT_X509_LOOKUP: Final = 4 +SSL_ERROR_SYSCALL: Final = 5 +SSL_ERROR_SSL: Final = 1 +SSL_ERROR_WANT_CONNECT: Final = 7 +SSL_ERROR_EOF: Final = 8 +SSL_ERROR_INVALID_ERROR_CODE: Final = 10 + +# verify modes +CERT_NONE: Final = 0 +CERT_OPTIONAL: Final = 1 +CERT_REQUIRED: Final = 2 + +# verify flags +VERIFY_DEFAULT: Final = 0 +VERIFY_CRL_CHECK_LEAF: Final = 0x4 +VERIFY_CRL_CHECK_CHAIN: Final = 0x8 +VERIFY_X509_STRICT: Final = 0x20 +VERIFY_X509_TRUSTED_FIRST: Final = 0x8000 +if sys.version_info >= (3, 10): + VERIFY_ALLOW_PROXY_CERTS: Final = 0x40 + VERIFY_X509_PARTIAL_CHAIN: Final = 0x80000 + +# alert descriptions +ALERT_DESCRIPTION_CLOSE_NOTIFY: Final = 0 +ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: Final = 10 +ALERT_DESCRIPTION_BAD_RECORD_MAC: Final = 20 +ALERT_DESCRIPTION_RECORD_OVERFLOW: Final = 22 +ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: Final = 30 +ALERT_DESCRIPTION_HANDSHAKE_FAILURE: Final = 40 +ALERT_DESCRIPTION_BAD_CERTIFICATE: Final = 42 +ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: Final = 43 +ALERT_DESCRIPTION_CERTIFICATE_REVOKED: Final = 44 +ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: Final = 45 +ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: Final = 46 +ALERT_DESCRIPTION_ILLEGAL_PARAMETER: Final = 47 +ALERT_DESCRIPTION_UNKNOWN_CA: Final = 48 +ALERT_DESCRIPTION_ACCESS_DENIED: Final = 49 +ALERT_DESCRIPTION_DECODE_ERROR: Final = 50 +ALERT_DESCRIPTION_DECRYPT_ERROR: Final = 51 +ALERT_DESCRIPTION_PROTOCOL_VERSION: Final = 70 +ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: Final = 71 +ALERT_DESCRIPTION_INTERNAL_ERROR: Final = 80 +ALERT_DESCRIPTION_USER_CANCELLED: Final = 90 +ALERT_DESCRIPTION_NO_RENEGOTIATION: Final = 100 +ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: Final = 110 +ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: Final = 111 +ALERT_DESCRIPTION_UNRECOGNIZED_NAME: Final = 112 +ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: Final = 113 +ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: Final = 114 +ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: Final = 115 + +# protocol versions +PROTOCOL_SSLv23: Final = 2 +PROTOCOL_TLS: Final = 2 +PROTOCOL_TLS_CLIENT: Final = 16 +PROTOCOL_TLS_SERVER: Final = 17 +PROTOCOL_TLSv1: Final = 3 +PROTOCOL_TLSv1_1: Final = 4 +PROTOCOL_TLSv1_2: Final = 5 + +# protocol options +OP_ALL: Final = 0x80000050 +OP_NO_SSLv2: Final = 0x0 +OP_NO_SSLv3: Final = 0x2000000 +OP_NO_TLSv1: Final = 0x4000000 +OP_NO_TLSv1_1: Final = 0x10000000 +OP_NO_TLSv1_2: Final = 0x8000000 +OP_NO_TLSv1_3: Final = 0x20000000 +OP_CIPHER_SERVER_PREFERENCE: Final = 0x400000 +OP_SINGLE_DH_USE: Final = 0x0 +OP_NO_TICKET: Final = 0x4000 +OP_SINGLE_ECDH_USE: Final = 0x0 +OP_NO_COMPRESSION: Final = 0x20000 +OP_ENABLE_MIDDLEBOX_COMPAT: Final = 0x100000 +OP_NO_RENEGOTIATION: Final = 0x40000000 +if sys.version_info >= (3, 11) or sys.platform == "linux": + OP_IGNORE_UNEXPECTED_EOF: Final = 0x80 +if sys.version_info >= (3, 12): + OP_LEGACY_SERVER_CONNECT: Final = 0x4 + OP_ENABLE_KTLS: Final = 0x8 + +# host flags +HOSTFLAG_ALWAYS_CHECK_SUBJECT: Final = 0x1 +HOSTFLAG_NEVER_CHECK_SUBJECT: Final = 0x20 +HOSTFLAG_NO_WILDCARDS: Final = 0x2 +HOSTFLAG_NO_PARTIAL_WILDCARDS: Final = 0x4 +HOSTFLAG_MULTI_LABEL_WILDCARDS: Final = 0x8 +HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: Final = 0x10 + +if sys.version_info >= (3, 10): + # certificate file types + ENCODING_PEM: Final = 1 + ENCODING_DER: Final = 2 + +# protocol versions +PROTO_MINIMUM_SUPPORTED: Final = -2 +PROTO_MAXIMUM_SUPPORTED: Final = -1 +PROTO_SSLv3: Final[int] +PROTO_TLSv1: Final[int] +PROTO_TLSv1_1: Final[int] +PROTO_TLSv1_2: Final[int] +PROTO_TLSv1_3: Final[int] + +# feature support +HAS_SNI: Final[bool] +HAS_TLS_UNIQUE: Final[bool] +HAS_ECDH: Final[bool] +HAS_NPN: Final[bool] +if sys.version_info >= (3, 13): + HAS_PSK: Final[bool] +HAS_ALPN: Final[bool] +HAS_SSLv2: Final[bool] +HAS_SSLv3: Final[bool] +HAS_TLSv1: Final[bool] +HAS_TLSv1_1: Final[bool] +HAS_TLSv1_2: Final[bool] +HAS_TLSv1_3: Final[bool] +if sys.version_info >= (3, 14): + HAS_PHA: Final[bool] + +# version info +OPENSSL_VERSION_NUMBER: Final[int] +OPENSSL_VERSION_INFO: Final[tuple[int, int, int, int, int]] +OPENSSL_VERSION: Final[str] +_OPENSSL_API_VERSION: Final[tuple[int, int, int, int, int]] diff --git a/mypy/typeshed/stdlib/_struct.pyi b/mypy/typeshed/stdlib/_struct.pyi new file mode 100644 index 0000000000000..a8fac2aea1b00 --- /dev/null +++ b/mypy/typeshed/stdlib/_struct.pyi @@ -0,0 +1,23 @@ +from _typeshed import ReadableBuffer, WriteableBuffer +from collections.abc import Iterator +from typing import Any +from typing_extensions import disjoint_base + +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: ... +@disjoint_base +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 b75e7608fa77d..6969ae48cae79 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -5,7 +5,7 @@ from collections.abc import Callable from threading import Thread from types import TracebackType from typing import Any, Final, NoReturn, final, overload -from typing_extensions import TypeVarTuple, Unpack +from typing_extensions import TypeVarTuple, Unpack, disjoint_base _Ts = TypeVarTuple("_Ts") @@ -13,17 +13,13 @@ error = RuntimeError def _count() -> int: ... @final -class LockType: +class RLock: 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: ... + __enter__ = acquire + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... + if sys.version_info >= (3, 14): + def locked(self) -> bool: ... if sys.version_info >= (3, 13): @final @@ -37,25 +33,59 @@ if sys.version_info >= (3, 13): def start_joinable_thread( function: Callable[[], object], handle: _ThreadHandle | None = None, daemon: bool = True ) -> _ThreadHandle: ... - lock = LockType + @final + class lock: + 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: ... + + LockType = lock +else: + @final + class LockType: + 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: ... @overload def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... @overload def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... +# Obsolete synonym for start_new_thread() +@overload +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: ... + def interrupt_main(signum: signal.Signals = signal.SIGINT, /) -> 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 = 0, /) -> int: ... -TIMEOUT_MAX: float +TIMEOUT_MAX: Final[float] def get_native_id() -> int: ... # only available on some platforms @final @@ -77,6 +107,10 @@ _excepthook: Callable[[_ExceptHookArgs], Any] if sys.version_info >= (3, 12): def daemon_threads_allowed() -> bool: ... +if sys.version_info >= (3, 14): + def set_name(name: str) -> None: ... + +@disjoint_base class _local: def __getattribute__(self, name: str, /) -> Any: ... def __setattr__(self, name: str, value: Any, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_threading_local.pyi b/mypy/typeshed/stdlib/_threading_local.pyi index 98683dabcef8f..5f6acaf840aa1 100644 --- a/mypy/typeshed/stdlib/_threading_local.pyi +++ b/mypy/typeshed/stdlib/_threading_local.pyi @@ -1,17 +1,24 @@ +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"] _LocalDict: TypeAlias = dict[Any, Any] class _localimpl: + __slots__ = ("key", "dicts", "localargs", "locallock", "__weakref__") 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: + __slots__ = ("_local__impl", "__dict__") + 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 63b1e7ca7cb46..a3868f467c6ca 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable from typing import Any, ClassVar, Final, final -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated # _tkinter is meant to be only used internally by tkinter, but some tkinter # functions e.g. return _tkinter.Tcl_Obj objects. Tcl_Obj represents a Tcl @@ -54,42 +54,43 @@ _TkinterTraceFunc: TypeAlias = Callable[[tuple[str, ...]], object] @final class TkappType: # Please keep in sync with tkinter.Tk - def adderrorinfo(self, msg, /): ... + def adderrorinfo(self, msg: str, /): ... def call(self, command: Any, /, *args: Any) -> Any: ... - def createcommand(self, name, func, /): ... + def createcommand(self, name: str, func, /): ... if sys.platform != "win32": - def createfilehandler(self, file, mask, func, /): ... - def deletefilehandler(self, file, /): ... + def createfilehandler(self, file, mask: int, func, /): ... + def deletefilehandler(self, file, /) -> None: ... - def createtimerhandler(self, milliseconds, func, /): ... - def deletecommand(self, name, /): ... + def createtimerhandler(self, milliseconds: int, func, /): ... + def deletecommand(self, name: str, /): ... def dooneevent(self, flags: int = 0, /): ... def eval(self, script: str, /) -> str: ... - def evalfile(self, fileName, /): ... - def exprboolean(self, s, /): ... - def exprdouble(self, s, /): ... - def exprlong(self, s, /): ... - def exprstring(self, s, /): ... - def getboolean(self, arg, /): ... - def getdouble(self, arg, /): ... - def getint(self, arg, /): ... + def evalfile(self, fileName: str, /): ... + def exprboolean(self, s: str, /): ... + def exprdouble(self, s: str, /): ... + def exprlong(self, s: str, /): ... + def exprstring(self, s: str, /): ... + def getboolean(self, arg, /) -> bool: ... + def getdouble(self, arg, /) -> float: ... + def getint(self, arg, /) -> int: ... def getvar(self, *args, **kwargs): ... def globalgetvar(self, *args, **kwargs): ... def globalsetvar(self, *args, **kwargs): ... def globalunsetvar(self, *args, **kwargs): ... - def interpaddr(self): ... + def interpaddr(self) -> int: ... def loadtk(self) -> None: ... - def mainloop(self, threshold: int = 0, /): ... - def quit(self): ... - def record(self, script, /): ... + def mainloop(self, threshold: int = 0, /) -> None: ... + def quit(self) -> None: ... + def record(self, script: str, /): ... def setvar(self, *ags, **kwargs): ... if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.9; removed in Python 3.11. Use `splitlist()` instead.") def split(self, arg, /): ... def splitlist(self, arg, /): ... def unsetvar(self, *args, **kwargs): ... def wantobjects(self, *args, **kwargs): ... - def willdispatch(self): ... + def willdispatch(self) -> None: ... if sys.version_info >= (3, 12): def gettrace(self, /) -> _TkinterTraceFunc | None: ... def settrace(self, func: _TkinterTraceFunc | None, /) -> None: ... @@ -113,16 +114,31 @@ TK_VERSION: Final[str] class TkttType: def deletetimerhandler(self): ... -def create( - screenName: str | None = None, - baseName: str = "", - className: str = "Tk", - interactive: bool = False, - wantobjects: bool = False, - wantTk: bool = True, - sync: bool = False, - use: str | None = None, - /, -): ... -def getbusywaitinterval(): ... -def setbusywaitinterval(new_val, /): ... +if sys.version_info >= (3, 13): + def create( + screenName: str | None = None, + baseName: str = "", + className: str = "Tk", + interactive: bool = False, + wantobjects: int = 0, + wantTk: bool = True, + sync: bool = False, + use: str | None = None, + /, + ): ... + +else: + def create( + screenName: str | None = None, + baseName: str = "", + className: str = "Tk", + interactive: bool = False, + wantobjects: bool = False, + wantTk: bool = True, + sync: bool = False, + use: str | None = None, + /, + ): ... + +def getbusywaitinterval() -> int: ... +def setbusywaitinterval(new_val: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_tracemalloc.pyi b/mypy/typeshed/stdlib/_tracemalloc.pyi index b1aeb710233e1..e9720f46692ce 100644 --- a/mypy/typeshed/stdlib/_tracemalloc.pyi +++ b/mypy/typeshed/stdlib/_tracemalloc.pyi @@ -1,4 +1,3 @@ -import sys from collections.abc import Sequence from tracemalloc import _FrameTuple, _TraceTuple @@ -9,9 +8,6 @@ def get_traceback_limit() -> int: ... def get_traced_memory() -> tuple[int, int]: ... def get_tracemalloc_memory() -> int: ... def is_tracing() -> bool: ... - -if sys.version_info >= (3, 9): - def reset_peak() -> None: ... - +def reset_peak() -> None: ... def start(nframe: int = 1, /) -> None: ... def stop() -> None: ... diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index 7201819b25ed9..b786923880e13 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -3,7 +3,7 @@ # See the README.md file in this directory for more information. import sys -from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized +from collections.abc import Awaitable, Callable, Iterable, Iterator, Sequence, Set as AbstractSet, Sized from dataclasses import Field from os import PathLike from types import FrameType, TracebackType @@ -22,7 +22,7 @@ from typing import ( final, overload, ) -from typing_extensions import Buffer, LiteralString, TypeAlias +from typing_extensions import Buffer, LiteralString, Self as _Self, TypeAlias _KT = TypeVar("_KT") _KT_co = TypeVar("_KT_co", covariant=True) @@ -54,7 +54,8 @@ Unused: TypeAlias = object # stable # Marker for return types that include None, but where forcing the user to # check for None can be detrimental. Sometimes called "the Any trick". See -# CONTRIBUTING.md for more information. +# https://typing.python.org/en/latest/guides/writing_stubs.html#the-any-trick +# for more information. MaybeNone: TypeAlias = Any # stable # Used to mark arguments that default to a sentinel value. This prevents @@ -65,10 +66,10 @@ MaybeNone: TypeAlias = Any # stable # In cases where the sentinel object is exported and can be used by user code, # a construct like this is better: # -# _SentinelType = NewType("_SentinelType", object) -# sentinel: _SentinelType +# _SentinelType = NewType("_SentinelType", object) # does not exist at runtime +# sentinel: Final[_SentinelType] # def foo(x: int | None | _SentinelType = ...) -> None: ... -sentinel: Any +sentinel: Any # stable # stable class IdentityFunction(Protocol): @@ -82,19 +83,21 @@ class SupportsNext(Protocol[_T_co]): class SupportsAnext(Protocol[_T_co]): def __anext__(self) -> Awaitable[_T_co]: ... -# Comparison protocols +class SupportsBool(Protocol): + def __bool__(self) -> bool: ... +# Comparison protocols class SupportsDunderLT(Protocol[_T_contra]): - def __lt__(self, other: _T_contra, /) -> bool: ... + def __lt__(self, other: _T_contra, /) -> SupportsBool: ... class SupportsDunderGT(Protocol[_T_contra]): - def __gt__(self, other: _T_contra, /) -> bool: ... + def __gt__(self, other: _T_contra, /) -> SupportsBool: ... class SupportsDunderLE(Protocol[_T_contra]): - def __le__(self, other: _T_contra, /) -> bool: ... + def __le__(self, other: _T_contra, /) -> SupportsBool: ... class SupportsDunderGE(Protocol[_T_contra]): - def __ge__(self, other: _T_contra, /) -> bool: ... + def __ge__(self, other: _T_contra, /) -> SupportsBool: ... class SupportsAllComparisons( SupportsDunderLT[Any], SupportsDunderGT[Any], SupportsDunderLE[Any], SupportsDunderGE[Any], Protocol @@ -117,6 +120,12 @@ class SupportsSub(Protocol[_T_contra, _T_co]): class SupportsRSub(Protocol[_T_contra, _T_co]): def __rsub__(self, x: _T_contra, /) -> _T_co: ... +class SupportsMul(Protocol[_T_contra, _T_co]): + def __mul__(self, x: _T_contra, /) -> _T_co: ... + +class SupportsRMul(Protocol[_T_contra, _T_co]): + def __rmul__(self, x: _T_contra, /) -> _T_co: ... + class SupportsDivMod(Protocol[_T_contra, _T_co]): def __divmod__(self, other: _T_contra, /) -> _T_co: ... @@ -133,6 +142,9 @@ class SupportsIter(Protocol[_T_co]): class SupportsAiter(Protocol[_T_co]): def __aiter__(self) -> _T_co: ... +class SupportsLen(Protocol): + def __len__(self) -> int: ... + class SupportsLenAndGetItem(Protocol[_T_co]): def __len__(self) -> int: ... def __getitem__(self, k: int, /) -> _T_co: ... @@ -151,11 +163,8 @@ class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): def keys(self) -> Iterable[_KT]: ... def __getitem__(self, key: _KT, /) -> _VT_co: ... -# This protocol is currently under discussion. Use SupportsContainsAndGetItem -# instead, if you require the __contains__ method. -# See https://github.com/python/typeshed/issues/11822. +# stable class SupportsGetItem(Protocol[_KT_contra, _VT_co]): - def __contains__(self, x: Any, /) -> bool: ... def __getitem__(self, key: _KT_contra, /) -> _VT_co: ... # stable @@ -270,6 +279,16 @@ class SupportsWrite(Protocol[_T_contra]): class SupportsFlush(Protocol): def flush(self) -> object: ... +# Suitable for dictionary view objects +class Viewable(Protocol[_T_co]): + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + +class SupportsGetItemViewable(Protocol[_KT, _VT_co]): + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_KT]: ... + def __getitem__(self, key: _KT, /) -> _VT_co: ... + # Unfortunately PEP 688 does not allow us to distinguish read-only # from writable buffers. We use these aliases for readability for now. # Perhaps a future extension of the buffer protocol will allow us to @@ -295,9 +314,6 @@ class SupportsGetItemBuffer(SliceableBuffer, IndexableBuffer, Protocol): class SizedBuffer(Sized, Buffer, Protocol): ... -# for compatibility with third-party stubs that may use this -_BufferWithLen: TypeAlias = SizedBuffer # not stable # noqa: Y047 - ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType] OptExcInfo: TypeAlias = ExcInfo | tuple[None, None, None] @@ -325,9 +341,9 @@ class structseq(Generic[_T_co]): # The second parameter will accept a dict of any kind without raising an exception, # but only has any meaning if you supply it a dict where the keys are strings. # https://github.com/python/typeshed/pull/6560#discussion_r767149830 - def __new__(cls: type[Self], sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> Self: ... + def __new__(cls, sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> _Self: ... if sys.version_info >= (3, 13): - def __replace__(self: Self, **kwargs: Any) -> Self: ... + def __replace__(self, **kwargs: Any) -> _Self: ... # Superset of typing.AnyStr that also includes LiteralString AnyOrLiteralStr = TypeVar("AnyOrLiteralStr", str, bytes, LiteralString) # noqa: Y001 @@ -350,7 +366,10 @@ class DataclassInstance(Protocol): __dataclass_fields__: ClassVar[dict[str, Field[Any]]] # Anything that can be passed to the int/float constructors -ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc +if sys.version_info >= (3, 14): + ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex +else: + ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc ConvertibleToFloat: TypeAlias = str | ReadableBuffer | SupportsFloat | SupportsIndex # A few classes updated from Foo(str, Enum) to Foo(StrEnum). This is a convenience so these @@ -361,3 +380,14 @@ else: from enum import Enum class StrEnum(str, Enum): ... + +# Objects that appear in annotations or in type expressions. +# Similar to PEP 747's TypeForm but a little broader. +AnnotationForm: TypeAlias = Any + +if sys.version_info >= (3, 14): + from annotationlib import Format + + # These return annotations, which can be arbitrary objects + AnnotateFunc: TypeAlias = Callable[[Format], dict[str, AnnotationForm]] + EvaluateFunc: TypeAlias = Callable[[Format], AnnotationForm] diff --git a/mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi b/mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi new file mode 100644 index 0000000000000..feb22aae00732 --- /dev/null +++ b/mypy/typeshed/stdlib/_typeshed/_type_checker_internals.pyi @@ -0,0 +1,89 @@ +# Internals used by some type checkers. +# +# Don't use this module directly. It is only for type checkers to use. + +import sys +import typing_extensions +from _collections_abc import dict_items, dict_keys, dict_values +from abc import ABCMeta +from collections.abc import Awaitable, Generator, Iterable, Mapping +from typing import Any, ClassVar, Generic, TypeVar, overload +from typing_extensions import Never + +_T = TypeVar("_T") + +# Used for an undocumented mypy feature. Does not exist at runtime. +promote = object() + +# Fallback type providing methods and attributes that appear on all `TypedDict` types. +# N.B. Keep this mostly in sync with typing_extensions._TypedDict/mypy_extensions._TypedDict +class TypedDictFallback(Mapping[str, object], metaclass=ABCMeta): + __total__: ClassVar[bool] + __required_keys__: ClassVar[frozenset[str]] + __optional_keys__: ClassVar[frozenset[str]] + # __orig_bases__ sometimes exists on <3.12, but not consistently, + # so we only add it to the stub on 3.12+ + if sys.version_info >= (3, 12): + __orig_bases__: ClassVar[tuple[Any, ...]] + if sys.version_info >= (3, 13): + __readonly_keys__: ClassVar[frozenset[str]] + __mutable_keys__: ClassVar[frozenset[str]] + + def copy(self) -> typing_extensions.Self: ... + # Using Never so that only calls using mypy plugin hook that specialize the signature + # can go through. + def setdefault(self, k: Never, default: object) -> object: ... + # Mypy plugin hook for 'pop' expects that 'default' has a type variable type. + def pop(self, k: Never, default: _T = ...) -> object: ... # pyright: ignore[reportInvalidTypeVarUse] + def update(self, m: typing_extensions.Self, /) -> None: ... + def __delitem__(self, k: Never) -> None: ... + def items(self) -> dict_items[str, object]: ... + def keys(self) -> dict_keys[str, object]: ... + def values(self) -> dict_values[str, object]: ... + @overload + def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... + @overload + def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... + @overload + def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... + @overload + def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... + # supposedly incompatible definitions of __or__ and __ior__ + def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... # type: ignore[misc] + +# Fallback type providing methods and attributes that appear on all `NamedTuple` types. +class NamedTupleFallback(tuple[Any, ...]): + _field_defaults: ClassVar[dict[str, Any]] + _fields: ClassVar[tuple[str, ...]] + # __orig_bases__ sometimes exists on <3.12, but not consistently + # So we only add it to the stub on 3.12+. + if sys.version_info >= (3, 12): + __orig_bases__: ClassVar[tuple[Any, ...]] + + @overload + def __init__(self, typename: str, fields: Iterable[tuple[str, Any]], /) -> None: ... + @overload + @typing_extensions.deprecated( + "Creating a typing.NamedTuple using keyword arguments is deprecated and support will be removed in Python 3.15" + ) + def __init__(self, typename: str, fields: None = None, /, **kwargs: Any) -> None: ... + @classmethod + def _make(cls, iterable: Iterable[Any]) -> typing_extensions.Self: ... + def _asdict(self) -> dict[str, Any]: ... + def _replace(self, **kwargs: Any) -> typing_extensions.Self: ... + if sys.version_info >= (3, 13): + def __replace__(self, **kwargs: Any) -> typing_extensions.Self: ... + +# Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter. +_S = TypeVar("_S") +_YieldT_co = TypeVar("_YieldT_co", covariant=True) +_SendT_nd_contra = TypeVar("_SendT_nd_contra", contravariant=True) +_ReturnT_nd_co = TypeVar("_ReturnT_nd_co", covariant=True) + +# The parameters correspond to Generator, but the 4th is the original type. +class AwaitableGenerator( + Awaitable[_ReturnT_nd_co], + Generator[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co], + Generic[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co, _S], + metaclass=ABCMeta, +): ... diff --git a/mypy/typeshed/stdlib/_warnings.pyi b/mypy/typeshed/stdlib/_warnings.pyi index 2e571e676c974..2dbc7b8552813 100644 --- a/mypy/typeshed/stdlib/_warnings.pyi +++ b/mypy/typeshed/stdlib/_warnings.pyi @@ -38,9 +38,9 @@ def warn_explicit( filename: str, lineno: int, module: str | None = ..., - registry: dict[str | tuple[str, type[Warning], int], int] | None = ..., - module_globals: dict[str, Any] | None = ..., - source: Any | None = ..., + registry: dict[str | tuple[str, type[Warning], int], int] | None = None, + module_globals: dict[str, Any] | None = None, + source: Any | None = None, ) -> None: ... @overload def warn_explicit( @@ -48,8 +48,8 @@ def warn_explicit( category: Any, filename: str, lineno: int, - module: str | None = ..., - registry: dict[str | tuple[str, type[Warning], int], int] | None = ..., - module_globals: dict[str, Any] | None = ..., - source: Any | None = ..., + module: str | None = None, + registry: dict[str | tuple[str, type[Warning], int], int] | None = None, + module_globals: dict[str, Any] | None = None, + source: Any | None = None, ) -> None: ... 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/_weakrefset.pyi b/mypy/typeshed/stdlib/_weakrefset.pyi index 2a4e682f64ed7..dad1ed7a4fb5c 100644 --- a/mypy/typeshed/stdlib/_weakrefset.pyi +++ b/mypy/typeshed/stdlib/_weakrefset.pyi @@ -1,11 +1,8 @@ -import sys from collections.abc import Iterable, Iterator, MutableSet -from typing import Any, TypeVar, overload +from types import GenericAlias +from typing import Any, ClassVar, TypeVar, overload from typing_extensions import Self -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["WeakSet"] _S = TypeVar("_S") @@ -21,6 +18,7 @@ class WeakSet(MutableSet[_T]): def copy(self) -> Self: ... def remove(self, item: _T) -> None: ... def update(self, other: Iterable[_T]) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __contains__(self, item: object) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T]: ... @@ -47,5 +45,4 @@ class WeakSet(MutableSet[_T]): def union(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... def __or__(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... def isdisjoint(self, other: Iterable[_T]) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index 0f71a06877481..d9e2c377b115a 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -128,21 +128,21 @@ if sys.platform == "win32": WAIT_TIMEOUT: Final = 258 if sys.version_info >= (3, 10): - LOCALE_NAME_INVARIANT: str - LOCALE_NAME_MAX_LENGTH: int - LOCALE_NAME_SYSTEM_DEFAULT: str - LOCALE_NAME_USER_DEFAULT: str | None + LOCALE_NAME_INVARIANT: Final[str] + LOCALE_NAME_MAX_LENGTH: Final[int] + LOCALE_NAME_SYSTEM_DEFAULT: Final[str] + LOCALE_NAME_USER_DEFAULT: Final[str | None] - LCMAP_FULLWIDTH: int - LCMAP_HALFWIDTH: int - LCMAP_HIRAGANA: int - LCMAP_KATAKANA: int - LCMAP_LINGUISTIC_CASING: int - LCMAP_LOWERCASE: int - LCMAP_SIMPLIFIED_CHINESE: int - LCMAP_TITLECASE: int - LCMAP_TRADITIONAL_CHINESE: int - LCMAP_UPPERCASE: int + LCMAP_FULLWIDTH: Final[int] + LCMAP_HALFWIDTH: Final[int] + LCMAP_HIRAGANA: Final[int] + LCMAP_KATAKANA: Final[int] + LCMAP_LINGUISTIC_CASING: Final[int] + LCMAP_LOWERCASE: Final[int] + LCMAP_SIMPLIFIED_CHINESE: Final[int] + LCMAP_TITLECASE: Final[int] + LCMAP_TRADITIONAL_CHINESE: Final[int] + LCMAP_UPPERCASE: Final[int] if sys.version_info >= (3, 12): COPYFILE2_CALLBACK_CHUNK_STARTED: Final = 1 @@ -172,6 +172,9 @@ if sys.platform == "win32": ERROR_ACCESS_DENIED: Final = 5 ERROR_PRIVILEGE_NOT_HELD: Final = 1314 + if sys.version_info >= (3, 14): + COPY_FILE_DIRECTORY: Final = 0x00000080 + def CloseHandle(handle: int, /) -> None: ... @overload def ConnectNamedPipe(handle: int, overlapped: Literal[True]) -> Overlapped: ... diff --git a/mypy/typeshed/stdlib/_zstd.pyi b/mypy/typeshed/stdlib/_zstd.pyi new file mode 100644 index 0000000000000..f5e98ef88bb9f --- /dev/null +++ b/mypy/typeshed/stdlib/_zstd.pyi @@ -0,0 +1,97 @@ +from _typeshed import ReadableBuffer +from collections.abc import Mapping +from compression.zstd import CompressionParameter, DecompressionParameter +from typing import Final, Literal, final +from typing_extensions import Self, TypeAlias + +ZSTD_CLEVEL_DEFAULT: Final = 3 +ZSTD_DStreamOutSize: Final = 131072 +ZSTD_btlazy2: Final = 6 +ZSTD_btopt: Final = 7 +ZSTD_btultra: Final = 8 +ZSTD_btultra2: Final = 9 +ZSTD_c_chainLog: Final = 103 +ZSTD_c_checksumFlag: Final = 201 +ZSTD_c_compressionLevel: Final = 100 +ZSTD_c_contentSizeFlag: Final = 200 +ZSTD_c_dictIDFlag: Final = 202 +ZSTD_c_enableLongDistanceMatching: Final = 160 +ZSTD_c_hashLog: Final = 102 +ZSTD_c_jobSize: Final = 401 +ZSTD_c_ldmBucketSizeLog: Final = 163 +ZSTD_c_ldmHashLog: Final = 161 +ZSTD_c_ldmHashRateLog: Final = 164 +ZSTD_c_ldmMinMatch: Final = 162 +ZSTD_c_minMatch: Final = 105 +ZSTD_c_nbWorkers: Final = 400 +ZSTD_c_overlapLog: Final = 402 +ZSTD_c_searchLog: Final = 104 +ZSTD_c_strategy: Final = 107 +ZSTD_c_targetLength: Final = 106 +ZSTD_c_windowLog: Final = 101 +ZSTD_d_windowLogMax: Final = 100 +ZSTD_dfast: Final = 2 +ZSTD_fast: Final = 1 +ZSTD_greedy: Final = 3 +ZSTD_lazy: Final = 4 +ZSTD_lazy2: Final = 5 + +_ZstdCompressorContinue: TypeAlias = Literal[0] +_ZstdCompressorFlushBlock: TypeAlias = Literal[1] +_ZstdCompressorFlushFrame: TypeAlias = Literal[2] + +@final +class ZstdCompressor: + CONTINUE: Final = 0 + FLUSH_BLOCK: Final = 1 + FLUSH_FRAME: Final = 2 + def __new__( + cls, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None + ) -> Self: ... + def compress( + self, /, data: ReadableBuffer, mode: _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 0 + ) -> bytes: ... + def flush(self, /, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 2) -> bytes: ... + def set_pledged_input_size(self, size: int | None, /) -> None: ... + @property + def last_mode(self) -> _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame: ... + +@final +class ZstdDecompressor: + def __new__(cls, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> Self: ... + 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: ... + +@final +class ZstdDict: + def __new__(cls, dict_content: bytes, /, *, is_raw: bool = False) -> Self: ... + def __len__(self, /) -> int: ... + @property + def as_digested_dict(self) -> tuple[Self, int]: ... + @property + def as_prefix(self) -> tuple[Self, int]: ... + @property + def as_undigested_dict(self) -> tuple[Self, int]: ... + @property + def dict_content(self) -> bytes: ... + @property + def dict_id(self) -> int: ... + +class ZstdError(Exception): ... + +def finalize_dict( + custom_dict_bytes: bytes, samples_bytes: bytes, samples_sizes: tuple[int, ...], dict_size: int, compression_level: int, / +) -> bytes: ... +def get_frame_info(frame_buffer: ReadableBuffer) -> tuple[int, int]: ... +def get_frame_size(frame_buffer: ReadableBuffer) -> int: ... +def get_param_bounds(parameter: int, is_compress: bool) -> tuple[int, int]: ... +def set_parameter_types(c_parameter_type: type[CompressionParameter], d_parameter_type: type[DecompressionParameter]) -> None: ... +def train_dict(samples_bytes: bytes, samples_sizes: tuple[int, ...], dict_size: int, /) -> bytes: ... + +zstd_version: Final[str] +zstd_version_number: Final[int] diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index fdca48ac7aafe..c8cd549e30eca 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("Use 'classmethod' with 'abstractmethod' instead") +@deprecated("Deprecated since Python 3.3. Use `@classmethod` stacked on top of `@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("Use 'staticmethod' with 'abstractmethod' instead") +@deprecated("Deprecated since Python 3.3. Use `@staticmethod` stacked on top of `@abstractmethod` instead.") class abstractstaticmethod(staticmethod[_P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[_P, _R_co]) -> None: ... -@deprecated("Use 'property' with 'abstractmethod' instead") +@deprecated("Deprecated since Python 3.3. Use `@property` stacked on top of `@abstractmethod` instead.") class abstractproperty(property): __isabstractmethod__: Literal[True] diff --git a/mypy/typeshed/stdlib/aifc.pyi b/mypy/typeshed/stdlib/aifc.pyi index 05bf53986b290..bfe12c6af2b0b 100644 --- a/mypy/typeshed/stdlib/aifc.pyi +++ b/mypy/typeshed/stdlib/aifc.pyi @@ -1,12 +1,8 @@ -import sys from types import TracebackType from typing import IO, Any, Literal, NamedTuple, overload from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 9): - __all__ = ["Error", "open"] -else: - __all__ = ["Error", "open", "openfp"] +__all__ = ["Error", "open"] class Error(Exception): ... @@ -81,11 +77,3 @@ def open(f: _File, mode: Literal["r", "rb"]) -> Aifc_read: ... def open(f: _File, mode: Literal["w", "wb"]) -> Aifc_write: ... @overload def open(f: _File, mode: str | None = None) -> Any: ... - -if sys.version_info < (3, 9): - @overload - def openfp(f: _File, mode: Literal["r", "rb"]) -> Aifc_read: ... - @overload - def openfp(f: _File, mode: Literal["w", "wb"]) -> Aifc_write: ... - @overload - def openfp(f: _File, mode: str | None = None) -> Any: ... diff --git a/mypy/typeshed/stdlib/annotationlib.pyi b/mypy/typeshed/stdlib/annotationlib.pyi new file mode 100644 index 0000000000000..3679dc29daaa0 --- /dev/null +++ b/mypy/typeshed/stdlib/annotationlib.pyi @@ -0,0 +1,146 @@ +import sys +from typing import Literal + +if sys.version_info >= (3, 14): + import enum + import types + from _typeshed import AnnotateFunc, AnnotationForm, EvaluateFunc, SupportsItems + from collections.abc import Mapping + from typing import Any, ParamSpec, TypeVar, TypeVarTuple, final, overload + from warnings import deprecated + + __all__ = [ + "Format", + "ForwardRef", + "call_annotate_function", + "call_evaluate_function", + "get_annotate_from_class_namespace", + "get_annotations", + "annotations_to_string", + "type_repr", + ] + + class Format(enum.IntEnum): + VALUE = 1 + VALUE_WITH_FAKE_GLOBALS = 2 + FORWARDREF = 3 + STRING = 4 + + @final + class ForwardRef: + __slots__ = ( + "__forward_is_argument__", + "__forward_is_class__", + "__forward_module__", + "__weakref__", + "__arg__", + "__globals__", + "__extra_names__", + "__code__", + "__ast_node__", + "__cell__", + "__owner__", + "__stringifier_dict__", + ) + __forward_is_argument__: bool + __forward_is_class__: bool + __forward_module__: str | None + def __init__( + self, arg: str, *, module: str | None = None, owner: object = None, is_argument: bool = True, is_class: bool = False + ) -> None: ... + @overload + def evaluate( + self, + *, + globals: dict[str, Any] | None = None, + locals: Mapping[str, Any] | None = None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, + owner: object = None, + format: Literal[Format.STRING], + ) -> str: ... + @overload + def evaluate( + self, + *, + globals: dict[str, Any] | None = None, + locals: Mapping[str, Any] | None = None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, + owner: object = None, + format: Literal[Format.FORWARDREF], + ) -> AnnotationForm | ForwardRef: ... + @overload + def evaluate( + self, + *, + globals: dict[str, Any] | None = None, + locals: Mapping[str, Any] | None = None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, + owner: object = None, + format: Format = Format.VALUE, # noqa: Y011 + ) -> AnnotationForm: ... + @deprecated("Use `ForwardRef.evaluate()` or `typing.evaluate_forward_ref()` instead.") + def _evaluate( + self, + globalns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ..., + *, + recursive_guard: frozenset[str], + ) -> AnnotationForm: ... + @property + def __forward_arg__(self) -> str: ... + @property + def __forward_code__(self) -> types.CodeType: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def __or__(self, other: Any) -> types.UnionType: ... + def __ror__(self, other: Any) -> types.UnionType: ... + + @overload + def call_evaluate_function(evaluate: EvaluateFunc, format: Literal[Format.STRING], *, owner: object = None) -> str: ... + @overload + def call_evaluate_function( + evaluate: EvaluateFunc, format: Literal[Format.FORWARDREF], *, owner: object = None + ) -> AnnotationForm | ForwardRef: ... + @overload + def call_evaluate_function(evaluate: EvaluateFunc, format: Format, *, owner: object = None) -> AnnotationForm: ... + @overload + def call_annotate_function( + annotate: AnnotateFunc, format: Literal[Format.STRING], *, owner: object = None + ) -> dict[str, str]: ... + @overload + def call_annotate_function( + annotate: AnnotateFunc, format: Literal[Format.FORWARDREF], *, owner: object = None + ) -> dict[str, AnnotationForm | ForwardRef]: ... + @overload + def call_annotate_function(annotate: AnnotateFunc, format: Format, *, owner: object = None) -> dict[str, AnnotationForm]: ... + def get_annotate_from_class_namespace(obj: Mapping[str, object]) -> AnnotateFunc | None: ... + @overload + def get_annotations( + obj: Any, # any object with __annotations__ or __annotate__ + *, + globals: dict[str, object] | None = None, + locals: Mapping[str, object] | None = None, + eval_str: bool = False, + format: Literal[Format.STRING], + ) -> dict[str, str]: ... + @overload + def get_annotations( + obj: Any, + *, + globals: dict[str, object] | None = None, + locals: Mapping[str, object] | None = None, + eval_str: bool = False, + format: Literal[Format.FORWARDREF], + ) -> dict[str, AnnotationForm | ForwardRef]: ... + @overload + def get_annotations( + obj: Any, + *, + globals: dict[str, object] | None = None, + locals: Mapping[str, object] | None = None, + eval_str: bool = False, + format: Format = Format.VALUE, # noqa: Y011 + ) -> dict[str, AnnotationForm]: ... + def type_repr(value: object) -> str: ... + def annotations_to_string(annotations: SupportsItems[str, object]) -> dict[str, str]: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 2526322ac8f6d..f4b3aac09aa96 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -1,8 +1,8 @@ import sys -from _typeshed import sentinel +from _typeshed import SupportsWrite, sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern -from typing import IO, Any, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload +from typing import IO, Any, ClassVar, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload, type_check_only from typing_extensions import Self, TypeAlias, deprecated __all__ = [ @@ -17,6 +17,7 @@ __all__ = [ "MetavarTypeHelpFormatter", "Namespace", "Action", + "BooleanOptionalAction", "ONE_OR_MORE", "OPTIONAL", "PARSER", @@ -25,23 +26,11 @@ __all__ = [ "ZERO_OR_MORE", ] -if sys.version_info >= (3, 9): - __all__ += ["BooleanOptionalAction"] - _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 -# that don't use a literal argument -_ActionStr: TypeAlias = str -# more precisely, Literal["?", "*", "+", "...", "A...", -# "==SUPPRESS=="], but using this would make it hard to annotate -# callers that don't use a literal argument -_NArgsStr: TypeAlias = str ONE_OR_MORE: Final = "+" OPTIONAL: Final = "?" @@ -51,7 +40,7 @@ _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: Final = "*" -_UNRECOGNIZED_ARGS_ATTR: Final[str] # undocumented +_UNRECOGNIZED_ARGS_ATTR: Final = "_unrecognized_args" # undocumented class ArgumentError(Exception): argument_name: str | None @@ -86,8 +75,13 @@ class _ActionsContainer: def add_argument( self, *name_or_flags: str, - action: _ActionStr | type[Action] = ..., - nargs: int | _NArgsStr | _SUPPRESS_T | None = None, + # str covers predefined actions ("store_true", "count", etc.) + # and user registered actions via the `register` method. + action: str | type[Action] = ..., + # more precisely, Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="], + # but using this would make it hard to annotate callers that don't use a + # literal argument and for subclasses to override this method. + nargs: int | str | _SUPPRESS_T | None = None, const: Any = ..., default: Any = ..., type: _ActionType = ..., @@ -120,6 +114,7 @@ class _ActionsContainer: def _handle_conflict_error(self, action: Action, conflicting_actions: Iterable[tuple[str, Action]]) -> NoReturn: ... def _handle_conflict_resolve(self, action: Action, conflicting_actions: Iterable[tuple[str, Action]]) -> None: ... +@type_check_only class _FormatterClass(Protocol): def __call__(self, *, prog: str) -> HelpFormatter: ... @@ -131,6 +126,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): fromfile_prefix_chars: str | None add_help: bool allow_abbrev: bool + exit_on_error: bool + + if sys.version_info >= (3, 14): + suggest_on_error: bool + color: bool # undocumented _positionals: _ArgumentGroup @@ -138,7 +138,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): _subparsers: _ArgumentGroup | None # Note: the constructor arguments are also used in _SubParsersAction.add_parser. - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 14): def __init__( self, prog: str | None = None, @@ -154,6 +154,9 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): add_help: bool = True, allow_abbrev: bool = True, exit_on_error: bool = True, + *, + suggest_on_error: bool = False, + color: bool = True, ) -> None: ... else: def __init__( @@ -170,6 +173,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): conflict_handler: str = "error", add_help: bool = True, allow_abbrev: bool = True, + exit_on_error: bool = True, ) -> None: ... @overload @@ -182,33 +186,33 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def add_subparsers( self: _ArgumentParserT, *, - title: str = ..., - description: str | None = ..., - prog: str = ..., + title: str = "subcommands", + description: str | None = None, + prog: str | None = None, action: type[Action] = ..., option_string: str = ..., - dest: str | None = ..., - required: bool = ..., - help: str | None = ..., - metavar: str | None = ..., + dest: str | None = None, + required: bool = False, + help: str | None = None, + metavar: str | None = None, ) -> _SubParsersAction[_ArgumentParserT]: ... @overload def add_subparsers( self, *, - title: str = ..., - description: str | None = ..., - prog: str = ..., + title: str = "subcommands", + description: str | None = None, + prog: str | None = None, parser_class: type[_ArgumentParserT], action: type[Action] = ..., option_string: str = ..., - dest: str | None = ..., - required: bool = ..., - help: str | None = ..., - metavar: str | None = ..., + dest: str | None = None, + required: bool = False, + help: str | None = None, + metavar: str | None = None, ) -> _SubParsersAction[_ArgumentParserT]: ... - def print_usage(self, file: IO[str] | None = None) -> None: ... - def print_help(self, file: IO[str] | None = None) -> None: ... + def print_usage(self, file: SupportsWrite[str] | None = None) -> None: ... + def print_help(self, file: SupportsWrite[str] | None = None) -> None: ... def format_usage(self) -> str: ... def format_help(self) -> str: ... @overload @@ -237,7 +241,13 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): # undocumented def _get_optional_actions(self) -> list[Action]: ... def _get_positional_actions(self) -> list[Action]: ... - def _parse_known_args(self, arg_strings: list[str], namespace: Namespace) -> tuple[Namespace, list[str]]: ... + if sys.version_info >= (3, 12): + def _parse_known_args( + self, arg_strings: list[str], namespace: Namespace, intermixed: bool + ) -> tuple[Namespace, list[str]]: ... + else: + def _parse_known_args(self, arg_strings: list[str], namespace: Namespace) -> tuple[Namespace, list[str]]: ... + def _read_args_from_files(self, arg_strings: list[str]) -> list[str]: ... def _match_argument(self, action: Action, arg_strings_pattern: str) -> int: ... def _match_arguments_partial(self, actions: Sequence[Action], arg_strings_pattern: str) -> list[int]: ... @@ -248,7 +258,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def _get_value(self, action: Action, arg_string: str) -> Any: ... def _check_value(self, action: Action, value: Any) -> None: ... def _get_formatter(self) -> HelpFormatter: ... - def _print_message(self, message: str, file: IO[str] | None = None) -> None: ... + def _print_message(self, message: str, file: SupportsWrite[str] | None = None) -> None: ... class HelpFormatter: # undocumented @@ -272,7 +282,15 @@ class HelpFormatter: def __init__(self, formatter: HelpFormatter, parent: Self | None, heading: str | None = None) -> None: ... def format_help(self) -> str: ... - def __init__(self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None) -> None: ... + if sys.version_info >= (3, 14): + def __init__( + self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None, color: bool = True + ) -> None: ... + else: + def __init__( + self, prog: str, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None + ) -> None: ... + def _indent(self) -> None: ... def _dedent(self) -> None: ... def _add_item(self, func: Callable[..., str], args: Iterable[Any]) -> None: ... @@ -352,8 +370,7 @@ class Action(_AttributeHolder): def __call__( self, parser: ArgumentParser, namespace: Namespace, values: str | Sequence[Any] | None, option_string: str | None = None ) -> None: ... - if sys.version_info >= (3, 9): - def format_usage(self) -> str: ... + def format_usage(self) -> str: ... if sys.version_info >= (3, 12): class BooleanOptionalAction(Action): @@ -418,7 +435,7 @@ if sys.version_info >= (3, 12): metavar: str | tuple[str, ...] | None = sentinel, ) -> None: ... -elif sys.version_info >= (3, 9): +else: class BooleanOptionalAction(Action): @overload def __init__( @@ -450,30 +467,71 @@ class Namespace(_AttributeHolder): def __setattr__(self, name: str, value: Any, /) -> None: ... def __contains__(self, key: str) -> bool: ... def __eq__(self, other: object) -> bool: ... - -class FileType: - # undocumented - _mode: str - _bufsize: int - _encoding: str | None - _errors: str | None - def __init__(self, mode: str = "r", bufsize: int = -1, encoding: str | None = None, errors: str | None = None) -> None: ... - def __call__(self, string: str) -> IO[Any]: ... + __hash__: ClassVar[None] # type: ignore[assignment] + +if sys.version_info >= (3, 14): + @deprecated("Deprecated since Python 3.14. Open files after parsing arguments instead.") + class FileType: + # undocumented + _mode: str + _bufsize: int + _encoding: str | None + _errors: str | None + def __init__( + self, mode: str = "r", bufsize: int = -1, encoding: str | None = None, errors: str | None = None + ) -> None: ... + def __call__(self, string: str) -> IO[Any]: ... + +else: + class FileType: + # undocumented + _mode: str + _bufsize: int + _encoding: str | None + _errors: str | None + def __init__( + self, mode: str = "r", bufsize: int = -1, encoding: str | None = None, errors: str | None = None + ) -> None: ... + def __call__(self, string: str) -> IO[Any]: ... # undocumented class _ArgumentGroup(_ActionsContainer): title: str | None _group_actions: list[Action] - def __init__( - self, - container: _ActionsContainer, - title: str | None = None, - description: str | None = None, - *, - prefix_chars: str = ..., - argument_default: Any = ..., - conflict_handler: str = ..., - ) -> None: ... + if sys.version_info >= (3, 14): + @overload + def __init__( + self, + container: _ActionsContainer, + title: str | None = None, + description: str | None = None, + *, + argument_default: Any = ..., + conflict_handler: str = ..., + ) -> None: ... + @overload + @deprecated("Undocumented `prefix_chars` parameter is deprecated since Python 3.14.") + def __init__( + self, + container: _ActionsContainer, + title: str | None = None, + description: str | None = None, + *, + prefix_chars: str, + argument_default: Any = ..., + conflict_handler: str = ..., + ) -> None: ... + else: + def __init__( + self, + container: _ActionsContainer, + title: str | None = None, + description: str | None = None, + *, + prefix_chars: str = ..., + argument_default: Any = ..., + conflict_handler: str = ..., + ) -> None: ... # undocumented class _MutuallyExclusiveGroup(_ArgumentGroup): @@ -688,7 +746,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): # Note: `add_parser` accepts all kwargs of `ArgumentParser.__init__`. It also # accepts its own `help` and `aliases` kwargs. - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 14): def add_parser( self, name: str, @@ -707,16 +765,19 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): fromfile_prefix_chars: str | None = ..., argument_default: Any = ..., conflict_handler: str = ..., - add_help: bool = ..., - allow_abbrev: bool = ..., - exit_on_error: bool = ..., + add_help: bool = True, + allow_abbrev: bool = True, + exit_on_error: bool = True, + suggest_on_error: bool = False, + color: bool = False, **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... - elif sys.version_info >= (3, 9): + elif sys.version_info >= (3, 13): def add_parser( self, name: str, *, + deprecated: bool = False, help: str | None = ..., aliases: Sequence[str] = ..., # Kwargs from ArgumentParser constructor @@ -730,9 +791,9 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): fromfile_prefix_chars: str | None = ..., argument_default: Any = ..., conflict_handler: str = ..., - add_help: bool = ..., - allow_abbrev: bool = ..., - exit_on_error: bool = ..., + add_help: bool = True, + allow_abbrev: bool = True, + exit_on_error: bool = True, **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... else: @@ -753,8 +814,9 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): fromfile_prefix_chars: str | None = ..., argument_default: Any = ..., conflict_handler: str = ..., - add_help: bool = ..., - allow_abbrev: bool = ..., + add_help: bool = True, + allow_abbrev: bool = True, + exit_on_error: bool = True, **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 878d8d8cb808b..a6b0344a1e2ea 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -1,42 +1,57 @@ import sys from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite -from collections.abc import Iterable - -# pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence -from typing import Any, Literal, MutableSequence, SupportsIndex, TypeVar, overload # noqa: Y022 -from typing_extensions import Self, TypeAlias - -if sys.version_info >= (3, 12): - from types import GenericAlias +from collections.abc import Iterable, MutableSequence +from types import GenericAlias +from typing import Any, ClassVar, Literal, SupportsIndex, TypeVar, overload +from typing_extensions import Self, TypeAlias, deprecated, disjoint_base _IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"] _FloatTypeCode: TypeAlias = Literal["f", "d"] -_UnicodeTypeCode: TypeAlias = Literal["u"] +if sys.version_info >= (3, 13): + _UnicodeTypeCode: TypeAlias = Literal["u", "w"] +else: + _UnicodeTypeCode: TypeAlias = Literal["u"] _TypeCode: TypeAlias = _IntTypeCode | _FloatTypeCode | _UnicodeTypeCode _T = TypeVar("_T", int, float, str) typecodes: str +@disjoint_base class array(MutableSequence[_T]): @property def typecode(self) -> _TypeCode: ... @property def itemsize(self) -> int: ... @overload - def __init__(self: array[int], typecode: _IntTypeCode, initializer: bytes | bytearray | Iterable[int] = ..., /) -> None: ... - @overload - def __init__( - self: array[float], typecode: _FloatTypeCode, initializer: bytes | bytearray | Iterable[float] = ..., / - ) -> None: ... + def __new__( + cls: type[array[int]], typecode: _IntTypeCode, initializer: bytes | bytearray | Iterable[int] = ..., / + ) -> array[int]: ... @overload - def __init__( - self: array[str], typecode: _UnicodeTypeCode, initializer: bytes | bytearray | Iterable[str] = ..., / - ) -> None: ... + def __new__( + cls: type[array[float]], typecode: _FloatTypeCode, initializer: bytes | bytearray | Iterable[float] = ..., / + ) -> array[float]: ... + if sys.version_info >= (3, 13): + @overload + def __new__( + cls: type[array[str]], typecode: Literal["w"], initializer: bytes | bytearray | Iterable[str] = ..., / + ) -> array[str]: ... + @overload + @deprecated("Deprecated since Python 3.3; will be removed in Python 3.16. Use 'w' typecode instead.") + def __new__( + cls: type[array[str]], typecode: Literal["u"], initializer: bytes | bytearray | Iterable[str] = ..., / + ) -> array[str]: ... + else: + @overload + @deprecated("Deprecated since Python 3.3; will be removed in Python 3.16.") + def __new__( + cls: type[array[str]], typecode: Literal["u"], initializer: bytes | bytearray | Iterable[str] = ..., / + ) -> array[str]: ... + @overload - def __init__(self, typecode: str, initializer: Iterable[_T], /) -> None: ... + def __new__(cls, typecode: str, initializer: Iterable[_T], /) -> Self: ... @overload - def __init__(self, typecode: str, initializer: bytes | bytearray = ..., /) -> None: ... + def __new__(cls, typecode: str, initializer: bytes | bytearray = ..., /) -> Self: ... def append(self, v: _T, /) -> None: ... def buffer_info(self) -> tuple[int, int]: ... def byteswap(self) -> None: ... @@ -58,10 +73,9 @@ class array(MutableSequence[_T]): def tofile(self, f: SupportsWrite[bytes], /) -> None: ... def tolist(self) -> list[_T]: ... def tounicode(self) -> str: ... - if sys.version_info < (3, 9): - def fromstring(self, buffer: str | ReadableBuffer, /) -> None: ... - def tostring(self) -> bytes: ... + __hash__: ClassVar[None] # type: ignore[assignment] + def __contains__(self, value: object, /) -> bool: ... def __len__(self) -> int: ... @overload def __getitem__(self, key: SupportsIndex, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 80049cff4ce05..e66e609ee6645 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -1,192 +1,1763 @@ +import ast +import builtins 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 collections.abc import Iterable, Iterator, Sequence +from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload, type_check_only +from typing_extensions import Self, Unpack, deprecated, disjoint_base -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 -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 +# 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; removed in Python 3.14") - class Str(Constant, metaclass=_ABC): - value: str - # Aliases for value, for backwards compatibility - s: str +# Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes +@type_check_only +class _Attributes(TypedDict, Generic[_EndPositionT], total=False): + lineno: int + col_offset: int + end_lineno: _EndPositionT + end_col_offset: _EndPositionT - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class Bytes(Constant, metaclass=_ABC): - value: bytes +# 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. +if sys.version_info >= (3, 12): + @disjoint_base + class AST: + __match_args__ = () + _attributes: ClassVar[tuple[str, ...]] + _fields: ClassVar[tuple[str, ...]] + if sys.version_info >= (3, 13): + _field_types: ClassVar[dict[str, Any]] + + if sys.version_info >= (3, 14): + def __replace__(self) -> Self: ... + +else: + class AST: + if sys.version_info >= (3, 10): + __match_args__ = () + _attributes: ClassVar[tuple[str, ...]] + _fields: ClassVar[tuple[str, ...]] + +class mod(AST): ... + +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: str + 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: str, + 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: str, + 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: str, + 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: str, + 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: str = ..., + args: arguments = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = ..., + type_comment: str | None = ..., + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> 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: str + 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: str, + 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: str, + 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: str, + 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: str, + 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: str = ..., + args: arguments = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = ..., + type_comment: str | None = ..., + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> 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: str + 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: str, + 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: str, + 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: str, + 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: str = ..., + 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__( # type: ignore[override] + 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: ... + +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[str] + if sys.version_info >= (3, 13): + def __init__(self, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[str], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Nonlocal(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[str] + if sys.version_info >= (3, 13): + def __init__(self, names: list[str] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[str], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[str] = ..., **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: ... + +if sys.version_info >= (3, 14): + class TemplateStr(expr): + __match_args__ = ("values",) + values: list[expr] + def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + + class Interpolation(expr): + __match_args__ = ("value", "str", "conversion", "format_spec") + value: expr + str: builtins.str + conversion: int + format_spec: expr | None = None + def __init__( + self, + value: expr = ..., + str: builtins.str = ..., + conversion: int = ..., + format_spec: expr | None = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + def __replace__( + self, + *, + value: expr = ..., + str: builtins.str = ..., + conversion: int = ..., + format_spec: expr | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +if sys.version_info >= (3, 10): + from types import EllipsisType + + _ConstantValue: typing_extensions.TypeAlias = str | bytes | bool | int | float | complex | None | EllipsisType +else: + # Rely on builtins.ellipsis + _ConstantValue: typing_extensions.TypeAlias = str | bytes | bool | int | float | complex | None | ellipsis # noqa: F821 + +class Constant(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "kind") + value: _ConstantValue + kind: str | None + if sys.version_info < (3, 14): # Aliases for value, for backwards compatibility - s: bytes + @property + @deprecated("Removed in Python 3.14. Use `value` instead.") + def n(self) -> _ConstantValue: ... + @n.setter + @deprecated("Removed in Python 3.14. Use `value` instead.") + def n(self, value: _ConstantValue) -> None: ... + @property + @deprecated("Removed in Python 3.14. Use `value` instead.") + def s(self) -> _ConstantValue: ... + @s.setter + @deprecated("Removed in Python 3.14. Use `value` instead.") + def s(self, value: _ConstantValue) -> None: ... - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class NameConstant(Constant, metaclass=_ABC): ... + def __init__(self, value: _ConstantValue, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - @deprecated("Replaced by ast.Constant; removed in Python 3.14") - class Ellipsis(Constant, metaclass=_ABC): ... + if sys.version_info >= (3, 14): + def __replace__(self, *, value: _ConstantValue = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> 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 Attribute(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "attr", "ctx") + value: expr + attr: str + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, attr: str, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... -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, 14): + def __replace__( + self, *, value: expr = ..., attr: str = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Subscript(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "slice", "ctx") + value: expr + slice: expr + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, slice: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., slice: expr = ..., 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: str + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, id: str, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, id: str = ..., 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__` + 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: ... + +@deprecated("Deprecated since Python 3.9.") +class slice(AST): ... + +class Slice(expr): + 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[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, lower: expr | None = ..., upper: expr | None = ..., step: expr | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +@deprecated("Deprecated since Python 3.9. Use `ast.Tuple` instead.") +class ExtSlice(slice): + def __new__(cls, dims: Iterable[slice] = (), **kwargs: Unpack[_Attributes]) -> Tuple: ... # type: ignore[misc] + +@deprecated("Deprecated since Python 3.9. Use the index value directly instead.") +class Index(slice): + def __new__(cls, value: expr, **kwargs: Unpack[_Attributes]) -> expr: ... # type: ignore[misc] + +class expr_context(AST): ... + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") +class AugLoad(expr_context): ... + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") +class AugStore(expr_context): ... + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") +class Param(expr_context): ... + +@deprecated("Deprecated since Python 3.9. Unused in Python 3.") +class Suite(mod): ... + +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: str | None + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, type: expr | None = None, name: str | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__(self, type: expr | None, name: str | None, body: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, type: expr | None = None, name: str | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, type: expr | None = ..., name: str | 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): - 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__ = ("arg", "annotation", "type_comment") + arg: str + annotation: expr | None + type_comment: str | None + def __init__( + self, arg: str, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... - if sys.version_info >= (3, 11): - def visit_TryStar(self, node: TryStar) -> Any: ... + if sys.version_info >= (3, 14): + def __replace__( + self, *, arg: str = ..., annotation: expr | None = ..., type_comment: str | None = ..., **kwargs: Unpack[_Attributes] + ) -> 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 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: str | None + value: expr + @overload + def __init__(self, arg: str | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__(self, arg: str | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> 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: ... + if sys.version_info >= (3, 14): + def __replace__(self, *, arg: str | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... -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 alias(AST): + name: str + asname: str | None + if sys.version_info >= (3, 10): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + if sys.version_info >= (3, 10): + __match_args__ = ("name", "asname") + if sys.version_info >= (3, 10): + def __init__(self, name: str, asname: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, name: str, asname: str | None = None) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, name: str = ..., asname: str | 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 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: ... + + class match_case(AST): + __match_args__ = ("pattern", "guard", "body") + pattern: ast.pattern + guard: expr | None + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, pattern: ast.pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ... + elif sys.version_info >= (3, 10): + @overload + def __init__(self, pattern: ast.pattern, guard: expr | None, body: list[stmt]) -> None: ... + @overload + def __init__(self, pattern: ast.pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, pattern: ast.pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ... + + 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 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: bool | None + def __init__(self, value: bool | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: bool | 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: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: str | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, keys: list[expr], patterns: list[pattern], rest: str | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: str | 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[str] + kwd_patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__( + self, + cls: expr, + patterns: list[pattern] = ..., + kwd_attrs: list[str] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + cls: expr, + patterns: list[pattern], + kwd_attrs: list[str], + 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[str] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class MatchStar(pattern): + __match_args__ = ("name",) + name: str | None + def __init__(self, name: str | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, name: str | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchAs(pattern): + __match_args__ = ("pattern", "name") + pattern: ast.pattern | None + name: str | None + def __init__( + self, pattern: ast.pattern | None = None, name: str | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, pattern: ast.pattern | None = ..., name: str | 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: str + bound: expr | None + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: str, bound: expr | None = None, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: str, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: str = ..., + 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: str + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__(self, name: str, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, name: str, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: str = ..., 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: str + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__(self, name: str, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, name: str, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: str = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + +if sys.version_info >= (3, 14): + @type_check_only + class _ABC(type): + def __init__(cls, *args: Unused) -> None: ... + +else: + class _ABC(type): + def __init__(cls, *args: Unused) -> None: ... + +if sys.version_info < (3, 14): + @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.") + class Num(Constant, metaclass=_ABC): + def __new__(cls, n: complex, **kwargs: Unpack[_Attributes]) -> Constant: ... # type: ignore[misc] # pyright: ignore[reportInconsistentConstructor] + + @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.") + class Str(Constant, metaclass=_ABC): + def __new__(cls, s: str, **kwargs: Unpack[_Attributes]) -> Constant: ... # type: ignore[misc] # pyright: ignore[reportInconsistentConstructor] + + @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.") + class Bytes(Constant, metaclass=_ABC): + def __new__(cls, s: bytes, **kwargs: Unpack[_Attributes]) -> Constant: ... # type: ignore[misc] # pyright: ignore[reportInconsistentConstructor] + + @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.") + class NameConstant(Constant, metaclass=_ABC): + def __new__(cls, value: _ConstantValue, kind: str | None, **kwargs: Unpack[_Attributes]) -> Constant: ... # type: ignore[misc] # pyright: ignore[reportInconsistentConstructor] + + @deprecated("Removed in Python 3.14. Use `ast.Constant` instead.") + class Ellipsis(Constant, metaclass=_ABC): + def __new__(cls, **kwargs: Unpack[_Attributes]) -> Constant: ... # type: ignore[misc] # pyright: ignore[reportInconsistentConstructor] + +# everything below here is defined in ast.py _T = _TypeVar("_T", bound=AST) if sys.version_info >= (3, 13): + @overload + def parse( + source: _T, + filename: str | bytes | os.PathLike[Any] = "", + mode: Literal["exec", "eval", "func_type", "single"] = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + optimize: Literal[-1, 0, 1, 2] = -1, + ) -> _T: ... @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", + filename: str | bytes | os.PathLike[Any] = "", mode: Literal["exec"] = "exec", *, type_comments: bool = False, @@ -196,7 +1767,7 @@ if sys.version_info >= (3, 13): @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], + filename: str | bytes | os.PathLike[Any], mode: Literal["eval"], *, type_comments: bool = False, @@ -206,7 +1777,7 @@ if sys.version_info >= (3, 13): @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], + filename: str | bytes | os.PathLike[Any], mode: Literal["func_type"], *, type_comments: bool = False, @@ -216,7 +1787,7 @@ if sys.version_info >= (3, 13): @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], + filename: str | bytes | os.PathLike[Any], mode: Literal["single"], *, type_comments: bool = False, @@ -253,19 +1824,28 @@ if sys.version_info >= (3, 13): @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", + filename: str | bytes | os.PathLike[Any] = "", mode: str = "exec", *, type_comments: bool = False, feature_version: None | int | tuple[int, int] = None, optimize: Literal[-1, 0, 1, 2] = -1, - ) -> AST: ... + ) -> mod: ... else: + @overload + def parse( + source: _T, + filename: str | bytes | os.PathLike[Any] = "", + mode: Literal["exec", "eval", "func_type", "single"] = "exec", + *, + type_comments: bool = False, + feature_version: None | int | tuple[int, int] = None, + ) -> _T: ... @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", + filename: str | bytes | os.PathLike[Any] = "", mode: Literal["exec"] = "exec", *, type_comments: bool = False, @@ -274,7 +1854,7 @@ else: @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], + filename: str | bytes | os.PathLike[Any], mode: Literal["eval"], *, type_comments: bool = False, @@ -283,7 +1863,7 @@ else: @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], + filename: str | bytes | os.PathLike[Any], mode: Literal["func_type"], *, type_comments: bool = False, @@ -292,7 +1872,7 @@ else: @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any], + filename: str | bytes | os.PathLike[Any], mode: Literal["single"], *, type_comments: bool = False, @@ -325,17 +1905,14 @@ else: @overload def parse( source: str | ReadableBuffer, - filename: str | ReadableBuffer | os.PathLike[Any] = "", + filename: str | bytes | os.PathLike[Any] = "", mode: str = "exec", *, type_comments: bool = False, feature_version: None | int | tuple[int, int] = None, - ) -> AST: ... + ) -> mod: ... -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( @@ -347,25 +1924,176 @@ if sys.version_info >= (3, 13): show_empty: bool = False, ) -> str: ... -elif sys.version_info >= (3, 9): +else: def dump( node: AST, annotate_fields: bool = True, include_attributes: bool = False, *, indent: int | str | None = None ) -> str: ... -else: - def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False) -> str: ... - +def 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: + # All visit methods below can be overwritten by subclasses and return an + # arbitrary value, which is passed to the caller. + def visit(self, node: AST) -> Any: ... + def generic_visit(self, node: AST) -> Any: ... + # The following visit methods are not defined on NodeVisitor, but can + # be implemented by subclasses and are called during a visit if defined. + 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("Removed in Python 3.14. Use `visit_Constant` instead.") + def visit_Num(self, node: Num) -> Any: ... # type: ignore[deprecated] + @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.") + def visit_Str(self, node: Str) -> Any: ... # type: ignore[deprecated] + @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.") + def visit_Bytes(self, node: Bytes) -> Any: ... # type: ignore[deprecated] + @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.") + def visit_NameConstant(self, node: NameConstant) -> Any: ... # type: ignore[deprecated] + @deprecated("Removed in Python 3.14. Use `visit_Constant` instead.") + 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. + +def unparse(ast_obj: AST) -> str: ... + +if sys.version_info >= (3, 14): + def main(args: Sequence[str] | None = None) -> None: ... + +else: + def main() -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/__init__.pyi b/mypy/typeshed/stdlib/asyncio/__init__.pyi index daf28862aa6a9..23cf57aaac335 100644 --- a/mypy/typeshed/stdlib/asyncio/__init__.pyi +++ b/mypy/typeshed/stdlib/asyncio/__init__.pyi @@ -1,3 +1,5 @@ +# This condition is so big, it's clearer to keep to platform condition in two blocks +# Can't NOQA on a specific line: https://github.com/plinss/flake8-noqa/issues/22 import sys from collections.abc import Awaitable, Coroutine, Generator from typing import Any, TypeVar @@ -16,10 +18,11 @@ from .runners import * from .streams import * from .subprocess import * from .tasks import * +from .threads import * from .transports import * -if sys.version_info >= (3, 9): - from .threads import * +if sys.version_info >= (3, 14): + from .graph import * if sys.version_info >= (3, 11): from .taskgroups import * @@ -30,6 +33,967 @@ if sys.platform == "win32": else: from .unix_events import * +if sys.platform == "win32": + if sys.version_info >= (3, 14): + + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "future_discard_from_awaited_by", # from futures + "future_add_to_awaited_by", # from futures + "capture_call_graph", # from graph + "format_call_graph", # from graph + "print_call_graph", # from graph + "FrameCallGraphEntry", # from graph + "FutureCallGraph", # from graph + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "_DefaultEventLoopPolicy", # from windows_events + "_WindowsSelectorEventLoopPolicy", # from windows_events + "_WindowsProactorEventLoopPolicy", # from windows_events + "EventLoop", # from windows_events + ) + elif sys.version_info >= (3, 13): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + "EventLoop", # from windows_events + ) + elif sys.version_info >= (3, 12): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) + elif sys.version_info >= (3, 11): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) + else: + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from windows_events + "ProactorEventLoop", # from windows_events + "IocpProactor", # from windows_events + "DefaultEventLoopPolicy", # from windows_events + "WindowsSelectorEventLoopPolicy", # from windows_events + "WindowsProactorEventLoopPolicy", # from windows_events + ) +else: + if sys.version_info >= (3, 14): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "future_discard_from_awaited_by", # from futures + "future_add_to_awaited_by", # from futures + "capture_call_graph", # from graph + "format_call_graph", # from graph + "print_call_graph", # from graph + "FrameCallGraphEntry", # from graph + "FutureCallGraph", # from graph + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "EventLoop", # from unix_events + ) + elif sys.version_info >= (3, 13): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "QueueShutDown", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + "EventLoop", # from unix_events + ) + elif sys.version_info >= (3, 12): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "create_eager_task_factory", # from tasks + "eager_task_factory", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "TaskGroup", # from taskgroups + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + elif sys.version_info >= (3, 11): + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "BrokenBarrierError", # from exceptions + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "Barrier", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "Runner", # from runners + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "Timeout", # from timeouts + "timeout", # from timeouts + "timeout_at", # from timeouts + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + else: + __all__ = ( + "BaseEventLoop", # from base_events + "Server", # from base_events + "coroutine", # from coroutines + "iscoroutinefunction", # from coroutines + "iscoroutine", # from coroutines + "AbstractEventLoopPolicy", # from events + "AbstractEventLoop", # from events + "AbstractServer", # from events + "Handle", # from events + "TimerHandle", # from events + "get_event_loop_policy", # from events + "set_event_loop_policy", # from events + "get_event_loop", # from events + "set_event_loop", # from events + "new_event_loop", # from events + "get_child_watcher", # from events + "set_child_watcher", # from events + "_set_running_loop", # from events + "get_running_loop", # from events + "_get_running_loop", # from events + "CancelledError", # from exceptions + "InvalidStateError", # from exceptions + "TimeoutError", # from exceptions + "IncompleteReadError", # from exceptions + "LimitOverrunError", # from exceptions + "SendfileNotAvailableError", # from exceptions + "Future", # from futures + "wrap_future", # from futures + "isfuture", # from futures + "Lock", # from locks + "Event", # from locks + "Condition", # from locks + "Semaphore", # from locks + "BoundedSemaphore", # from locks + "BaseProtocol", # from protocols + "Protocol", # from protocols + "DatagramProtocol", # from protocols + "SubprocessProtocol", # from protocols + "BufferedProtocol", # from protocols + "run", # from runners + "Queue", # from queues + "PriorityQueue", # from queues + "LifoQueue", # from queues + "QueueFull", # from queues + "QueueEmpty", # from queues + "StreamReader", # from streams + "StreamWriter", # from streams + "StreamReaderProtocol", # from streams + "open_connection", # from streams + "start_server", # from streams + "open_unix_connection", # from streams + "start_unix_server", # from streams + "create_subprocess_exec", # from subprocess + "create_subprocess_shell", # from subprocess + "Task", # from tasks + "create_task", # from tasks + "FIRST_COMPLETED", # from tasks + "FIRST_EXCEPTION", # from tasks + "ALL_COMPLETED", # from tasks + "wait", # from tasks + "wait_for", # from tasks + "as_completed", # from tasks + "sleep", # from tasks + "gather", # from tasks + "shield", # from tasks + "ensure_future", # from tasks + "run_coroutine_threadsafe", # from tasks + "current_task", # from tasks + "all_tasks", # from tasks + "_register_task", # from tasks + "_unregister_task", # from tasks + "_enter_task", # from tasks + "_leave_task", # from tasks + "to_thread", # from threads + "BaseTransport", # from transports + "ReadTransport", # from transports + "WriteTransport", # from transports + "Transport", # from transports + "DatagramTransport", # from transports + "SubprocessTransport", # from transports + "SelectorEventLoop", # from unix_events + "AbstractChildWatcher", # from unix_events + "SafeChildWatcher", # from unix_events + "FastChildWatcher", # from unix_events + "PidfdChildWatcher", # from unix_events + "MultiLoopChildWatcher", # from unix_events + "ThreadedChildWatcher", # from unix_events + "DefaultEventLoopPolicy", # from unix_events + ) + _T_co = TypeVar("_T_co", covariant=True) # Aliases imported by multiple submodules in typeshed diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index cba2c77995284..1f493210d6655 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -8,15 +8,14 @@ from asyncio.protocols import BaseProtocol from asyncio.tasks import Task from asyncio.transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport from collections.abc import Callable, Iterable, Sequence +from concurrent.futures import Executor, ThreadPoolExecutor from contextvars import Context -from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket +from socket import AddressFamily, AddressInfo, SocketKind, _Address, _RetAddress, socket from typing import IO, Any, Literal, TypeVar, overload from typing_extensions import TypeAlias, TypeVarTuple, Unpack -if sys.version_info >= (3, 9): - __all__ = ("BaseEventLoop", "Server") -else: - __all__ = ("BaseEventLoop",) +# Keep asyncio.__all__ updated with any changes to __all__ here +__all__ = ("BaseEventLoop", "Server") _T = TypeVar("_T") _Ts = TypeVarTuple("_Ts") @@ -95,8 +94,8 @@ class BaseEventLoop(AbstractEventLoop): def call_soon_threadsafe( self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None ) -> Handle: ... - def run_in_executor(self, executor: Any, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... - def set_default_executor(self, executor: Any) -> None: ... + def run_in_executor(self, executor: Executor | None, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... + def set_default_executor(self, executor: ThreadPoolExecutor) -> None: ... # type: ignore[override] # Network I/O methods returning Futures. async def getaddrinfo( self, @@ -236,8 +235,8 @@ class BaseEventLoop(AbstractEventLoop): host: str | Sequence[str] | None = None, port: int = ..., *, - family: int = ..., - flags: int = ..., + family: int = 0, + flags: int = 1, sock: None = None, backlog: int = 100, ssl: _SSLContext = None, @@ -255,8 +254,8 @@ class BaseEventLoop(AbstractEventLoop): host: None = None, port: None = None, *, - family: int = ..., - flags: int = ..., + family: int = 0, + flags: int = 1, sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, @@ -275,8 +274,8 @@ class BaseEventLoop(AbstractEventLoop): host: str | Sequence[str] | None = None, port: int = ..., *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: None = None, backlog: int = 100, ssl: _SSLContext = None, @@ -293,8 +292,8 @@ class BaseEventLoop(AbstractEventLoop): host: None = None, port: None = None, *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, @@ -312,8 +311,8 @@ class BaseEventLoop(AbstractEventLoop): host: str | Sequence[str] | None = None, port: int = ..., *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: None = None, backlog: int = 100, ssl: _SSLContext = None, @@ -329,8 +328,8 @@ class BaseEventLoop(AbstractEventLoop): host: None = None, port: None = None, *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, @@ -452,6 +451,7 @@ class BaseEventLoop(AbstractEventLoop): bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, + text: Literal[False] | None = None, **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... def add_reader(self, fd: FileDescriptorLike, callback: Callable[[Unpack[_Ts]], Any], *args: Unpack[_Ts]) -> None: ... @@ -482,7 +482,7 @@ class BaseEventLoop(AbstractEventLoop): def set_debug(self, enabled: bool) -> None: ... if sys.version_info >= (3, 12): async def shutdown_default_executor(self, timeout: float | None = None) -> None: ... - elif sys.version_info >= (3, 9): + else: async def shutdown_default_executor(self) -> None: ... def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/base_futures.pyi b/mypy/typeshed/stdlib/asyncio/base_futures.pyi index 55d2fbdbdb627..2cd0f2e3a7e4a 100644 --- a/mypy/typeshed/stdlib/asyncio/base_futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_futures.pyi @@ -1,19 +1,17 @@ +from _asyncio import Future from collections.abc import Callable, Sequence from contextvars import Context from typing import Any, Final +from typing_extensions import TypeIs from . import futures __all__ = () -# asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py -# but it leads to circular import error in pytype tool. -# That's why the import order is reversed. -from .futures import isfuture as isfuture - _PENDING: Final = "PENDING" # undocumented _CANCELLED: Final = "CANCELLED" # undocumented _FINISHED: Final = "FINISHED" # undocumented +def isfuture(obj: object) -> TypeIs[Future[Any]]: ... 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/coroutines.pyi b/mypy/typeshed/stdlib/asyncio/coroutines.pyi index bc797de7fd518..59212f4ec398b 100644 --- a/mypy/typeshed/stdlib/asyncio/coroutines.pyi +++ b/mypy/typeshed/stdlib/asyncio/coroutines.pyi @@ -1,8 +1,9 @@ import sys from collections.abc import Awaitable, Callable, Coroutine from typing import Any, TypeVar, overload -from typing_extensions import ParamSpec, TypeGuard, TypeIs +from typing_extensions import ParamSpec, TypeGuard, TypeIs, deprecated +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ("iscoroutinefunction", "iscoroutine") else: @@ -13,6 +14,7 @@ _FunctionT = TypeVar("_FunctionT", bound=Callable[..., Any]) _P = ParamSpec("_P") if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `async def` instead.") def coroutine(func: _FunctionT) -> _FunctionT: ... @overload diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index eed688fc792aa..5dc698bc5e15c 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -1,11 +1,18 @@ 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 +from concurrent.futures import Executor from contextvars import Context -from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket -from typing import IO, Any, Literal, Protocol, TypeVar, overload +from socket import AddressFamily, AddressInfo, SocketKind, _Address, _RetAddress, socket +from typing import IO, Any, Literal, Protocol, TypeVar, overload, type_check_only from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack, deprecated from . import _AwaitableLike, _CoroutineLike @@ -14,11 +21,13 @@ from .futures import Future from .protocols import BaseProtocol from .tasks import Task from .transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport -from .unix_events import AbstractChildWatcher +if sys.version_info < (3, 14): + from .unix_events import AbstractChildWatcher + +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 14): __all__ = ( - "AbstractEventLoopPolicy", "AbstractEventLoop", "AbstractServer", "Handle", @@ -59,10 +68,12 @@ _ExceptionHandler: TypeAlias = Callable[[AbstractEventLoop, _Context], object] _ProtocolFactory: TypeAlias = Callable[[], BaseProtocol] _SSLContext: TypeAlias = bool | None | ssl.SSLContext +@type_check_only class _TaskFactory(Protocol): def __call__(self, loop: AbstractEventLoop, factory: _CoroutineLike[_T], /) -> Future[_T]: ... class Handle: + __slots__ = ("_callback", "_args", "_cancelled", "_loop", "_source_traceback", "_repr", "__weakref__", "_context") _cancelled: bool _args: Sequence[Any] def __init__( @@ -75,6 +86,7 @@ class Handle: def get_context(self) -> Context: ... class TimerHandle(Handle): + __slots__ = ["_scheduled", "_when"] def __init__( self, when: float, @@ -130,27 +142,19 @@ class AbstractEventLoop: @abstractmethod async def shutdown_asyncgens(self) -> None: ... # Methods scheduling callbacks. All these return Handles. - if sys.version_info >= (3, 9): # "context" added in 3.9.10/3.10.2 - @abstractmethod - def call_soon( - self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None - ) -> Handle: ... - @abstractmethod - def call_later( - self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None - ) -> TimerHandle: ... - @abstractmethod - def call_at( - self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None - ) -> TimerHandle: ... - else: - @abstractmethod - def call_soon(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ... - @abstractmethod - def call_later(self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> TimerHandle: ... - @abstractmethod - def call_at(self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> TimerHandle: ... - + # "context" added in 3.9.10/3.10.2 for call_* + @abstractmethod + def call_soon( + self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> Handle: ... + @abstractmethod + def call_later( + self, delay: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> TimerHandle: ... + @abstractmethod + def call_at( + self, when: float, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> TimerHandle: ... @abstractmethod def time(self) -> float: ... # Future methods @@ -171,19 +175,15 @@ class AbstractEventLoop: @abstractmethod def get_task_factory(self) -> _TaskFactory | None: ... # Methods for interacting with threads - if sys.version_info >= (3, 9): # "context" added in 3.9.10/3.10.2 - @abstractmethod - def call_soon_threadsafe( - self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None - ) -> Handle: ... - else: - @abstractmethod - def call_soon_threadsafe(self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> Handle: ... - + # "context" added in 3.9.10/3.10.2 @abstractmethod - def run_in_executor(self, executor: Any, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... + def call_soon_threadsafe( + self, callback: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts], context: Context | None = None + ) -> Handle: ... @abstractmethod - def set_default_executor(self, executor: Any) -> None: ... + def run_in_executor(self, executor: Executor | None, func: Callable[[Unpack[_Ts]], _T], *args: Unpack[_Ts]) -> Future[_T]: ... + @abstractmethod + def set_default_executor(self, executor: Executor) -> None: ... # Network I/O methods returning Futures. @abstractmethod async def getaddrinfo( @@ -289,8 +289,8 @@ class AbstractEventLoop: host: str | Sequence[str] | None = None, port: int = ..., *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: None = None, backlog: int = 100, ssl: _SSLContext = None, @@ -309,8 +309,8 @@ class AbstractEventLoop: host: None = None, port: None = None, *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, @@ -330,8 +330,8 @@ class AbstractEventLoop: host: str | Sequence[str] | None = None, port: int = ..., *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: None = None, backlog: int = 100, ssl: _SSLContext = None, @@ -349,8 +349,8 @@ class AbstractEventLoop: host: None = None, port: None = None, *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, @@ -369,8 +369,8 @@ class AbstractEventLoop: host: str | Sequence[str] | None = None, port: int = ..., *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: None = None, backlog: int = 100, ssl: _SSLContext = None, @@ -387,8 +387,8 @@ class AbstractEventLoop: host: None = None, port: None = None, *, - family: int = ..., - flags: int = ..., + family: int = AddressFamily.AF_UNSPEC, + flags: int = AddressInfo.AI_PASSIVE, sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, @@ -536,7 +536,7 @@ class AbstractEventLoop: bufsize: Literal[0] = 0, encoding: None = None, errors: None = None, - text: Literal[False] | None = ..., + text: Literal[False] | None = None, **kwargs: Any, ) -> tuple[SubprocessTransport, _ProtocolT]: ... @abstractmethod @@ -599,25 +599,34 @@ class AbstractEventLoop: def get_debug(self) -> bool: ... @abstractmethod def set_debug(self, enabled: bool) -> None: ... - if sys.version_info >= (3, 9): + @abstractmethod + async def shutdown_default_executor(self) -> None: ... + +if sys.version_info >= (3, 14): + class _AbstractEventLoopPolicy: + @abstractmethod + def get_event_loop(self) -> AbstractEventLoop: ... @abstractmethod - async def shutdown_default_executor(self) -> None: ... + def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def new_event_loop(self) -> AbstractEventLoop: ... -class AbstractEventLoopPolicy: - @abstractmethod - def get_event_loop(self) -> AbstractEventLoop: ... - @abstractmethod - def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ... - @abstractmethod - def new_event_loop(self) -> AbstractEventLoop: ... - # Child processes handling (Unix only). - if sys.version_info < (3, 14): +else: + @type_check_only + class _AbstractEventLoopPolicy: + @abstractmethod + def get_event_loop(self) -> AbstractEventLoop: ... + @abstractmethod + def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ... + @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") + @deprecated("Deprecated since Python 3.12; 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") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... else: @abstractmethod @@ -625,28 +634,42 @@ class AbstractEventLoopPolicy: @abstractmethod def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... -class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta): - def get_event_loop(self) -> AbstractEventLoop: ... - def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ... - def new_event_loop(self) -> AbstractEventLoop: ... + AbstractEventLoopPolicy = _AbstractEventLoopPolicy + +if sys.version_info >= (3, 14): + class _BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy, metaclass=ABCMeta): + def get_event_loop(self) -> AbstractEventLoop: ... + def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ... + def new_event_loop(self) -> AbstractEventLoop: ... + +else: + class BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy, metaclass=ABCMeta): + def get_event_loop(self) -> AbstractEventLoop: ... + def set_event_loop(self, loop: AbstractEventLoop | None) -> None: ... + def new_event_loop(self) -> AbstractEventLoop: ... + +if sys.version_info >= (3, 14): + def _get_event_loop_policy() -> _AbstractEventLoopPolicy: ... + def _set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ... + @deprecated("Deprecated since Python 3.14; will be removed in Python 3.16.") + def get_event_loop_policy() -> _AbstractEventLoopPolicy: ... + @deprecated("Deprecated since Python 3.14; will be removed in Python 3.16.") + def set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ... + +else: + def get_event_loop_policy() -> _AbstractEventLoopPolicy: ... + def set_event_loop_policy(policy: _AbstractEventLoopPolicy | None) -> None: ... -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, 14): if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") def get_child_watcher() -> AbstractChildWatcher: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + @deprecated("Deprecated since Python 3.12; 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: ... - -def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... -def _get_running_loop() -> AbstractEventLoop: ... -def get_running_loop() -> AbstractEventLoop: ... diff --git a/mypy/typeshed/stdlib/asyncio/exceptions.pyi b/mypy/typeshed/stdlib/asyncio/exceptions.pyi index 0746394d582fa..759838f45de47 100644 --- a/mypy/typeshed/stdlib/asyncio/exceptions.pyi +++ b/mypy/typeshed/stdlib/asyncio/exceptions.pyi @@ -1,5 +1,6 @@ import sys +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ( "BrokenBarrierError", diff --git a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi index 41505b14cd087..597eb9e56e1a1 100644 --- a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi +++ b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi @@ -3,9 +3,10 @@ import sys import traceback from collections.abc import Iterable from types import FrameType, FunctionType -from typing import Any, overload +from typing import Any, overload, type_check_only from typing_extensions import TypeAlias +@type_check_only class _HasWrapper: __wrapper__: _HasWrapper | FunctionType diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index e19fd53f33111..c907c7036b040 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -1,57 +1,19 @@ 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 TypeVar +from .base_futures import isfuture as isfuture from .events import AbstractEventLoop -if sys.version_info >= (3, 9): - from types import GenericAlias +# Keep asyncio.__all__ updated with any changes to __all__ here +if sys.version_info >= (3, 14): + from _asyncio import future_add_to_awaited_by, future_discard_from_awaited_by -__all__ = ("Future", "wrap_future", "isfuture") + __all__ = ("Future", "wrap_future", "isfuture", "future_discard_from_awaited_by", "future_add_to_awaited_by") +else: + __all__ = ("Future", "wrap_future", "isfuture") _T = TypeVar("_T") -# asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py -# but it leads to circular import error in pytype tool. -# That's why the import order is reversed. -def isfuture(obj: object) -> TypeIs[Future[Any]]: ... - -class Future(Awaitable[_T], Iterable[_T]): - _state: str - @property - def _exception(self) -> BaseException | None: ... - _blocking: bool - @property - def _log_traceback(self) -> bool: ... - @_log_traceback.setter - def _log_traceback(self, val: Literal[False]) -> None: ... - _asyncio_future_blocking: bool # is a part of duck-typing contract for `Future` - def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ... - def __del__(self) -> None: ... - def get_loop(self) -> AbstractEventLoop: ... - @property - def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ... - def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ... - if sys.version_info >= (3, 9): - def cancel(self, msg: Any | None = None) -> bool: ... - else: - def cancel(self) -> bool: ... - - def cancelled(self) -> bool: ... - def done(self) -> bool: ... - def result(self) -> _T: ... - def exception(self) -> BaseException | None: ... - def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ... - def set_result(self, result: _T, /) -> None: ... - def set_exception(self, exception: type | BaseException, /) -> None: ... - def __iter__(self) -> Generator[Any, None, _T]: ... - def __await__(self) -> Generator[Any, None, _T]: ... - @property - def _loop(self) -> AbstractEventLoop: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ... diff --git a/mypy/typeshed/stdlib/asyncio/graph.pyi b/mypy/typeshed/stdlib/asyncio/graph.pyi new file mode 100644 index 0000000000000..18a8a6457d757 --- /dev/null +++ b/mypy/typeshed/stdlib/asyncio/graph.pyi @@ -0,0 +1,28 @@ +import sys +from _typeshed import SupportsWrite +from asyncio import Future +from dataclasses import dataclass +from types import FrameType +from typing import Any, overload + +if sys.version_info >= (3, 14): + __all__ = ("capture_call_graph", "format_call_graph", "print_call_graph", "FrameCallGraphEntry", "FutureCallGraph") + + @dataclass(frozen=True, slots=True) + class FrameCallGraphEntry: + frame: FrameType + + @dataclass(frozen=True, slots=True) + class FutureCallGraph: + future: Future[Any] + call_stack: tuple[FrameCallGraphEntry, ...] + awaited_by: tuple[FutureCallGraph, ...] + + @overload + def capture_call_graph(future: None = None, /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ... + @overload + def capture_call_graph(future: Future[Any], /, *, depth: int = 1, limit: int | None = None) -> FutureCallGraph | None: ... + def format_call_graph(future: Future[Any] | None = None, /, *, depth: int = 1, limit: int | None = None) -> str: ... + def print_call_graph( + future: Future[Any] | None = None, /, *, file: SupportsWrite[str] | None = None, depth: int = 1, limit: int | None = None + ) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi index 0114aeb233292..17390b0c5a0ee 100644 --- a/mypy/typeshed/stdlib/asyncio/locks.pyi +++ b/mypy/typeshed/stdlib/asyncio/locks.pyi @@ -2,7 +2,7 @@ import enum import sys from _typeshed import Unused from collections import deque -from collections.abc import Callable, Generator +from collections.abc import Callable from types import TracebackType from typing import Any, Literal, TypeVar from typing_extensions import Self @@ -15,6 +15,7 @@ if sys.version_info >= (3, 10): else: _LoopBoundMixin = object +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ("Lock", "Event", "Condition", "Semaphore", "BoundedSemaphore", "Barrier") else: @@ -22,29 +23,11 @@ else: _T = TypeVar("_T") -if sys.version_info >= (3, 9): - class _ContextManagerMixin: - async def __aenter__(self) -> None: ... - async def __aexit__( - self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None - ) -> None: ... - -else: - class _ContextManager: - def __init__(self, lock: Lock | Semaphore) -> None: ... - def __enter__(self) -> None: ... - def __exit__(self, *args: Unused) -> None: ... - - class _ContextManagerMixin: - # Apparently this exists to *prohibit* use as a context manager. - # def __enter__(self) -> NoReturn: ... see: https://github.com/python/typing/issues/1043 - # def __exit__(self, *args: Any) -> None: ... - def __iter__(self) -> Generator[Any, None, _ContextManager]: ... - def __await__(self) -> Generator[Any, None, _ContextManager]: ... - async def __aenter__(self) -> None: ... - async def __aexit__( - self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None - ) -> None: ... +class _ContextManagerMixin: + async def __aenter__(self) -> None: ... + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None + ) -> None: ... class Lock(_ContextManagerMixin, _LoopBoundMixin): _waiters: deque[Future[Any]] | None diff --git a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi index 957fdd6ce2559..909d671df289d 100644 --- a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi @@ -62,3 +62,4 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, _ProactorBaseWritePip class BaseProactorEventLoop(base_events.BaseEventLoop): def __init__(self, proactor: Any) -> None: ... + async def sock_recv(self, sock: socket, n: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/asyncio/protocols.pyi b/mypy/typeshed/stdlib/asyncio/protocols.pyi index 5173b74ed5a03..3a8965f03e29c 100644 --- a/mypy/typeshed/stdlib/asyncio/protocols.pyi +++ b/mypy/typeshed/stdlib/asyncio/protocols.pyi @@ -2,24 +2,30 @@ from _typeshed import ReadableBuffer from asyncio import transports from typing import Any +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseProtocol", "Protocol", "DatagramProtocol", "SubprocessProtocol", "BufferedProtocol") class BaseProtocol: + __slots__ = () def connection_made(self, transport: transports.BaseTransport) -> None: ... def connection_lost(self, exc: Exception | None) -> None: ... def pause_writing(self) -> None: ... def resume_writing(self) -> None: ... class Protocol(BaseProtocol): + # Need annotation or mypy will complain about 'Cannot determine type of "__slots__" in base class' + __slots__: tuple[str, ...] = () def data_received(self, data: bytes) -> None: ... def eof_received(self) -> bool | None: ... class BufferedProtocol(BaseProtocol): + __slots__ = () def get_buffer(self, sizehint: int) -> ReadableBuffer: ... def buffer_updated(self, nbytes: int) -> None: ... def eof_received(self) -> bool | None: ... class DatagramProtocol(BaseProtocol): + __slots__ = () def connection_made(self, transport: transports.DatagramTransport) -> None: ... # type: ignore[override] # addr can be a tuple[int, int] for some unusual protocols like socket.AF_NETLINK. # Use tuple[str | Any, int] to not cause typechecking issues on most usual cases. @@ -29,6 +35,7 @@ class DatagramProtocol(BaseProtocol): def error_received(self, exc: Exception) -> None: ... class SubprocessProtocol(BaseProtocol): + __slots__: tuple[str, ...] = () def pipe_data_received(self, fd: int, data: bytes) -> None: ... def pipe_connection_lost(self, fd: int, exc: Exception | None) -> None: ... def process_exited(self) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi index 895205aa95197..2fa2226d0e6ae 100644 --- a/mypy/typeshed/stdlib/asyncio/queues.pyi +++ b/mypy/typeshed/stdlib/asyncio/queues.pyi @@ -1,10 +1,9 @@ import sys +from _typeshed import SupportsRichComparisonT from asyncio.events import AbstractEventLoop +from types import GenericAlias from typing import Any, Generic, TypeVar -if sys.version_info >= (3, 9): - from types import GenericAlias - if sys.version_info >= (3, 10): from .mixins import _LoopBoundMixin else: @@ -13,6 +12,7 @@ else: class QueueEmpty(Exception): ... class QueueFull(Exception): ... +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 13): __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty", "QueueShutDown") @@ -47,10 +47,9 @@ class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 def get_nowait(self) -> _T: ... async def join(self) -> None: ... def task_done(self) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, type: Any, /) -> GenericAlias: ... + 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 PriorityQueue(Queue[SupportsRichComparisonT]): ... class LifoQueue(Queue[_T]): ... diff --git a/mypy/typeshed/stdlib/asyncio/runners.pyi b/mypy/typeshed/stdlib/asyncio/runners.pyi index 37a85b709cdc4..919e6521f8a15 100644 --- a/mypy/typeshed/stdlib/asyncio/runners.pyi +++ b/mypy/typeshed/stdlib/asyncio/runners.pyi @@ -7,6 +7,7 @@ from typing_extensions import Self from .events import AbstractEventLoop +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 11): __all__ = ("Runner", "run") else: @@ -25,7 +26,7 @@ if sys.version_info >= (3, 11): if sys.version_info >= (3, 12): def run( - main: Coroutine[Any, Any, _T], *, debug: bool | None = ..., loop_factory: Callable[[], AbstractEventLoop] | None = ... + main: Coroutine[Any, Any, _T], *, debug: bool | None = None, loop_factory: Callable[[], AbstractEventLoop] | None = None ) -> _T: ... else: diff --git a/mypy/typeshed/stdlib/asyncio/selector_events.pyi b/mypy/typeshed/stdlib/asyncio/selector_events.pyi index 430f2dd405cd6..18c5df033e2f6 100644 --- a/mypy/typeshed/stdlib/asyncio/selector_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/selector_events.pyi @@ -1,4 +1,5 @@ import selectors +from socket import socket from . import base_events @@ -6,3 +7,4 @@ __all__ = ("BaseSelectorEventLoop",) class BaseSelectorEventLoop(base_events.BaseEventLoop): def __init__(self, selector: selectors.BaseSelector | None = None) -> None: ... + async def sock_recv(self, sock: socket, n: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index ded1933dd6597..ab102f124c2e8 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -76,7 +76,7 @@ class _SSLProtocolTransport(transports._FlowControlMixin, transports.Transport): def get_extra_info(self, name: str, default: Any | None = None) -> dict[str, Any]: ... @property def _protocol_paused(self) -> bool: ... - def write(self, data: bytes | bytearray | memoryview) -> None: ... + def write(self, data: bytes | bytearray | memoryview[Any]) -> None: ... # any memoryview format or shape def can_write_eof(self) -> Literal[False]: ... if sys.version_info >= (3, 11): def get_write_buffer_limits(self) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 0be5249e2169f..33cffb11ed780 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -1,14 +1,15 @@ 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 import Any, Protocol, SupportsIndex, type_check_only from typing_extensions import Self, TypeAlias from . import events, protocols, transports from .base_events import Server +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.platform == "win32": __all__ = ("StreamReader", "StreamWriter", "StreamReaderProtocol", "open_connection", "start_server") else: @@ -24,6 +25,7 @@ else: _ClientConnectedCallback: TypeAlias = Callable[[StreamReader, StreamWriter], Awaitable[None] | None] +@type_check_only class _ReaduntilBuffer(ReadableBuffer, Sized, Protocol): ... if sys.version_info >= (3, 10): @@ -32,7 +34,7 @@ if sys.version_info >= (3, 10): port: int | str | None = None, *, limit: int = 65536, - ssl_handshake_timeout: float | None = ..., + ssl_handshake_timeout: float | None = None, **kwds: Any, ) -> tuple[StreamReader, StreamWriter]: ... async def start_server( @@ -41,7 +43,7 @@ if sys.version_info >= (3, 10): port: int | str | None = None, *, limit: int = 65536, - ssl_handshake_timeout: float | None = ..., + ssl_handshake_timeout: float | None = None, **kwds: Any, ) -> Server: ... @@ -52,7 +54,7 @@ else: *, loop: events.AbstractEventLoop | None = None, limit: int = 65536, - ssl_handshake_timeout: float | None = ..., + ssl_handshake_timeout: float | None = None, **kwds: Any, ) -> tuple[StreamReader, StreamWriter]: ... async def start_server( @@ -62,7 +64,7 @@ else: *, loop: events.AbstractEventLoop | None = None, limit: int = 65536, - ssl_handshake_timeout: float | None = ..., + ssl_handshake_timeout: float | None = None, **kwds: Any, ) -> Server: ... @@ -137,7 +139,7 @@ class StreamWriter: 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/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi index 19452d4eb469c..ceee2b5b90a09 100644 --- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi @@ -5,6 +5,7 @@ from asyncio import events, protocols, streams, transports from collections.abc import Callable, Collection from typing import IO, Any, Literal +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("create_subprocess_exec", "create_subprocess_shell") PIPE: int @@ -59,7 +60,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), group: None | str | int = None, extra_groups: None | Collection[str | int] = None, user: None | str | int = None, @@ -91,7 +92,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), group: None | str | int = None, extra_groups: None | Collection[str | int] = None, user: None | str | int = None, @@ -125,7 +126,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), group: None | str | int = None, extra_groups: None | Collection[str | int] = None, user: None | str | int = None, @@ -156,7 +157,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), group: None | str | int = None, extra_groups: None | Collection[str | int] = None, user: None | str | int = None, @@ -190,7 +191,7 @@ else: # >= 3.9 creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), group: None | str | int = None, extra_groups: None | Collection[str | int] = None, user: None | str | int = None, @@ -221,7 +222,7 @@ else: # >= 3.9 creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), group: None | str | int = None, extra_groups: None | Collection[str | int] = None, user: None | str | int = None, diff --git a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi index aec3f1127f159..30b7c9129f6f9 100644 --- a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi +++ b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi @@ -8,6 +8,7 @@ from . import _CoroutineLike from .events import AbstractEventLoop from .tasks import Task +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 12): __all__ = ("TaskGroup",) else: diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index bb423e8573993..1442f7400a9c9 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -1,19 +1,24 @@ 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, Final, Literal, Protocol, TypeVar, overload, type_check_only 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 +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.version_info >= (3, 12): __all__ = ( "Task", @@ -74,13 +79,20 @@ 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 +FIRST_COMPLETED: Final = concurrent.futures.FIRST_COMPLETED +FIRST_EXCEPTION: Final = concurrent.futures.FIRST_EXCEPTION +ALL_COMPLETED: Final = concurrent.futures.ALL_COMPLETED -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 13): + @type_check_only + 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: @@ -337,7 +349,8 @@ else: *coros_or_futures: _FutureLike[_T], loop: AbstractEventLoop | None = None, return_exceptions: bool ) -> Future[list[_T | BaseException]]: ... -def run_coroutine_threadsafe(coro: _FutureLike[_T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ... +# unlike some asyncio apis, This does strict runtime checking of actually being a coroutine, not of any future-like. +def run_coroutine_threadsafe(coro: Coroutine[Any, Any, _T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ... if sys.version_info >= (3, 10): def shield(arg: _FutureLike[_T]) -> Future[_T]: ... @@ -395,62 +408,8 @@ else: 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: ... + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co] def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... @@ -460,13 +419,34 @@ 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, 14): + def eager_task_factory( + loop: AbstractEventLoop | None, + coro: _TaskCompatibleCoro[_T_co], + *, + name: str | None = None, + context: Context | None = None, + eager_start: bool = True, + ) -> Task[_T_co]: ... + +elif sys.version_info >= (3, 12): + def eager_task_factory( + loop: AbstractEventLoop | None, + coro: _TaskCompatibleCoro[_T_co], + *, + name: str | None = None, + context: Context | None = None, + ) -> Task[_T_co]: ... if sys.version_info >= (3, 12): _TaskT_co = TypeVar("_TaskT_co", bound=Task[Any], covariant=True) + @type_check_only class _CustomTaskConstructor(Protocol[_TaskT_co]): def __call__( self, @@ -479,6 +459,7 @@ if sys.version_info >= (3, 12): eager_start: bool, ) -> _TaskT_co: ... + @type_check_only class _EagerTaskFactoryType(Protocol[_TaskT_co]): def __call__( self, @@ -492,13 +473,3 @@ if sys.version_info >= (3, 12): def create_eager_task_factory( custom_task_constructor: _CustomTaskConstructor[_TaskT_co], ) -> _EagerTaskFactoryType[_TaskT_co]: ... - def eager_task_factory( - loop: AbstractEventLoop | None, - coro: _TaskCompatibleCoro[_T_co], - *, - 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/threads.pyi b/mypy/typeshed/stdlib/asyncio/threads.pyi index 799efd25fea4d..00aae2ea814cb 100644 --- a/mypy/typeshed/stdlib/asyncio/threads.pyi +++ b/mypy/typeshed/stdlib/asyncio/threads.pyi @@ -2,6 +2,7 @@ from collections.abc import Callable from typing import TypeVar from typing_extensions import ParamSpec +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("to_thread",) _P = ParamSpec("_P") _R = TypeVar("_R") diff --git a/mypy/typeshed/stdlib/asyncio/timeouts.pyi b/mypy/typeshed/stdlib/asyncio/timeouts.pyi index 2f0e40e256801..668cccbfe8b18 100644 --- a/mypy/typeshed/stdlib/asyncio/timeouts.pyi +++ b/mypy/typeshed/stdlib/asyncio/timeouts.pyi @@ -2,6 +2,7 @@ from types import TracebackType from typing import final from typing_extensions import Self +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("Timeout", "timeout", "timeout_at") @final diff --git a/mypy/typeshed/stdlib/asyncio/tools.pyi b/mypy/typeshed/stdlib/asyncio/tools.pyi new file mode 100644 index 0000000000000..bc8b809b9c055 --- /dev/null +++ b/mypy/typeshed/stdlib/asyncio/tools.pyi @@ -0,0 +1,46 @@ +import sys +from collections.abc import Iterable +from enum import Enum +from typing import NamedTuple, SupportsIndex, type_check_only + +@type_check_only +class _AwaitedInfo(NamedTuple): # AwaitedInfo_Type from _remote_debugging + thread_id: int + awaited_by: list[_TaskInfo] + +@type_check_only +class _TaskInfo(NamedTuple): # TaskInfo_Type from _remote_debugging + task_id: int + task_name: str + coroutine_stack: list[_CoroInfo] + awaited_by: list[_CoroInfo] + +@type_check_only +class _CoroInfo(NamedTuple): # CoroInfo_Type from _remote_debugging + call_stack: list[_FrameInfo] + task_name: int | str + +@type_check_only +class _FrameInfo(NamedTuple): # FrameInfo_Type from _remote_debugging + filename: str + lineno: int + funcname: str + +class NodeType(Enum): + COROUTINE = 1 + TASK = 2 + +class CycleFoundException(Exception): + cycles: list[list[int]] + id2name: dict[int, str] + def __init__(self, cycles: list[list[int]], id2name: dict[int, str]) -> None: ... + +def get_all_awaited_by(pid: SupportsIndex) -> list[_AwaitedInfo]: ... +def build_async_tree(result: Iterable[_AwaitedInfo], task_emoji: str = "(T)", cor_emoji: str = "") -> list[list[str]]: ... +def build_task_table(result: Iterable[_AwaitedInfo]) -> list[list[int | str]]: ... + +if sys.version_info >= (3, 14): + def exit_with_permission_help_text() -> None: ... + +def display_awaited_by_tasks_table(pid: SupportsIndex) -> None: ... +def display_awaited_by_tasks_tree(pid: SupportsIndex) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi index 531f776724384..cc870d5e0b9ad 100644 --- a/mypy/typeshed/stdlib/asyncio/transports.pyi +++ b/mypy/typeshed/stdlib/asyncio/transports.pyi @@ -4,9 +4,11 @@ from collections.abc import Iterable, Mapping from socket import _Address from typing import Any +# Keep asyncio.__all__ updated with any changes to __all__ here __all__ = ("BaseTransport", "ReadTransport", "WriteTransport", "Transport", "DatagramTransport", "SubprocessTransport") class BaseTransport: + __slots__ = ("_extra",) def __init__(self, extra: Mapping[str, Any] | None = None) -> None: ... def get_extra_info(self, name: str, default: Any = None) -> Any: ... def is_closing(self) -> bool: ... @@ -15,27 +17,34 @@ class BaseTransport: def get_protocol(self) -> BaseProtocol: ... class ReadTransport(BaseTransport): + __slots__ = () def is_reading(self) -> bool: ... def pause_reading(self) -> None: ... def resume_reading(self) -> None: ... class WriteTransport(BaseTransport): + __slots__ = () def set_write_buffer_limits(self, high: int | None = None, low: int | None = None) -> None: ... def get_write_buffer_size(self) -> int: ... def get_write_buffer_limits(self) -> tuple[int, int]: ... - def write(self, data: bytes | bytearray | memoryview) -> None: ... - def writelines(self, list_of_data: Iterable[bytes | bytearray | memoryview]) -> None: ... + def write(self, data: bytes | bytearray | memoryview[Any]) -> None: ... # any memoryview format or shape + def writelines( + self, list_of_data: Iterable[bytes | bytearray | memoryview[Any]] + ) -> None: ... # any memoryview format or shape def write_eof(self) -> None: ... def can_write_eof(self) -> bool: ... def abort(self) -> None: ... -class Transport(ReadTransport, WriteTransport): ... +class Transport(ReadTransport, WriteTransport): + __slots__ = () class DatagramTransport(BaseTransport): + __slots__ = () def sendto(self, data: bytes | bytearray | memoryview, addr: _Address | None = None) -> None: ... def abort(self) -> None: ... class SubprocessTransport(BaseTransport): + __slots__ = () def get_pid(self) -> int: ... def get_returncode(self) -> int | None: ... def get_pipe_transport(self, fd: int) -> BaseTransport | None: ... @@ -44,4 +53,5 @@ class SubprocessTransport(BaseTransport): def kill(self) -> None: ... class _FlowControlMixin(Transport): + __slots__ = ("_loop", "_protocol_paused", "_high_water", "_low_water") def __init__(self, extra: Mapping[str, Any] | None = None, loop: AbstractEventLoop | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/trsock.pyi b/mypy/typeshed/stdlib/asyncio/trsock.pyi index e74cf6fd4e052..492f1e42adf20 100644 --- a/mypy/typeshed/stdlib/asyncio/trsock.pyi +++ b/mypy/typeshed/stdlib/asyncio/trsock.pyi @@ -5,7 +5,7 @@ from builtins import type as Type # alias to avoid name clashes with property n from collections.abc import Iterable from types import TracebackType from typing import Any, BinaryIO, NoReturn, overload -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated # These are based in socket, maybe move them out into _typeshed.pyi or such _Address: TypeAlias = socket._Address @@ -14,6 +14,7 @@ _WriteBuffer: TypeAlias = bytearray | memoryview _CMSG: TypeAlias = tuple[int, int, bytes] class TransportSocket: + __slots__ = ("_sock",) def __init__(self, sock: socket.socket) -> None: ... @property def family(self) -> int: ... @@ -42,53 +43,87 @@ class TransportSocket: def setblocking(self, flag: bool) -> None: ... if sys.version_info < (3, 11): def _na(self, what: str) -> None: ... + @deprecated("Removed in Python 3.11") def accept(self) -> tuple[socket.socket, _RetAddress]: ... + @deprecated("Removed in Python 3.11") def connect(self, address: _Address) -> None: ... + @deprecated("Removed in Python 3.11") def connect_ex(self, address: _Address) -> int: ... + @deprecated("Removed in Python 3.11") def bind(self, address: _Address) -> None: ... if sys.platform == "win32": + @deprecated("Removed in Python 3.11") def ioctl(self, control: int, option: int | tuple[int, int, int] | bool) -> None: ... else: + @deprecated("Removed in Python 3.11") def ioctl(self, control: int, option: int | tuple[int, int, int] | bool) -> NoReturn: ... + @deprecated("Removed in Python 3.11") def listen(self, backlog: int = ..., /) -> None: ... + @deprecated("Removed in Python 3.11") def makefile(self) -> BinaryIO: ... - def sendfile(self, file: BinaryIO, offset: int = ..., count: int | None = ...) -> int: ... + @deprecated("Rmoved in Python 3.11") + def sendfile(self, file: BinaryIO, offset: int = 0, count: int | None = None) -> int: ... + @deprecated("Removed in Python 3.11") def close(self) -> None: ... + @deprecated("Removed in Python 3.11") def detach(self) -> int: ... if sys.platform == "linux": + @deprecated("Removed in Python 3.11") def sendmsg_afalg( - self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ... + self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = 0 ) -> int: ... else: + @deprecated("Removed in Python 3.11.") def sendmsg_afalg( - self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ... + self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = 0 ) -> NoReturn: ... + @deprecated("Removed in Python 3.11.") def sendmsg( - self, buffers: Iterable[ReadableBuffer], ancdata: Iterable[_CMSG] = ..., flags: int = ..., address: _Address = ..., / + self, + buffers: Iterable[ReadableBuffer], + ancdata: Iterable[_CMSG] = ..., + flags: int = 0, + address: _Address | None = None, + /, ) -> int: ... @overload + @deprecated("Removed in Python 3.11.") def sendto(self, data: ReadableBuffer, address: _Address) -> int: ... @overload + @deprecated("Removed in Python 3.11.") def sendto(self, data: ReadableBuffer, flags: int, address: _Address) -> int: ... - def send(self, data: ReadableBuffer, flags: int = ...) -> int: ... - def sendall(self, data: ReadableBuffer, flags: int = ...) -> None: ... + @deprecated("Removed in Python 3.11.") + def send(self, data: ReadableBuffer, flags: int = 0) -> int: ... + @deprecated("Removed in Python 3.11.") + def sendall(self, data: ReadableBuffer, flags: int = 0) -> None: ... + @deprecated("Removed in Python 3.11.") def set_inheritable(self, inheritable: bool) -> None: ... if sys.platform == "win32": + @deprecated("Removed in Python 3.11.") def share(self, process_id: int) -> bytes: ... else: + @deprecated("Removed in Python 3.11.") def share(self, process_id: int) -> NoReturn: ... - def recv_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> int: ... - def recvfrom_into(self, buffer: _WriteBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ... + @deprecated("Removed in Python 3.11.") + def recv_into(self, buffer: _WriteBuffer, nbytes: int = 0, flags: int = 0) -> int: ... + @deprecated("Removed in Python 3.11.") + def recvfrom_into(self, buffer: _WriteBuffer, nbytes: int = 0, flags: int = 0) -> tuple[int, _RetAddress]: ... + @deprecated("Removed in Python 3.11.") def recvmsg_into( - self, buffers: Iterable[_WriteBuffer], ancbufsize: int = ..., flags: int = ..., / + self, buffers: Iterable[_WriteBuffer], ancbufsize: int = 0, flags: int = 0, / ) -> tuple[int, list[_CMSG], int, Any]: ... - def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ... - def recvfrom(self, bufsize: int, flags: int = ...) -> tuple[bytes, _RetAddress]: ... - def recv(self, bufsize: int, flags: int = ...) -> bytes: ... + @deprecated("Removed in Python 3.11.") + def recvmsg(self, bufsize: int, ancbufsize: int = 0, flags: int = 0, /) -> tuple[bytes, list[_CMSG], int, Any]: ... + @deprecated("Removed in Python 3.11.") + def recvfrom(self, bufsize: int, flags: int = 0) -> tuple[bytes, _RetAddress]: ... + @deprecated("Removed in Python 3.11.") + def recv(self, bufsize: int, flags: int = 0) -> bytes: ... + @deprecated("Removed in Python 3.11.") def __enter__(self) -> socket.socket: ... + @deprecated("Removed in Python 3.11.") def __exit__( self, exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index fb21c5b5fa05f..9071ee9a2fa7e 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -7,16 +7,18 @@ from socket import socket from typing import Literal from typing_extensions import Self, TypeVarTuple, Unpack, deprecated +from . import events from .base_events import Server, _ProtocolFactory, _SSLContext -from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy from .selector_events import BaseSelectorEventLoop _Ts = TypeVarTuple("_Ts") +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.platform != "win32": if sys.version_info >= (3, 14): - __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy", "EventLoop") + __all__ = ("SelectorEventLoop", "EventLoop") elif sys.version_info >= (3, 13): + # Adds EventLoop __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", @@ -28,23 +30,14 @@ if sys.platform != "win32": "DefaultEventLoopPolicy", "EventLoop", ) - elif sys.version_info >= (3, 9): - __all__ = ( - "SelectorEventLoop", - "AbstractChildWatcher", - "SafeChildWatcher", - "FastChildWatcher", - "PidfdChildWatcher", - "MultiLoopChildWatcher", - "ThreadedChildWatcher", - "DefaultEventLoopPolicy", - ) else: + # adds PidfdChildWatcher __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", "SafeChildWatcher", "FastChildWatcher", + "PidfdChildWatcher", "MultiLoopChildWatcher", "ThreadedChildWatcher", "DefaultEventLoopPolicy", @@ -55,7 +48,7 @@ if sys.platform != "win32": # 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") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") class AbstractChildWatcher: @abstractmethod def add_child_handler( @@ -64,7 +57,7 @@ if sys.version_info < (3, 14): @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ... @abstractmethod def close(self) -> None: ... @abstractmethod @@ -85,7 +78,7 @@ if sys.version_info < (3, 14): @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ... @abstractmethod def close(self) -> None: ... @abstractmethod @@ -105,9 +98,9 @@ if sys.platform != "win32": class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def close(self) -> None: ... def is_active(self) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__( @@ -118,7 +111,7 @@ if sys.platform != "win32": ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") class FastChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... def __exit__( @@ -135,7 +128,7 @@ if sys.platform != "win32": class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): def close(self) -> None: ... def is_active(self) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ... class SafeChildWatcher(BaseChildWatcher): def __enter__(self) -> Self: ... @@ -173,12 +166,14 @@ if sys.platform != "win32": cleanup_socket: bool = True, ) -> Server: ... - class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy): - if sys.version_info < (3, 14): + if sys.version_info >= (3, 14): + class _UnixDefaultEventLoopPolicy(events._BaseDefaultEventLoopPolicy): ... + else: + class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy): if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + @deprecated("Deprecated since Python 3.12; 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") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... else: def get_child_watcher(self) -> AbstractChildWatcher: ... @@ -186,14 +181,17 @@ if sys.platform != "win32": SelectorEventLoop = _UnixSelectorEventLoop - DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy + if sys.version_info >= (3, 14): + _DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy + else: + DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy if sys.version_info >= (3, 13): EventLoop = SelectorEventLoop 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") + @deprecated("Deprecated since Python 3.12; removed in Python 3.14.") class MultiLoopChildWatcher(AbstractChildWatcher): def is_active(self) -> bool: ... def close(self) -> None: ... @@ -205,7 +203,7 @@ if sys.platform != "win32": 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: ... + def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ... else: class MultiLoopChildWatcher(AbstractChildWatcher): @@ -219,7 +217,7 @@ if sys.platform != "win32": 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: ... + def attach_loop(self, loop: events.AbstractEventLoop | None) -> None: ... if sys.version_info < (3, 14): class ThreadedChildWatcher(AbstractChildWatcher): @@ -234,18 +232,17 @@ if sys.platform != "win32": 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: ... + def attach_loop(self, loop: events.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: ... + 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: events.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 e5205ba4dcb07..a32381bfb3e63 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -6,8 +6,19 @@ from typing import IO, Any, ClassVar, Final, NoReturn from . import events, futures, proactor_events, selector_events, streams, windows_utils +# Keep asyncio.__all__ updated with any changes to __all__ here if sys.platform == "win32": - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 14): + __all__ = ( + "SelectorEventLoop", + "ProactorEventLoop", + "IocpProactor", + "_DefaultEventLoopPolicy", + "_WindowsSelectorEventLoopPolicy", + "_WindowsProactorEventLoopPolicy", + "EventLoop", + ) + elif sys.version_info >= (3, 13): # 3.13 added `EventLoop`. __all__ = ( "SelectorEventLoop", @@ -84,17 +95,27 @@ if sys.platform == "win32": SelectorEventLoop = _WindowsSelectorEventLoop - class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): - _loop_factory: ClassVar[type[SelectorEventLoop]] - if sys.version_info < (3, 14): + if sys.version_info >= (3, 14): + class _WindowsSelectorEventLoopPolicy(events._BaseDefaultEventLoopPolicy): + _loop_factory: ClassVar[type[SelectorEventLoop]] + + class _WindowsProactorEventLoopPolicy(events._BaseDefaultEventLoopPolicy): + _loop_factory: ClassVar[type[ProactorEventLoop]] + + else: + class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): + _loop_factory: ClassVar[type[SelectorEventLoop]] def get_child_watcher(self) -> NoReturn: ... def set_child_watcher(self, watcher: Any) -> NoReturn: ... - class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): - _loop_factory: ClassVar[type[ProactorEventLoop]] - def get_child_watcher(self) -> NoReturn: ... - def set_child_watcher(self, watcher: Any) -> NoReturn: ... + class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): + _loop_factory: ClassVar[type[ProactorEventLoop]] + def get_child_watcher(self) -> NoReturn: ... + def set_child_watcher(self, watcher: Any) -> NoReturn: ... - DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy + if sys.version_info >= (3, 14): + _DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy + else: + DefaultEventLoopPolicy = WindowsProactorEventLoopPolicy 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 4fa0145323762..5cedd61b5f4a3 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi @@ -9,8 +9,8 @@ if sys.platform == "win32": __all__ = ("pipe", "Popen", "PIPE", "PipeHandle") BUFSIZE: Final = 8192 - PIPE = subprocess.PIPE - STDOUT = subprocess.STDOUT + PIPE: Final = subprocess.PIPE + STDOUT: Final = subprocess.STDOUT def pipe(*, duplex: bool = False, overlapped: tuple[bool, bool] = (True, True), bufsize: int = 8192) -> tuple[int, int]: ... class PipeHandle: @@ -34,9 +34,9 @@ if sys.platform == "win32": def __new__( cls, args: subprocess._CMD, - stdin: subprocess._FILE | None = ..., - stdout: subprocess._FILE | None = ..., - stderr: subprocess._FILE | None = ..., + stdin: subprocess._FILE | None = None, + stdout: subprocess._FILE | None = None, + stderr: subprocess._FILE | None = None, **kwds: Any, ) -> Self: ... def __init__( diff --git a/mypy/typeshed/stdlib/base64.pyi b/mypy/typeshed/stdlib/base64.pyi index 8be4cfe69de00..279d74a94ebe2 100644 --- a/mypy/typeshed/stdlib/base64.pyi +++ b/mypy/typeshed/stdlib/base64.pyi @@ -56,10 +56,6 @@ def encode(input: IO[bytes], output: IO[bytes]) -> None: ... def encodebytes(s: ReadableBuffer) -> bytes: ... def decodebytes(s: ReadableBuffer) -> bytes: ... -if sys.version_info < (3, 9): - def encodestring(s: ReadableBuffer) -> bytes: ... - def decodestring(s: ReadableBuffer) -> bytes: ... - if sys.version_info >= (3, 13): def z85encode(s: ReadableBuffer) -> bytes: ... def z85decode(s: str | ReadableBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index 75bfa91cc3798..b6be2210ffe2e 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -1,14 +1,16 @@ import sys from _typeshed import ExcInfo, TraceFunction, Unused -from collections.abc import Callable, Iterable, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping +from contextlib import contextmanager from types import CodeType, FrameType, TracebackType -from typing import IO, Any, Final, SupportsInt, TypeVar -from typing_extensions import ParamSpec +from typing import IO, Any, Final, Literal, SupportsInt, TypeVar +from typing_extensions import ParamSpec, TypeAlias __all__ = ["BdbQuit", "Bdb", "Breakpoint"] _T = TypeVar("_T") _P = ParamSpec("_P") +_Backend: TypeAlias = Literal["settrace", "monitoring"] # A union of code-object flags at runtime. # The exact values of code-object flags are implementation details, @@ -27,9 +29,18 @@ class Bdb: stopframe: FrameType | None returnframe: FrameType | None stoplineno: int - def __init__(self, skip: Iterable[str] | None = None) -> None: ... + if sys.version_info >= (3, 14): + backend: _Backend + def __init__(self, skip: Iterable[str] | None = None, backend: _Backend = "settrace") -> None: ... + else: + def __init__(self, skip: Iterable[str] | None = None) -> None: ... + def canonic(self, filename: str) -> str: ... def reset(self) -> None: ... + if sys.version_info >= (3, 12): + @contextmanager + def set_enterframe(self, frame: FrameType) -> Iterator[None]: ... + def trace_dispatch(self, frame: FrameType, event: str, arg: Any) -> TraceFunction: ... def dispatch_line(self, frame: FrameType) -> TraceFunction: ... def dispatch_call(self, frame: FrameType, arg: None) -> TraceFunction: ... @@ -70,16 +81,21 @@ class Bdb: def get_bpbynumber(self, arg: SupportsInt) -> Breakpoint: ... def get_break(self, filename: str, lineno: int) -> bool: ... def get_breaks(self, filename: str, lineno: int) -> list[Breakpoint]: ... - def get_file_breaks(self, filename: str) -> list[Breakpoint]: ... - def get_all_breaks(self) -> list[Breakpoint]: ... + def get_file_breaks(self, filename: str) -> list[int]: ... + def get_all_breaks(self) -> dict[str, list[int]]: ... def get_stack(self, f: FrameType | None, t: TracebackType | None) -> tuple[list[tuple[FrameType, int]], int]: ... - def format_stack_entry(self, frame_lineno: int, lprefix: str = ": ") -> str: ... + def format_stack_entry(self, frame_lineno: tuple[FrameType, int], lprefix: str = ": ") -> str: ... def run( self, cmd: str | CodeType, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None ) -> None: ... def runeval(self, expr: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> None: ... def runctx(self, cmd: str | CodeType, globals: dict[str, Any] | None, locals: Mapping[str, Any] | None) -> None: ... def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> _T | None: ... + if sys.version_info >= (3, 14): + def start_trace(self) -> None: ... + def stop_trace(self) -> None: ... + def disable_current_event(self) -> None: ... + def restart_events(self) -> None: ... class Breakpoint: next: int diff --git a/mypy/typeshed/stdlib/binascii.pyi b/mypy/typeshed/stdlib/binascii.pyi index 32e018c653cbc..5606d5cdf74d9 100644 --- a/mypy/typeshed/stdlib/binascii.pyi +++ b/mypy/typeshed/stdlib/binascii.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import ReadableBuffer -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated # Many functions in binascii accept buffer objects # or ASCII-only strings. @@ -20,15 +20,19 @@ def a2b_qp(data: _AsciiBuffer, header: bool = False) -> bytes: ... def b2a_qp(data: ReadableBuffer, quotetabs: bool = False, istext: bool = True, header: bool = False) -> bytes: ... if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.9; removed in Python 3.11.") def a2b_hqx(data: _AsciiBuffer, /) -> bytes: ... + @deprecated("Deprecated since Python 3.9; removed in Python 3.11.") def rledecode_hqx(data: ReadableBuffer, /) -> bytes: ... + @deprecated("Deprecated since Python 3.9; removed in Python 3.11.") def rlecode_hqx(data: ReadableBuffer, /) -> bytes: ... + @deprecated("Deprecated since Python 3.9; removed in Python 3.11.") def b2a_hqx(data: ReadableBuffer, /) -> bytes: ... def crc_hqx(data: ReadableBuffer, crc: int, /) -> int: ... def crc32(data: ReadableBuffer, crc: int = 0, /) -> int: ... -def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... -def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... +def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = 1) -> bytes: ... +def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = 1) -> bytes: ... def a2b_hex(hexstr: _AsciiBuffer, /) -> bytes: ... def unhexlify(hexstr: _AsciiBuffer, /) -> bytes: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 0999fb1d6c36b..30dfb9dbb25c9 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1,11 +1,11 @@ -# ruff: noqa: PYI036 # This is the module declaring BaseException import _ast +import _sitebuiltins import _typeshed import sys import types from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import ( - AnyStr_co, + AnnotationForm, ConvertibleToFloat, ConvertibleToInt, FileDescriptorOrPath, @@ -32,20 +32,21 @@ from _typeshed import ( ) from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper -from types import CellType, CodeType, TracebackType +from os import PathLike +from types import CellType, CodeType, GenericAlias, TracebackType # mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} # are imported from collections.abc in builtins.pyi -from typing import ( # noqa: Y022 +from typing import ( # noqa: Y022,UP035 IO, Any, BinaryIO, ClassVar, + Final, Generic, Mapping, MutableMapping, MutableSequence, - NoReturn, Protocol, Sequence, SupportsAbs, @@ -70,10 +71,11 @@ from typing_extensions import ( # noqa: Y023 TypeIs, TypeVarTuple, deprecated, + disjoint_base, ) -if sys.version_info >= (3, 9): - from types import GenericAlias +if sys.version_info >= (3, 14): + from _typeshed import AnnotateFunc _T = TypeVar("_T") _I = TypeVar("_I", default=int) @@ -88,12 +90,21 @@ _T2 = TypeVar("_T2") _T3 = TypeVar("_T3") _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") -_SupportsNextT = TypeVar("_SupportsNextT", bound=SupportsNext[Any], covariant=True) -_SupportsAnextT = TypeVar("_SupportsAnextT", bound=SupportsAnext[Any], covariant=True) +_SupportsNextT_co = TypeVar("_SupportsNextT_co", bound=SupportsNext[Any], covariant=True) +_SupportsAnextT_co = TypeVar("_SupportsAnextT_co", bound=SupportsAnext[Any], covariant=True) _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) _AwaitableT_co = TypeVar("_AwaitableT_co", bound=Awaitable[Any], covariant=True) _P = ParamSpec("_P") +# Type variables for slice +_StartT_co = TypeVar("_StartT_co", covariant=True, default=Any) # slice -> slice[Any, Any, Any] +_StopT_co = TypeVar("_StopT_co", covariant=True, default=_StartT_co) # slice[A] -> slice[A, A, A] +# NOTE: step could differ from start and stop, (e.g. datetime/timedelta)l +# the default (start|stop) is chosen to cater to the most common case of int/index slices. +# FIXME: https://github.com/python/typing/issues/213 (replace step=start|stop with step=start&stop) +_StepT_co = TypeVar("_StepT_co", covariant=True, default=_StartT_co | _StopT_co) # slice[A,B] -> slice[A, B, A|B] + +@disjoint_base class object: __doc__: str | None __dict__: dict[str, Any] @@ -102,7 +113,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. @@ -129,6 +140,7 @@ class object: @classmethod def __subclasshook__(cls, subclass: type, /) -> bool: ... +@disjoint_base class staticmethod(Generic[_P, _R_co]): @property def __func__(self) -> Callable[_P, _R_co]: ... @@ -145,7 +157,11 @@ class staticmethod(Generic[_P, _R_co]): @property def __wrapped__(self) -> Callable[_P, _R_co]: ... def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ... + if sys.version_info >= (3, 14): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + __annotate__: AnnotateFunc | None +@disjoint_base class classmethod(Generic[_T, _P, _R_co]): @property def __func__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ... @@ -161,7 +177,11 @@ class classmethod(Generic[_T, _P, _R_co]): __qualname__: str @property def __wrapped__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ... + if sys.version_info >= (3, 14): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + __annotate__: AnnotateFunc | None +@disjoint_base class type: # object.__base__ is None. Otherwise, it would be a type. @property @@ -169,8 +189,9 @@ class type: __bases__: tuple[type, ...] @property def __basicsize__(self) -> int: ... - @property - def __dict__(self) -> types.MappingProxyType[str, Any]: ... # type: ignore[override] + # type.__dict__ is read-only at runtime, but that can't be expressed currently. + # See https://github.com/python/typeshed/issues/11033 for a discussion. + __dict__: Final[types.MappingProxyType[str, Any]] # type: ignore[assignment] @property def __dictoffset__(self) -> int: ... @property @@ -206,11 +227,17 @@ class type: @classmethod def __prepare__(metacls, name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]: ... if sys.version_info >= (3, 10): - def __or__(self, value: Any, /) -> types.UnionType: ... - def __ror__(self, value: Any, /) -> types.UnionType: ... + # `int | str` produces an instance of `UnionType`, but `int | int` produces an instance of `type`, + # and `abc.ABC | abc.ABC` produces an instance of `abc.ABCMeta`. + def __or__(self: _typeshed.Self, value: Any, /) -> types.UnionType | _typeshed.Self: ... + def __ror__(self: _typeshed.Self, value: Any, /) -> types.UnionType | _typeshed.Self: ... if sys.version_info >= (3, 12): __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] + __annotations__: dict[str, AnnotationForm] + if sys.version_info >= (3, 14): + __annotate__: AnnotateFunc | None +@disjoint_base class super: @overload def __init__(self, t: Any, obj: Any, /) -> None: ... @@ -223,9 +250,10 @@ _PositiveInteger: TypeAlias = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, _NegativeInteger: TypeAlias = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20] _LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0] # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed +@disjoint_base class int: @overload - def __new__(cls, x: ConvertibleToInt = ..., /) -> Self: ... + def __new__(cls, x: ConvertibleToInt = 0, /) -> Self: ... @overload def __new__(cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self: ... def as_integer_ratio(self) -> tuple[int, Literal[1]]: ... @@ -313,7 +341,11 @@ class int: def __trunc__(self) -> int: ... def __ceil__(self) -> int: ... def __floor__(self) -> int: ... - def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ... + if sys.version_info >= (3, 14): + def __round__(self, ndigits: SupportsIndex | None = None, /) -> int: ... + else: + def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ... + def __getnewargs__(self) -> tuple[int]: ... def __eq__(self, value: object, /) -> bool: ... def __ne__(self, value: object, /) -> bool: ... @@ -327,9 +359,11 @@ class int: def __hash__(self) -> int: ... def __bool__(self) -> bool: ... def __index__(self) -> int: ... + def __format__(self, format_spec: str, /) -> str: ... +@disjoint_base class float: - def __new__(cls, x: ConvertibleToFloat = ..., /) -> Self: ... + def __new__(cls, x: ConvertibleToFloat = 0, /) -> Self: ... def as_integer_ratio(self) -> tuple[int, int]: ... def hex(self) -> str: ... def is_integer(self) -> bool: ... @@ -369,10 +403,8 @@ class float: def __rpow__(self, value: float, mod: None = None, /) -> Any: ... def __getnewargs__(self) -> tuple[float]: ... def __trunc__(self) -> int: ... - if sys.version_info >= (3, 9): - def __ceil__(self) -> int: ... - def __floor__(self) -> int: ... - + def __ceil__(self) -> int: ... + def __floor__(self) -> int: ... @overload def __round__(self, ndigits: None = None, /) -> int: ... @overload @@ -390,14 +422,19 @@ class float: def __abs__(self) -> float: ... def __hash__(self) -> int: ... def __bool__(self) -> bool: ... + def __format__(self, format_spec: str, /) -> str: ... + if sys.version_info >= (3, 14): + @classmethod + def from_number(cls, number: float | SupportsIndex | SupportsFloat, /) -> Self: ... +@disjoint_base class complex: # Python doesn't currently accept SupportsComplex for the second argument @overload def __new__( cls, - real: complex | SupportsComplex | SupportsFloat | SupportsIndex = ..., - imag: complex | SupportsFloat | SupportsIndex = ..., + real: complex | SupportsComplex | SupportsFloat | SupportsIndex = 0, + imag: complex | SupportsFloat | SupportsIndex = 0, ) -> Self: ... @overload def __new__(cls, real: str | SupportsComplex | SupportsFloat | SupportsIndex | complex) -> Self: ... @@ -423,33 +460,40 @@ class complex: def __abs__(self) -> float: ... def __hash__(self) -> int: ... def __bool__(self) -> bool: ... + def __format__(self, format_spec: str, /) -> str: ... if sys.version_info >= (3, 11): def __complex__(self) -> complex: ... + if sys.version_info >= (3, 14): + @classmethod + def from_number(cls, number: complex | SupportsComplex | SupportsFloat | SupportsIndex, /) -> Self: ... +@type_check_only class _FormatMapMapping(Protocol): def __getitem__(self, key: str, /) -> Any: ... +@type_check_only class _TranslateTable(Protocol): def __getitem__(self, key: int, /) -> str | int | None: ... +@disjoint_base class str(Sequence[str]): @overload - def __new__(cls, object: object = ...) -> Self: ... + def __new__(cls, object: object = "") -> Self: ... @overload - def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... + def __new__(cls, object: ReadableBuffer, encoding: str = "utf-8", errors: str = "strict") -> Self: ... def capitalize(self) -> str: ... # type: ignore[misc] def casefold(self) -> str: ... # type: ignore[misc] def center(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] - def count(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def count(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... def encode(self, encoding: str = "utf-8", errors: str = "strict") -> bytes: ... def endswith( - self, suffix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, suffix: str | tuple[str, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> bool: ... def expandtabs(self, tabsize: SupportsIndex = 8) -> str: ... # type: ignore[misc] - def find(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def find(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... 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: ... + def index(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... def isascii(self) -> bool: ... @@ -471,12 +515,11 @@ class str(Sequence[str]): def replace(self, old: str, new: str, /, count: SupportsIndex = -1) -> str: ... # type: ignore[misc] else: def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc] - if sys.version_info >= (3, 9): - def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] - def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] - def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... - def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... + def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc] + def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc] + def rfind(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... + def rindex(self, sub: str, start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> int: ... def rjust(self, width: SupportsIndex, fillchar: str = " ", /) -> str: ... # type: ignore[misc] def rpartition(self, sep: str, /) -> tuple[str, str, str]: ... # type: ignore[misc] def rsplit(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] @@ -484,7 +527,7 @@ class str(Sequence[str]): def split(self, sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]: ... # type: ignore[misc] def splitlines(self, keepends: bool = False) -> list[str]: ... # type: ignore[misc] def startswith( - self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, prefix: str | tuple[str, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> bool: ... def strip(self, chars: str | None = None, /) -> str: ... # type: ignore[misc] def swapcase(self) -> str: ... # type: ignore[misc] @@ -518,34 +561,36 @@ class str(Sequence[str]): def __ne__(self, value: object, /) -> bool: ... def __rmul__(self, value: SupportsIndex, /) -> str: ... # type: ignore[misc] def __getnewargs__(self) -> tuple[str]: ... + def __format__(self, format_spec: str, /) -> str: ... +@disjoint_base class bytes(Sequence[int]): @overload def __new__(cls, o: Iterable[SupportsIndex] | SupportsIndex | SupportsBytes | ReadableBuffer, /) -> Self: ... @overload - def __new__(cls, string: str, /, encoding: str, errors: str = ...) -> Self: ... + def __new__(cls, string: str, /, encoding: str, errors: str = "strict") -> Self: ... @overload def __new__(cls) -> Self: ... def capitalize(self) -> bytes: ... def center(self, width: SupportsIndex, fillchar: bytes = b" ", /) -> bytes: ... def count( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: ... def endswith( self, suffix: ReadableBuffer | tuple[ReadableBuffer, ...], - start: SupportsIndex | None = ..., - end: SupportsIndex | None = ..., + start: SupportsIndex | None = None, + end: SupportsIndex | None = None, /, ) -> bool: ... def expandtabs(self, tabsize: SupportsIndex = 8) -> bytes: ... def find( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... - def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... + def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = 1) -> str: ... def index( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... @@ -561,15 +606,13 @@ class bytes(Sequence[int]): def lstrip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ... def partition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ... def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ... - if sys.version_info >= (3, 9): - def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ... - def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ... - + def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ... + def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ... def rfind( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def rindex( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def rjust(self, width: SupportsIndex, fillchar: bytes | bytearray = b" ", /) -> bytes: ... def rpartition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ... @@ -580,14 +623,14 @@ class bytes(Sequence[int]): def startswith( self, prefix: ReadableBuffer | tuple[ReadableBuffer, ...], - start: SupportsIndex | None = ..., - end: SupportsIndex | None = ..., + start: SupportsIndex | None = None, + end: SupportsIndex | None = None, /, ) -> bool: ... def strip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ... def swapcase(self) -> bytes: ... def title(self) -> bytes: ... - def translate(self, table: ReadableBuffer | None, /, delete: bytes = b"") -> bytes: ... + def translate(self, table: ReadableBuffer | None, /, delete: ReadableBuffer = b"") -> bytes: ... def upper(self) -> bytes: ... def zfill(self, width: SupportsIndex, /) -> bytes: ... @classmethod @@ -619,36 +662,37 @@ class bytes(Sequence[int]): def __buffer__(self, flags: int, /) -> memoryview: ... +@disjoint_base class bytearray(MutableSequence[int]): @overload def __init__(self) -> None: ... @overload def __init__(self, ints: Iterable[SupportsIndex] | SupportsIndex | ReadableBuffer, /) -> None: ... @overload - def __init__(self, string: str, /, encoding: str, errors: str = ...) -> None: ... + def __init__(self, string: str, /, encoding: str, errors: str = "strict") -> None: ... def append(self, item: SupportsIndex, /) -> None: ... def capitalize(self) -> bytearray: ... def center(self, width: SupportsIndex, fillchar: bytes = b" ", /) -> bytearray: ... def count( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def copy(self) -> bytearray: ... def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: ... def endswith( self, suffix: ReadableBuffer | tuple[ReadableBuffer, ...], - start: SupportsIndex | None = ..., - end: SupportsIndex | None = ..., + start: SupportsIndex | None = None, + end: SupportsIndex | None = None, /, ) -> bool: ... def expandtabs(self, tabsize: SupportsIndex = 8) -> bytearray: ... def extend(self, iterable_of_ints: Iterable[SupportsIndex], /) -> None: ... def find( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... - def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... + def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = 1) -> str: ... def index( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def insert(self, index: SupportsIndex, item: SupportsIndex, /) -> None: ... def isalnum(self) -> bool: ... @@ -666,16 +710,14 @@ class bytearray(MutableSequence[int]): def partition(self, sep: ReadableBuffer, /) -> tuple[bytearray, bytearray, bytearray]: ... def pop(self, index: int = -1, /) -> int: ... def remove(self, value: int, /) -> None: ... - if sys.version_info >= (3, 9): - def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ... - def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ... - + def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ... + def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ... def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ... def rfind( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def rindex( - self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., / + self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... def rjust(self, width: SupportsIndex, fillchar: bytes | bytearray = b" ", /) -> bytearray: ... def rpartition(self, sep: ReadableBuffer, /) -> tuple[bytearray, bytearray, bytearray]: ... @@ -686,8 +728,8 @@ class bytearray(MutableSequence[int]): def startswith( self, prefix: ReadableBuffer | tuple[ReadableBuffer, ...], - start: SupportsIndex | None = ..., - end: SupportsIndex | None = ..., + start: SupportsIndex | None = None, + end: SupportsIndex | None = None, /, ) -> bool: ... def strip(self, bytes: ReadableBuffer | None = None, /) -> bytearray: ... @@ -730,6 +772,8 @@ class bytearray(MutableSequence[int]): def __alloc__(self) -> int: ... def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... + if sys.version_info >= (3, 14): + def resize(self, size: int, /) -> 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" @@ -764,7 +808,11 @@ class memoryview(Sequence[_I]): def __new__(cls, obj: ReadableBuffer) -> Self: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / + self, + exc_type: type[BaseException] | None, # noqa: PYI036 # This is the module declaring BaseException + exc_val: BaseException | None, + exc_tb: TracebackType | None, + /, ) -> None: ... @overload def cast(self, format: Literal["c", "@c"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[bytes]: ... @@ -786,7 +834,7 @@ class memoryview(Sequence[_I]): @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: @@ -795,13 +843,20 @@ class memoryview(Sequence[_I]): def tolist(self) -> list[int]: ... def toreadonly(self) -> memoryview: ... def release(self) -> None: ... - def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... + def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = 1) -> str: ... 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] + if sys.version_info >= (3, 14): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + @final class bool(int): - def __new__(cls, o: object = ..., /) -> Self: ... + def __new__(cls, o: object = False, /) -> Self: ... # The following overloads could be represented more elegantly with a TypeVar("_B", bool, int), # however mypy has a bug regarding TypeVar constraints (https://github.com/python/mypy/issues/11880). @overload @@ -829,27 +884,52 @@ class bool(int): @overload def __rxor__(self, value: int, /) -> int: ... def __getnewargs__(self) -> tuple[int]: ... - @deprecated("Will throw an error in Python 3.14. Use `not` for logical negation of bools instead.") + @deprecated("Will throw an error in Python 3.16. Use `not` for logical negation of bools instead.") def __invert__(self) -> int: ... @final -class slice: +class slice(Generic[_StartT_co, _StopT_co, _StepT_co]): @property - def start(self) -> Any: ... + def start(self) -> _StartT_co: ... @property - def step(self) -> Any: ... + def step(self) -> _StepT_co: ... @property - def stop(self) -> Any: ... + def stop(self) -> _StopT_co: ... + # Note: __new__ overloads map `None` to `Any`, since users expect slice(x, None) + # to be compatible with slice(None, x). + # generic slice -------------------------------------------------------------------- + @overload + def __new__(cls, start: None, stop: None = None, step: None = None, /) -> slice[Any, Any, Any]: ... + # unary overloads ------------------------------------------------------------------ + @overload + def __new__(cls, stop: _T2, /) -> slice[Any, _T2, Any]: ... + # binary overloads ----------------------------------------------------------------- + @overload + def __new__(cls, start: _T1, stop: None, step: None = None, /) -> slice[_T1, Any, Any]: ... + @overload + def __new__(cls, start: None, stop: _T2, step: None = None, /) -> slice[Any, _T2, Any]: ... + @overload + def __new__(cls, start: _T1, stop: _T2, step: None = None, /) -> slice[_T1, _T2, Any]: ... + # ternary overloads ---------------------------------------------------------------- @overload - def __new__(cls, stop: Any, /) -> Self: ... + def __new__(cls, start: None, stop: None, step: _T3, /) -> slice[Any, Any, _T3]: ... @overload - def __new__(cls, start: Any, stop: Any, step: Any = ..., /) -> Self: ... + def __new__(cls, start: _T1, stop: None, step: _T3, /) -> slice[_T1, Any, _T3]: ... + @overload + def __new__(cls, start: None, stop: _T2, step: _T3, /) -> slice[Any, _T2, _T3]: ... + @overload + 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]: ... +@disjoint_base class tuple(Sequence[_T_co]): - def __new__(cls, iterable: Iterable[_T_co] = ..., /) -> Self: ... + def __new__(cls, iterable: Iterable[_T_co] = (), /) -> Self: ... def __len__(self) -> int: ... def __contains__(self, key: object, /) -> bool: ... @overload @@ -871,12 +951,12 @@ class tuple(Sequence[_T_co]): def __rmul__(self, value: SupportsIndex, /) -> tuple[_T_co, ...]: ... def count(self, value: Any, /) -> int: ... def index(self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # 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 +# Obsolete, use types.FunctionType instead. @final @type_check_only class function: @@ -890,8 +970,10 @@ class function: def __globals__(self) -> dict[str, Any]: ... __name__: str __qualname__: str - __annotations__: dict[str, Any] - __kwdefaults__: dict[str, Any] + __annotations__: dict[str, AnnotationForm] + if sys.version_info >= (3, 14): + __annotate__: AnnotateFunc | None + __kwdefaults__: dict[str, Any] | None if sys.version_info >= (3, 10): @property def __builtins__(self) -> dict[str, Any]: ... @@ -899,9 +981,30 @@ class function: __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] __module__: str + if sys.version_info >= (3, 13): + def __new__( + cls, + code: CodeType, + globals: dict[str, Any], + name: str | None = None, + argdefs: tuple[object, ...] | None = None, + closure: tuple[CellType, ...] | None = None, + kwdefaults: dict[str, object] | None = None, + ) -> Self: ... + else: + def __new__( + cls, + code: CodeType, + globals: dict[str, Any], + name: str | None = None, + argdefs: tuple[object, ...] | None = None, + closure: tuple[CellType, ...] | None = None, + ) -> Self: ... + # mypy uses `builtins.function.__get__` to represent methods, properties, and getset_descriptors so we type the return as Any. def __get__(self, instance: object, owner: type | None = None, /) -> Any: ... +@disjoint_base class list(MutableSequence[_T]): @overload def __init__(self) -> None: ... @@ -954,9 +1057,9 @@ class list(MutableSequence[_T]): def __lt__(self, value: list[_T], /) -> bool: ... def __le__(self, value: list[_T], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... +@disjoint_base class dict(MutableMapping[_KT, _VT]): # __init__ should be kept roughly in line with `collections.UserDict.__init__`, which has similar semantics # Also multiprocessing.managers.SyncManager.dict() @@ -1005,7 +1108,7 @@ class dict(MutableMapping[_KT, _VT]): def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> dict[_T, _S]: ... # Positional-only in dict, but not in MutableMapping @overload # type: ignore[override] - def get(self, key: _KT, /) -> _VT | None: ... + def get(self, key: _KT, default: None = None, /) -> _VT | None: ... @overload def get(self, key: _KT, default: _VT, /) -> _VT: ... @overload @@ -1024,22 +1127,22 @@ class dict(MutableMapping[_KT, _VT]): def __eq__(self, value: object, /) -> bool: ... def __reversed__(self) -> Iterator[_KT]: ... __hash__: ClassVar[None] # type: ignore[assignment] - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - @overload - def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ... - @overload - def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... - @overload - def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ... - @overload - def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... - # dict.__ior__ should be kept roughly in line with MutableMapping.update() - @overload # type: ignore[misc] - def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ... - @overload - def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + @overload + def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ... + @overload + def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ... + @overload + def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + # dict.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ... + @overload + def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ... +@disjoint_base class set(MutableSet[_T]): @overload def __init__(self) -> None: ... @@ -1077,9 +1180,9 @@ class set(MutableSet[_T]): def __gt__(self, value: AbstractSet[object], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... +@disjoint_base class frozenset(AbstractSet[_T_co]): @overload def __new__(cls) -> Self: ... @@ -1106,15 +1209,14 @@ class frozenset(AbstractSet[_T_co]): def __gt__(self, value: AbstractSet[object], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... +@disjoint_base 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): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @final class range(Sequence[int]): @@ -1127,7 +1229,7 @@ class range(Sequence[int]): @overload def __new__(cls, stop: SupportsIndex, /) -> Self: ... @overload - def __new__(cls, start: SupportsIndex, stop: SupportsIndex, step: SupportsIndex = ..., /) -> Self: ... + def __new__(cls, start: SupportsIndex, stop: SupportsIndex, step: SupportsIndex = 1, /) -> Self: ... def count(self, value: int, /) -> int: ... def index(self, value: int, /) -> int: ... # type: ignore[override] def __len__(self) -> int: ... @@ -1141,6 +1243,7 @@ class range(Sequence[int]): def __getitem__(self, key: slice, /) -> range: ... def __reversed__(self) -> Iterator[int]: ... +@disjoint_base class property: fget: Callable[[Any], Any] | None fset: Callable[[Any, Any], None] | None @@ -1151,26 +1254,21 @@ class property: def __init__( self, - fget: Callable[[Any], Any] | None = ..., - fset: Callable[[Any, Any], None] | None = ..., - fdel: Callable[[Any], None] | None = ..., - doc: str | None = ..., + fget: Callable[[Any], Any] | None = None, + fset: Callable[[Any, Any], None] | None = None, + fdel: Callable[[Any], None] | None = None, + doc: str | None = None, ) -> None: ... def getter(self, fget: Callable[[Any], Any], /) -> property: ... def setter(self, fset: Callable[[Any, Any], None], /) -> property: ... def deleter(self, fdel: Callable[[Any], None], /) -> property: ... + @overload + def __get__(self, instance: None, owner: type, /) -> Self: ... + @overload def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ... def __set__(self, instance: Any, value: Any, /) -> None: ... def __delete__(self, instance: Any, /) -> None: ... -@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] - -NotImplemented: _NotImplementedType - def abs(x: SupportsAbs[_T], /) -> _T: ... def all(iterable: Iterable[object], /) -> bool: ... def any(iterable: Iterable[object], /) -> bool: ... @@ -1178,16 +1276,11 @@ def ascii(obj: object, /) -> str: ... def bin(number: int | SupportsIndex, /) -> str: ... def breakpoint(*args: Any, **kws: Any) -> None: ... def callable(obj: object, /) -> TypeIs[Callable[..., object]]: ... -def chr(i: int, /) -> str: ... - -# We define this here instead of using os.PathLike to avoid import cycle issues. -# See https://github.com/python/typeshed/pull/991#issuecomment-288160993 -class _PathLike(Protocol[AnyStr_co]): - def __fspath__(self) -> AnyStr_co: ... +def chr(i: int | SupportsIndex, /) -> str: ... if sys.version_info >= (3, 10): - def aiter(async_iterable: SupportsAiter[_SupportsAnextT], /) -> _SupportsAnextT: ... - + def aiter(async_iterable: SupportsAiter[_SupportsAnextT_co], /) -> _SupportsAnextT_co: ... + @type_check_only class _SupportsSynchronousAnext(Protocol[_AwaitableT_co]): def __anext__(self) -> _AwaitableT_co: ... @@ -1205,7 +1298,7 @@ if sys.version_info >= (3, 10): @overload def compile( source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], + filename: str | bytes | PathLike[Any], mode: str, flags: Literal[0], dont_inherit: bool = False, @@ -1216,7 +1309,7 @@ def compile( @overload def compile( source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], + filename: str | bytes | PathLike[Any], mode: str, *, dont_inherit: bool = False, @@ -1226,7 +1319,7 @@ def compile( @overload def compile( source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], + filename: str | bytes | PathLike[Any], mode: str, flags: Literal[1024], dont_inherit: bool = False, @@ -1237,7 +1330,7 @@ def compile( @overload def compile( source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | ReadableBuffer | _PathLike[Any], + filename: str | bytes | PathLike[Any], mode: str, flags: int, dont_inherit: bool = False, @@ -1245,8 +1338,10 @@ def compile( *, _feature_version: int = -1, ) -> Any: ... -def copyright() -> None: ... -def credits() -> None: ... + +copyright: _sitebuiltins._Printer +credits: _sitebuiltins._Printer + def delattr(obj: object, name: str, /) -> None: ... def dir(o: object = ..., /) -> list[str]: ... @overload @@ -1301,8 +1396,9 @@ else: /, ) -> None: ... -def exit(code: sys._ExitCode = None) -> NoReturn: ... +exit: _sitebuiltins.Quitter +@disjoint_base class filter(Iterator[_T]): @overload def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... @@ -1335,16 +1431,18 @@ def getattr(o: object, name: str, default: _T, /) -> Any | _T: ... def globals() -> dict[str, Any]: ... def hasattr(obj: object, name: str, /) -> bool: ... def hash(obj: object, /) -> int: ... -def help(request: object = ...) -> None: ... + +help: _sitebuiltins._Helper + def hex(number: int | SupportsIndex, /) -> str: ... def id(obj: object, /) -> int: ... def input(prompt: object = "", /) -> str: ... - +@type_check_only class _GetItemIterable(Protocol[_T_co]): def __getitem__(self, i: int, /) -> _T_co: ... @overload -def iter(object: SupportsIter[_SupportsNextT], /) -> _SupportsNextT: ... +def iter(object: SupportsIter[_SupportsNextT_co], /) -> _SupportsNextT_co: ... @overload def iter(object: _GetItemIterable[_T], /) -> Iterator[_T]: ... @overload @@ -1352,7 +1450,6 @@ def iter(object: Callable[[], _T | None], sentinel: None, /) -> Iterator[_T]: .. @overload def iter(object: Callable[[], _T], sentinel: object, /) -> Iterator[_T]: ... -# Keep this alias in sync with unittest.case._ClassInfo if sys.version_info >= (3, 10): _ClassInfo: TypeAlias = type | types.UnionType | tuple[_ClassInfo, ...] else: @@ -1361,52 +1458,114 @@ else: def isinstance(obj: object, class_or_tuple: _ClassInfo, /) -> bool: ... def issubclass(cls: type, class_or_tuple: _ClassInfo, /) -> bool: ... def len(obj: Sized, /) -> int: ... -def license() -> None: ... -def locals() -> dict[str, Any]: ... +license: _sitebuiltins._Printer + +def locals() -> dict[str, Any]: ... +@disjoint_base class map(Iterator[_S]): - @overload - def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... - @overload - def __new__(cls, func: Callable[[_T1, _T2], _S], iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ... - @overload - def __new__( - cls, func: Callable[[_T1, _T2, _T3], _S], iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / - ) -> Self: ... - @overload - def __new__( - cls, - func: Callable[[_T1, _T2, _T3, _T4], _S], - iter1: Iterable[_T1], - iter2: Iterable[_T2], - iter3: Iterable[_T3], - iter4: Iterable[_T4], - /, - ) -> Self: ... - @overload - def __new__( - cls, - func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], - iter1: Iterable[_T1], - iter2: Iterable[_T2], - iter3: Iterable[_T3], - iter4: Iterable[_T4], - iter5: Iterable[_T5], - /, - ) -> Self: ... - @overload - def __new__( - cls, - func: Callable[..., _S], - iter1: Iterable[Any], - iter2: Iterable[Any], - iter3: Iterable[Any], - iter4: Iterable[Any], - iter5: Iterable[Any], - iter6: Iterable[Any], - /, - *iterables: Iterable[Any], - ) -> Self: ... + # 3.14 adds `strict` argument. + if sys.version_info >= (3, 14): + @overload + def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /, *, strict: bool = False) -> Self: ... + @overload + def __new__( + cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /, *, strict: bool = False + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[[_T1, _T2, _T3], _S], + iterable: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + /, + *, + strict: bool = False, + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[[_T1, _T2, _T3, _T4], _S], + iterable: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + /, + *, + strict: bool = False, + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], + iterable: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + /, + *, + strict: bool = False, + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[..., _S], + iterable: Iterable[Any], + iter2: Iterable[Any], + iter3: Iterable[Any], + iter4: Iterable[Any], + iter5: Iterable[Any], + iter6: Iterable[Any], + /, + *iterables: Iterable[Any], + strict: bool = False, + ) -> Self: ... + else: + @overload + def __new__(cls, func: Callable[[_T1], _S], iterable: Iterable[_T1], /) -> Self: ... + @overload + def __new__(cls, func: Callable[[_T1, _T2], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], /) -> Self: ... + @overload + def __new__( + cls, func: Callable[[_T1, _T2, _T3], _S], iterable: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[[_T1, _T2, _T3, _T4], _S], + iterable: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + /, + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[[_T1, _T2, _T3, _T4, _T5], _S], + iterable: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + iter5: Iterable[_T5], + /, + ) -> Self: ... + @overload + def __new__( + cls, + func: Callable[..., _S], + iterable: Iterable[Any], + iter2: Iterable[Any], + iter3: Iterable[Any], + iter4: Iterable[Any], + iter5: Iterable[Any], + iter6: Iterable[Any], + /, + *iterables: Iterable[Any], + ) -> Self: ... + def __iter__(self) -> Self: ... def __next__(self) -> _S: ... @@ -1533,7 +1692,7 @@ def open( opener: _Opener | None = None, ) -> IO[Any]: ... def ord(c: str | bytes | bytearray, /) -> int: ... - +@type_check_only class _SupportsWriteAndFlush(SupportsWrite[_T_contra], SupportsFlush, Protocol[_T_contra]): ... @overload @@ -1549,17 +1708,20 @@ def print( *values: object, sep: str | None = " ", end: str | None = "\n", file: _SupportsWriteAndFlush[str] | None = None, flush: bool ) -> None: ... -_E = TypeVar("_E", contravariant=True) -_M = TypeVar("_M", contravariant=True) +_E_contra = TypeVar("_E_contra", contravariant=True) +_M_contra = TypeVar("_M_contra", contravariant=True) -class _SupportsPow2(Protocol[_E, _T_co]): - def __pow__(self, other: _E, /) -> _T_co: ... +@type_check_only +class _SupportsPow2(Protocol[_E_contra, _T_co]): + def __pow__(self, other: _E_contra, /) -> _T_co: ... -class _SupportsPow3NoneOnly(Protocol[_E, _T_co]): - def __pow__(self, other: _E, modulo: None = None, /) -> _T_co: ... +@type_check_only +class _SupportsPow3NoneOnly(Protocol[_E_contra, _T_co]): + def __pow__(self, other: _E_contra, modulo: None = None, /) -> _T_co: ... -class _SupportsPow3(Protocol[_E, _M, _T_co]): - def __pow__(self, other: _E, modulo: _M, /) -> _T_co: ... +@type_check_only +class _SupportsPow3(Protocol[_E_contra, _M_contra, _T_co]): + def __pow__(self, other: _E_contra, modulo: _M_contra, /) -> _T_co: ... _SupportsSomeKindOfPow = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed _SupportsPow2[Any, Any] | _SupportsPow3NoneOnly[Any, Any] | _SupportsPow3[Any, Any, Any] @@ -1595,17 +1757,19 @@ def pow(base: float, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> @overload def pow(base: complex, exp: complex | _SupportsSomeKindOfPow, mod: None = None) -> complex: ... @overload -def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] +def pow(base: _SupportsPow2[_E_contra, _T_co], exp: _E_contra, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] @overload -def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] +def pow(base: _SupportsPow3NoneOnly[_E_contra, _T_co], exp: _E_contra, mod: None = None) -> _T_co: ... # type: ignore[overload-overlap] @overload -def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M) -> _T_co: ... +def pow(base: _SupportsPow3[_E_contra, _M_contra, _T_co], exp: _E_contra, mod: _M_contra) -> _T_co: ... @overload def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = None) -> Any: ... @overload def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex: ... -def quit(code: sys._ExitCode = None) -> NoReturn: ... +quit: _sitebuiltins.Quitter + +@disjoint_base class reversed(Iterator[_T]): @overload def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] @@ -1621,9 +1785,11 @@ def repr(obj: object, /) -> str: ... # and https://github.com/python/typeshed/pull/9151 # on why we don't use `SupportsRound` from `typing.pyi` +@type_check_only class _SupportsRound1(Protocol[_T_co]): def __round__(self) -> _T_co: ... +@type_check_only class _SupportsRound2(Protocol[_T_co]): def __round__(self, ndigits: int, /) -> _T_co: ... @@ -1645,6 +1811,7 @@ def sorted(iterable: Iterable[_T], /, *, key: Callable[[_T], SupportsRichCompari _AddableT1 = TypeVar("_AddableT1", bound=SupportsAdd[Any, Any]) _AddableT2 = TypeVar("_AddableT2", bound=SupportsAdd[Any, Any]) +@type_check_only class _SupportsSumWithNoDefaultGiven(SupportsAdd[Any, Any], SupportsRAdd[int, Any], Protocol): ... _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWithNoDefaultGiven) @@ -1666,22 +1833,29 @@ def sum(iterable: Iterable[_AddableT1], /, start: _AddableT2) -> _AddableT1 | _A def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... @overload def vars(object: Any = ..., /) -> dict[str, Any]: ... - +@disjoint_base class zip(Iterator[_T_co]): if sys.version_info >= (3, 10): @overload - def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... + def __new__(cls, *, strict: bool = False) -> zip[Any]: ... @overload - def __new__(cls, iter1: Iterable[_T1], /, *, strict: bool = ...) -> zip[tuple[_T1]]: ... + def __new__(cls, iter1: Iterable[_T1], /, *, strict: bool = False) -> zip[tuple[_T1]]: ... @overload - def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, strict: bool = ...) -> zip[tuple[_T1, _T2]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, strict: bool = False) -> zip[tuple[_T1, _T2]]: ... @overload def __new__( - cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, strict: bool = ... + cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, strict: bool = False ) -> zip[tuple[_T1, _T2, _T3]]: ... @overload def __new__( - cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /, *, strict: bool = ... + cls, + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + iter4: Iterable[_T4], + /, + *, + strict: bool = False, ) -> zip[tuple[_T1, _T2, _T3, _T4]]: ... @overload def __new__( @@ -1693,7 +1867,7 @@ class zip(Iterator[_T_co]): iter5: Iterable[_T5], /, *, - strict: bool = ..., + strict: bool = False, ) -> zip[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload def __new__( @@ -1706,7 +1880,7 @@ class zip(Iterator[_T_co]): iter6: Iterable[Any], /, *iterables: Iterable[Any], - strict: bool = ..., + strict: bool = False, ) -> zip[tuple[Any, ...]]: ... else: @overload @@ -1747,20 +1921,20 @@ def __import__( name: str, globals: Mapping[str, object] | None = None, locals: Mapping[str, object] | None = None, - fromlist: Sequence[str] = (), + fromlist: Sequence[str] | None = (), level: int = 0, ) -> types.ModuleType: ... def __build_class__(func: Callable[[], CellType | Any], name: str, /, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... if sys.version_info >= (3, 10): - from types import EllipsisType + from types import EllipsisType, NotImplementedType # Backwards compatibility hack for folks who relied on the ellipsis type # existing in typeshed in Python 3.9 and earlier. ellipsis = EllipsisType Ellipsis: EllipsisType - + NotImplemented: NotImplementedType else: # Actually the type of Ellipsis is , but since it's # not exposed anywhere under that name, we make it private here. @@ -1770,6 +1944,13 @@ else: Ellipsis: ellipsis + @final + @type_check_only + class _NotImplementedType(Any): ... + + NotImplemented: _NotImplementedType + +@disjoint_base class BaseException: args: tuple[Any, ...] __cause__: BaseException | None @@ -1780,6 +1961,10 @@ class BaseException: 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: ... + # Necessary for security-focused static analyzers (e.g, pysa) + # See https://github.com/python/typeshed/pull/14900 + def __str__(self) -> str: ... # noqa: Y029 + def __repr__(self) -> str: ... # noqa: Y029 if sys.version_info >= (3, 11): # only present after add_note() is called __notes__: list[str] @@ -1788,17 +1973,20 @@ class BaseException: class GeneratorExit(BaseException): ... class KeyboardInterrupt(BaseException): ... +@disjoint_base class SystemExit(BaseException): code: sys._ExitCode class Exception(BaseException): ... +@disjoint_base class StopIteration(Exception): value: Any +@disjoint_base class OSError(Exception): - errno: int - strerror: str + errno: int | None + strerror: str | None # filename, filename2 are actually str | bytes | None filename: Any filename2: Any @@ -1813,17 +2001,22 @@ if sys.platform == "win32": class ArithmeticError(Exception): ... class AssertionError(Exception): ... -class AttributeError(Exception): - if sys.version_info >= (3, 10): - def __init__(self, *args: object, name: str | None = ..., obj: object = ...) -> None: ... - name: str +if sys.version_info >= (3, 10): + @disjoint_base + class AttributeError(Exception): + def __init__(self, *args: object, name: str | None = None, obj: object = None) -> None: ... + name: str | None obj: object +else: + class AttributeError(Exception): ... + class BufferError(Exception): ... class EOFError(Exception): ... +@disjoint_base class ImportError(Exception): - def __init__(self, *args: object, name: str | None = ..., path: str | None = ...) -> None: ... + def __init__(self, *args: object, name: str | None = None, path: str | None = None) -> None: ... name: str | None path: str | None msg: str # undocumented @@ -1833,27 +2026,49 @@ class ImportError(Exception): class LookupError(Exception): ... class MemoryError(Exception): ... -class NameError(Exception): - if sys.version_info >= (3, 10): - def __init__(self, *args: object, name: str | None = ...) -> None: ... - name: str +if sys.version_info >= (3, 10): + @disjoint_base + class NameError(Exception): + def __init__(self, *args: object, name: str | None = None) -> None: ... + name: str | None + +else: + class NameError(Exception): ... class ReferenceError(Exception): ... class RuntimeError(Exception): ... +class StopAsyncIteration(Exception): ... -class StopAsyncIteration(Exception): - value: Any - +@disjoint_base 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): ... @@ -1888,6 +2103,7 @@ class IndentationError(SyntaxError): ... class TabError(IndentationError): ... class UnicodeError(ValueError): ... +@disjoint_base class UnicodeDecodeError(UnicodeError): encoding: str object: bytes @@ -1896,6 +2112,7 @@ class UnicodeDecodeError(UnicodeError): reason: str def __init__(self, encoding: str, object: ReadableBuffer, start: int, end: int, reason: str, /) -> None: ... +@disjoint_base class UnicodeEncodeError(UnicodeError): encoding: str object: str @@ -1904,6 +2121,7 @@ class UnicodeEncodeError(UnicodeError): reason: str def __init__(self, encoding: str, object: str, start: int, end: int, reason: str, /) -> None: ... +@disjoint_base class UnicodeTranslateError(UnicodeError): encoding: None object: str @@ -1934,6 +2152,7 @@ if sys.version_info >= (3, 11): _ExceptionT = TypeVar("_ExceptionT", bound=Exception) # See `check_exception_group.py` for use-cases and comments. + @disjoint_base class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): def __new__(cls, message: str, exceptions: Sequence[_BaseExceptionT_co], /) -> Self: ... def __init__(self, message: str, exceptions: Sequence[_BaseExceptionT_co], /) -> None: ... @@ -1943,27 +2162,27 @@ if sys.version_info >= (3, 11): def exceptions(self) -> tuple[_BaseExceptionT_co | BaseExceptionGroup[_BaseExceptionT_co], ...]: ... @overload def subgroup( - self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / + self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> ExceptionGroup[_ExceptionT] | None: ... @overload def subgroup( - self, condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], / + self, matcher_value: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], / ) -> BaseExceptionGroup[_BaseExceptionT] | None: ... @overload def subgroup( - self, condition: Callable[[_BaseExceptionT_co | Self], bool], / + self, matcher_value: Callable[[_BaseExceptionT_co | Self], bool], / ) -> BaseExceptionGroup[_BaseExceptionT_co] | None: ... @overload def split( - self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / + self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> tuple[ExceptionGroup[_ExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ... @overload def split( - self, condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], / + self, matcher_value: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...], / ) -> tuple[BaseExceptionGroup[_BaseExceptionT] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ... @overload def split( - self, condition: Callable[[_BaseExceptionT_co | Self], bool], / + self, matcher_value: Callable[[_BaseExceptionT_co | Self], bool], / ) -> tuple[BaseExceptionGroup[_BaseExceptionT_co] | None, BaseExceptionGroup[_BaseExceptionT_co] | None]: ... # In reality it is `NonEmptySequence`: @overload @@ -1980,17 +2199,19 @@ if sys.version_info >= (3, 11): # We accept a narrower type, but that's OK. @overload # type: ignore[override] def subgroup( - self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / + self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> ExceptionGroup[_ExceptionT] | None: ... @overload - def subgroup(self, condition: Callable[[_ExceptionT_co | Self], bool], /) -> ExceptionGroup[_ExceptionT_co] | None: ... + def subgroup( + self, matcher_value: Callable[[_ExceptionT_co | Self], bool], / + ) -> ExceptionGroup[_ExceptionT_co] | None: ... @overload # type: ignore[override] def split( - self, condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / + self, matcher_value: type[_ExceptionT] | tuple[type[_ExceptionT], ...], / ) -> tuple[ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None]: ... @overload def split( - self, condition: Callable[[_ExceptionT_co | Self], bool], / + self, matcher_value: Callable[[_ExceptionT_co | Self], bool], / ) -> tuple[ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None]: ... if sys.version_info >= (3, 13): diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index a7837e1b9ff8a..7bd829d040cb8 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -1,18 +1,25 @@ -import _compression import sys -from _compression import BaseStream +from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompressor from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Iterable -from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, final, overload +from io import TextIOWrapper +from typing import IO, Literal, Protocol, SupportsIndex, overload, type_check_only from typing_extensions import Self, TypeAlias +if sys.version_info >= (3, 14): + from compression._common._streams import BaseStream, _Reader +else: + from _compression import BaseStream, _Reader + __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "decompress"] # The following attributes and methods are optional: # def fileno(self) -> int: ... # def close(self) -> object: ... -class _ReadableFileobj(_compression._Reader, Protocol): ... +@type_check_only +class _ReadableFileobj(_Reader, Protocol): ... +@type_check_only class _WritableFileobj(Protocol): def write(self, b: bytes, /) -> object: ... # The following attributes and methods are optional: @@ -44,7 +51,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: _WritableFileobj, @@ -62,7 +69,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: StrOrBytesPath, @@ -80,7 +87,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj, @@ -89,58 +96,24 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> BZ2File | TextIO: ... +) -> BZ2File | TextIOWrapper: ... class BZ2File(BaseStream, IO[bytes]): def __enter__(self) -> Self: ... - if sys.version_info >= (3, 9): - @overload - def __init__(self, filename: _WritableFileobj, mode: _WriteBinaryMode, *, compresslevel: int = 9) -> None: ... - @overload - def __init__(self, filename: _ReadableFileobj, mode: _ReadBinaryMode = "r", *, compresslevel: int = 9) -> None: ... - @overload - def __init__( - self, filename: StrOrBytesPath, mode: _ReadBinaryMode | _WriteBinaryMode = "r", *, compresslevel: int = 9 - ) -> None: ... - else: - @overload - def __init__( - self, filename: _WritableFileobj, mode: _WriteBinaryMode, buffering: Any | None = None, compresslevel: int = 9 - ) -> None: ... - @overload - def __init__( - self, filename: _ReadableFileobj, mode: _ReadBinaryMode = "r", buffering: Any | None = None, compresslevel: int = 9 - ) -> None: ... - @overload - def __init__( - self, - filename: StrOrBytesPath, - mode: _ReadBinaryMode | _WriteBinaryMode = "r", - buffering: Any | None = None, - compresslevel: int = 9, - ) -> None: ... - + @overload + def __init__(self, filename: _WritableFileobj, mode: _WriteBinaryMode, *, compresslevel: int = 9) -> None: ... + @overload + def __init__(self, filename: _ReadableFileobj, mode: _ReadBinaryMode = "r", *, compresslevel: int = 9) -> None: ... + @overload + def __init__( + self, filename: StrOrBytesPath, mode: _ReadBinaryMode | _WriteBinaryMode = "r", *, compresslevel: int = 9 + ) -> None: ... def read(self, size: int | None = -1) -> bytes: ... def read1(self, size: int = -1) -> 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/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index cabf3b881c30f..d00f0d5d2bce3 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -167,18 +167,18 @@ if sys.version_info >= (3, 12): NOVEMBER = 11 DECEMBER = 12 - JANUARY = Month.JANUARY - FEBRUARY = Month.FEBRUARY - MARCH = Month.MARCH - APRIL = Month.APRIL - MAY = Month.MAY - JUNE = Month.JUNE - JULY = Month.JULY - AUGUST = Month.AUGUST - SEPTEMBER = Month.SEPTEMBER - OCTOBER = Month.OCTOBER - NOVEMBER = Month.NOVEMBER - DECEMBER = Month.DECEMBER + JANUARY: Final = Month.JANUARY + FEBRUARY: Final = Month.FEBRUARY + MARCH: Final = Month.MARCH + APRIL: Final = Month.APRIL + MAY: Final = Month.MAY + JUNE: Final = Month.JUNE + JULY: Final = Month.JULY + AUGUST: Final = Month.AUGUST + SEPTEMBER: Final = Month.SEPTEMBER + OCTOBER: Final = Month.OCTOBER + NOVEMBER: Final = Month.NOVEMBER + DECEMBER: Final = Month.DECEMBER class Day(enum.IntEnum): MONDAY = 0 @@ -189,13 +189,13 @@ if sys.version_info >= (3, 12): SATURDAY = 5 SUNDAY = 6 - MONDAY = Day.MONDAY - TUESDAY = Day.TUESDAY - WEDNESDAY = Day.WEDNESDAY - THURSDAY = Day.THURSDAY - FRIDAY = Day.FRIDAY - SATURDAY = Day.SATURDAY - SUNDAY = Day.SUNDAY + MONDAY: Final = Day.MONDAY + TUESDAY: Final = Day.TUESDAY + WEDNESDAY: Final = Day.WEDNESDAY + THURSDAY: Final = Day.THURSDAY + FRIDAY: Final = Day.FRIDAY + SATURDAY: Final = Day.SATURDAY + SUNDAY: Final = Day.SUNDAY else: MONDAY: Final = 0 TUESDAY: Final = 1 diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi index 3a2e2a91b2419..0f9d4343b6307 100644 --- a/mypy/typeshed/stdlib/cgi.pyi +++ b/mypy/typeshed/stdlib/cgi.pyi @@ -1,9 +1,10 @@ +import os from _typeshed import SupportsContainsAndGetItem, SupportsGetItem, SupportsItemAccess, Unused from builtins import list as _list, type as _type from collections.abc import Iterable, Iterator, Mapping from email.message import Message from types import TracebackType -from typing import IO, Any, Protocol +from typing import IO, Any, Protocol, type_check_only from typing_extensions import Self __all__ = [ @@ -23,7 +24,7 @@ __all__ = [ def parse( fp: IO[Any] | None = None, - environ: SupportsItemAccess[str, str] = ..., + environ: SupportsItemAccess[str, str] = os.environ, keep_blank_values: bool = ..., strict_parsing: bool = ..., separator: str = "&", @@ -31,14 +32,14 @@ def parse( def parse_multipart( fp: IO[Any], pdict: SupportsGetItem[str, bytes], encoding: str = "utf-8", errors: str = "replace", separator: str = "&" ) -> dict[str, list[Any]]: ... - +@type_check_only class _Environ(Protocol): def __getitem__(self, k: str, /) -> str: ... def keys(self) -> Iterable[str]: ... def parse_header(line: str) -> tuple[str, dict[str, str]]: ... -def test(environ: _Environ = ...) -> None: ... -def print_environ(environ: _Environ = ...) -> None: ... +def test(environ: _Environ = os.environ) -> None: ... +def print_environ(environ: _Environ = os.environ) -> None: ... def print_form(form: dict[str, Any]) -> None: ... def print_directory() -> None: ... def print_environ_usage() -> None: ... @@ -85,7 +86,7 @@ class FieldStorage: fp: IO[Any] | None = None, headers: Mapping[str, str] | Message | None = None, outerboundary: bytes = b"", - environ: SupportsContainsAndGetItem[str, str] = ..., + environ: SupportsContainsAndGetItem[str, str] = os.environ, keep_blank_values: int = 0, strict_parsing: int = 0, limit: int | None = None, diff --git a/mypy/typeshed/stdlib/cmath.pyi b/mypy/typeshed/stdlib/cmath.pyi index fab9d10230f84..aed4c63862fe7 100644 --- a/mypy/typeshed/stdlib/cmath.pyi +++ b/mypy/typeshed/stdlib/cmath.pyi @@ -1,13 +1,13 @@ -from typing import SupportsComplex, SupportsFloat, SupportsIndex +from typing import Final, SupportsComplex, SupportsFloat, SupportsIndex from typing_extensions import TypeAlias -e: float -pi: float -inf: float -infj: complex -nan: float -nanj: complex -tau: float +e: Final[float] +pi: Final[float] +inf: Final[float] +infj: Final[complex] +nan: Final[float] +nanj: Final[complex] +tau: Final[float] _C: TypeAlias = SupportsFloat | SupportsComplex | SupportsIndex | complex @@ -23,7 +23,7 @@ def exp(z: _C, /) -> complex: ... def isclose(a: _C, b: _C, *, rel_tol: SupportsFloat = 1e-09, abs_tol: SupportsFloat = 0.0) -> bool: ... def isinf(z: _C, /) -> bool: ... def isnan(z: _C, /) -> bool: ... -def log(x: _C, base: _C = ..., /) -> complex: ... +def log(z: _C, base: _C = ..., /) -> complex: ... def log10(z: _C, /) -> complex: ... def phase(z: _C, /) -> float: ... def polar(z: _C, /) -> tuple[float, float]: ... diff --git a/mypy/typeshed/stdlib/code.pyi b/mypy/typeshed/stdlib/code.pyi index 02689238a9a51..0b13c8a5016d4 100644 --- a/mypy/typeshed/stdlib/code.pyi +++ b/mypy/typeshed/stdlib/code.pyi @@ -1,18 +1,22 @@ import sys -from codeop import CommandCompiler -from collections.abc import Callable, Mapping +from codeop import CommandCompiler, compile_command as compile_command +from collections.abc import Callable from types import CodeType from typing import Any __all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", "compile_command"] class InteractiveInterpreter: - locals: Mapping[str, Any] # undocumented + locals: dict[str, Any] # undocumented compile: CommandCompiler # undocumented - def __init__(self, locals: Mapping[str, Any] | None = None) -> None: ... + def __init__(self, locals: dict[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: ... @@ -21,11 +25,11 @@ class InteractiveConsole(InteractiveInterpreter): filename: str # undocumented if sys.version_info >= (3, 13): def __init__( - self, locals: Mapping[str, Any] | None = None, filename: str = "", *, local_exit: bool = False + self, locals: dict[str, Any] | None = None, filename: str = "", *, local_exit: bool = False ) -> None: ... def push(self, line: str, filename: str | None = None) -> bool: ... else: - def __init__(self, locals: Mapping[str, Any] | None = None, filename: str = "") -> None: ... + def __init__(self, locals: dict[str, Any] | None = None, filename: str = "") -> None: ... def push(self, line: str) -> bool: ... def interact(self, banner: str | None = None, exitmsg: str | None = None) -> None: ... @@ -36,7 +40,7 @@ if sys.version_info >= (3, 13): def interact( banner: str | None = None, readfunc: Callable[[str], str] | None = None, - local: Mapping[str, Any] | None = None, + local: dict[str, Any] | None = None, exitmsg: str | None = None, local_exit: bool = False, ) -> None: ... @@ -45,8 +49,6 @@ else: def interact( banner: str | None = None, readfunc: Callable[[str], str] | None = None, - local: Mapping[str, Any] | None = None, + local: dict[str, Any] | None = None, exitmsg: str | None = None, ) -> None: ... - -def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ... diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index a41df9752d335..fa4d4fd4ba928 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -1,10 +1,11 @@ +import sys import types from _codecs import * from _typeshed import ReadableBuffer from abc import abstractmethod from collections.abc import Callable, Generator, Iterable -from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO -from typing_extensions import Self +from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO, overload, type_check_only +from typing_extensions import Self, TypeAlias, disjoint_base __all__ = [ "register", @@ -58,16 +59,34 @@ BOM32_LE: Final = b"\xff\xfe" BOM64_BE: Final = b"\x00\x00\xfe\xff" BOM64_LE: Final = b"\xff\xfe\x00\x00" +_BufferedEncoding: TypeAlias = Literal[ + "idna", + "raw-unicode-escape", + "unicode-escape", + "utf-16", + "utf-16-be", + "utf-16-le", + "utf-32", + "utf-32-be", + "utf-32-le", + "utf-7", + "utf-8", + "utf-8-sig", +] + +@type_check_only class _WritableStream(Protocol): def write(self, data: bytes, /) -> object: ... def seek(self, offset: int, whence: int, /) -> object: ... def close(self) -> object: ... +@type_check_only class _ReadableStream(Protocol): def read(self, size: int = ..., /) -> bytes: ... def seek(self, offset: int, whence: int, /) -> object: ... def close(self) -> object: ... +@type_check_only class _Stream(_WritableStream, _ReadableStream, Protocol): ... # TODO: this only satisfies the most common interface, where @@ -76,55 +95,99 @@ class _Stream(_WritableStream, _ReadableStream, Protocol): ... # There *are* bytes->bytes and str->str encodings in the standard library. # They were much more common in Python 2 than in Python 3. +@type_check_only class _Encoder(Protocol): def __call__(self, input: str, errors: str = ..., /) -> tuple[bytes, int]: ... # signature of Codec().encode +@type_check_only class _Decoder(Protocol): def __call__(self, input: ReadableBuffer, errors: str = ..., /) -> tuple[str, int]: ... # signature of Codec().decode +@type_check_only class _StreamReader(Protocol): def __call__(self, stream: _ReadableStream, errors: str = ..., /) -> StreamReader: ... +@type_check_only class _StreamWriter(Protocol): def __call__(self, stream: _WritableStream, errors: str = ..., /) -> StreamWriter: ... +@type_check_only class _IncrementalEncoder(Protocol): def __call__(self, errors: str = ...) -> IncrementalEncoder: ... +@type_check_only class _IncrementalDecoder(Protocol): def __call__(self, errors: str = ...) -> IncrementalDecoder: ... -class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]): - _is_text_encoding: bool - @property - def encode(self) -> _Encoder: ... - @property - def decode(self) -> _Decoder: ... - @property - def streamreader(self) -> _StreamReader: ... - @property - def streamwriter(self) -> _StreamWriter: ... - @property - def incrementalencoder(self) -> _IncrementalEncoder: ... - @property - def incrementaldecoder(self) -> _IncrementalDecoder: ... - name: str - def __new__( - cls, - encode: _Encoder, - decode: _Decoder, - streamreader: _StreamReader | None = None, - streamwriter: _StreamWriter | None = None, - incrementalencoder: _IncrementalEncoder | None = None, - incrementaldecoder: _IncrementalDecoder | None = None, - name: str | None = None, - *, - _is_text_encoding: bool | None = None, - ) -> Self: ... +@type_check_only +class _BufferedIncrementalDecoder(Protocol): + def __call__(self, errors: str = ...) -> BufferedIncrementalDecoder: ... + +if sys.version_info >= (3, 12): + class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]): + _is_text_encoding: bool + @property + def encode(self) -> _Encoder: ... + @property + def decode(self) -> _Decoder: ... + @property + def streamreader(self) -> _StreamReader: ... + @property + def streamwriter(self) -> _StreamWriter: ... + @property + def incrementalencoder(self) -> _IncrementalEncoder: ... + @property + def incrementaldecoder(self) -> _IncrementalDecoder: ... + name: str + def __new__( + cls, + encode: _Encoder, + decode: _Decoder, + streamreader: _StreamReader | None = None, + streamwriter: _StreamWriter | None = None, + incrementalencoder: _IncrementalEncoder | None = None, + incrementaldecoder: _IncrementalDecoder | None = None, + name: str | None = None, + *, + _is_text_encoding: bool | None = None, + ) -> Self: ... + +else: + @disjoint_base + class CodecInfo(tuple[_Encoder, _Decoder, _StreamReader, _StreamWriter]): + _is_text_encoding: bool + @property + def encode(self) -> _Encoder: ... + @property + def decode(self) -> _Decoder: ... + @property + def streamreader(self) -> _StreamReader: ... + @property + def streamwriter(self) -> _StreamWriter: ... + @property + def incrementalencoder(self) -> _IncrementalEncoder: ... + @property + def incrementaldecoder(self) -> _IncrementalDecoder: ... + name: str + def __new__( + cls, + encode: _Encoder, + decode: _Decoder, + streamreader: _StreamReader | None = None, + streamwriter: _StreamWriter | None = None, + incrementalencoder: _IncrementalEncoder | None = None, + incrementaldecoder: _IncrementalDecoder | None = None, + name: str | None = None, + *, + _is_text_encoding: bool | None = None, + ) -> Self: ... def getencoder(encoding: str) -> _Encoder: ... def getdecoder(encoding: str) -> _Decoder: ... def getincrementalencoder(encoding: str) -> _IncrementalEncoder: ... +@overload +def getincrementaldecoder(encoding: _BufferedEncoding) -> _BufferedIncrementalDecoder: ... +@overload def getincrementaldecoder(encoding: str) -> _IncrementalDecoder: ... def getreader(encoding: str) -> _StreamReader: ... def getwriter(encoding: str) -> _StreamWriter: ... @@ -202,6 +265,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 +273,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: ... @@ -250,6 +317,8 @@ class StreamReaderWriter(TextIO): def writable(self) -> bool: ... class StreamRecoder(BinaryIO): + data_encoding: str + file_encoding: str def __init__( self, stream: _Stream, diff --git a/mypy/typeshed/stdlib/codeop.pyi b/mypy/typeshed/stdlib/codeop.pyi index 6a51b7786384c..8e311343eb89d 100644 --- a/mypy/typeshed/stdlib/codeop.pyi +++ b/mypy/typeshed/stdlib/codeop.pyi @@ -1,12 +1,20 @@ +import sys from types import CodeType __all__ = ["compile_command", "Compile", "CommandCompiler"] -def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ... +if sys.version_info >= (3, 14): + def compile_command(source: str, filename: str = "", symbol: str = "single", flags: int = 0) -> CodeType | None: ... + +else: + def compile_command(source: str, filename: str = "", symbol: str = "single") -> CodeType | None: ... class Compile: flags: int - def __call__(self, source: str, filename: str, symbol: str) -> CodeType: ... + if sys.version_info >= (3, 13): + def __call__(self, source: str, filename: str, symbol: str, flags: int = 0) -> CodeType: ... + else: + def __call__(self, source: str, filename: str, symbol: str) -> CodeType: ... class CommandCompiler: compiler: Compile diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index b2ed53e4427eb..8636e6cdbdc31 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -1,11 +1,9 @@ import sys from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT -from typing import Any, Generic, NoReturn, SupportsIndex, TypeVar, final, overload -from typing_extensions import Self - -if sys.version_info >= (3, 9): - from types import GenericAlias +from types import GenericAlias +from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload, type_check_only +from typing_extensions import Self, disjoint_base if sys.version_info >= (3, 10): from collections.abc import ( @@ -17,7 +15,6 @@ if sys.version_info >= (3, 10): Mapping, MutableMapping, MutableSequence, - Reversible, Sequence, ValuesView, ) @@ -94,24 +91,25 @@ class UserDict(MutableMapping[_KT, _VT]): @classmethod @overload def fromkeys(cls, iterable: Iterable[_T], value: _S) -> UserDict[_T, _S]: ... - if sys.version_info >= (3, 9): - @overload - def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... - @overload - def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... - @overload - def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... - @overload - def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... - # UserDict.__ior__ should be kept roughly in line with MutableMapping.update() - @overload # type: ignore[misc] - def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... - @overload - def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... + @overload + def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... + @overload + def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... + @overload + def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... + # UserDict.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... + @overload + def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... if sys.version_info >= (3, 12): @overload def get(self, key: _KT, default: None = None) -> _VT | None: ... @overload + def get(self, key: _KT, default: _VT) -> _VT: ... + @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... class UserList(MutableSequence[_T]): @@ -120,6 +118,7 @@ class UserList(MutableSequence[_T]): def __init__(self, initlist: None = None) -> None: ... @overload def __init__(self, initlist: Iterable[_T]) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __lt__(self, other: list[_T] | UserList[_T]) -> bool: ... def __le__(self, other: list[_T] | UserList[_T]) -> bool: ... def __gt__(self, other: list[_T] | UserList[_T]) -> bool: ... @@ -213,10 +212,8 @@ class UserString(Sequence[UserString]): def lstrip(self, chars: str | None = None) -> Self: ... maketrans = str.maketrans def partition(self, sep: str) -> tuple[str, str, str]: ... - if sys.version_info >= (3, 9): - def removeprefix(self, prefix: str | UserString, /) -> Self: ... - def removesuffix(self, suffix: str | UserString, /) -> Self: ... - + def removeprefix(self, prefix: str | UserString, /) -> Self: ... + def removesuffix(self, suffix: str | UserString, /) -> Self: ... def replace(self, old: str | UserString, new: str | UserString, maxsplit: int = -1) -> Self: ... def rfind(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ... def rindex(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ... @@ -234,6 +231,7 @@ class UserString(Sequence[UserString]): def upper(self) -> Self: ... def zfill(self, width: int) -> Self: ... +@disjoint_base class deque(MutableSequence[_T]): @property def maxlen(self) -> int | None: ... @@ -255,6 +253,7 @@ class deque(MutableSequence[_T]): def rotate(self, n: int = 1, /) -> None: ... def __copy__(self) -> Self: ... def __len__(self) -> int: ... + __hash__: ClassVar[None] # type: ignore[assignment] # These methods of deque don't take slices, unlike MutableSequence, hence the type: ignores def __getitem__(self, key: SupportsIndex, /) -> _T: ... # type: ignore[override] def __setitem__(self, key: SupportsIndex, value: _T, /) -> None: ... # type: ignore[override] @@ -270,8 +269,7 @@ class deque(MutableSequence[_T]): def __gt__(self, value: deque[_T], /) -> bool: ... def __ge__(self, value: deque[_T], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Counter(dict[_T, int], Generic[_T]): @overload @@ -331,13 +329,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 +343,22 @@ 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[reportGeneralTypeIssues] +@type_check_only +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[reportGeneralTypeIssues] +@type_check_only +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[reportGeneralTypeIssues] +@type_check_only +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]): +@disjoint_base +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: ... @@ -386,16 +388,16 @@ class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... def __eq__(self, value: object, /) -> bool: ... - if sys.version_info >= (3, 9): - @overload - def __or__(self, value: dict[_KT, _VT], /) -> Self: ... - @overload - def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... - @overload - def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... - @overload - def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] + @overload + def __or__(self, value: dict[_KT, _VT], /) -> Self: ... + @overload + def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... + @overload + def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] +@disjoint_base class defaultdict(dict[_KT, _VT]): default_factory: Callable[[], _VT] | None @overload @@ -434,15 +436,14 @@ class defaultdict(dict[_KT, _VT]): def __missing__(self, key: _KT, /) -> _VT: ... def __copy__(self) -> Self: ... def copy(self) -> Self: ... - if sys.version_info >= (3, 9): - @overload - def __or__(self, value: dict[_KT, _VT], /) -> Self: ... - @overload - def __or__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... - @overload - def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... - @overload - def __ror__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] + @overload + def __or__(self, value: dict[_KT, _VT], /) -> Self: ... + @overload + def __or__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... + @overload + def __ror__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] class ChainMap(MutableMapping[_KT, _VT]): maps: list[MutableMapping[_KT, _VT]] @@ -459,6 +460,8 @@ class ChainMap(MutableMapping[_KT, _VT]): @overload def get(self, key: _KT, default: None = None) -> _VT | None: ... @overload + def get(self, key: _KT, default: _VT) -> _VT: ... + @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... def __missing__(self, key: _KT) -> _VT: ... # undocumented def __bool__(self) -> bool: ... @@ -477,9 +480,15 @@ class ChainMap(MutableMapping[_KT, _VT]): __copy__ = copy # 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]: ... + if sys.version_info >= (3, 13): + @classmethod + @overload + def fromkeys(cls, iterable: Iterable[_T], /) -> ChainMap[_T, Any | None]: ... + else: + @classmethod + @overload + def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ... + @classmethod @overload # Special-case None: the user probably wants to add non-None values later. @@ -487,17 +496,16 @@ class ChainMap(MutableMapping[_KT, _VT]): @classmethod @overload def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> ChainMap[_T, _S]: ... - if sys.version_info >= (3, 9): - @overload - def __or__(self, other: Mapping[_KT, _VT]) -> Self: ... - @overload - def __or__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... - @overload - def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ... - @overload - def __ror__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... - # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update() - @overload # type: ignore[misc] - def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... - @overload - def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... + @overload + def __or__(self, other: Mapping[_KT, _VT]) -> Self: ... + @overload + def __or__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ... + @overload + def __ror__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... + # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... + @overload + def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... diff --git a/mypy/typeshed/stdlib/colorsys.pyi b/mypy/typeshed/stdlib/colorsys.pyi index 443ee828ebfe7..4afcb5392b58e 100644 --- a/mypy/typeshed/stdlib/colorsys.pyi +++ b/mypy/typeshed/stdlib/colorsys.pyi @@ -1,3 +1,5 @@ +from typing import Final + __all__ = ["rgb_to_yiq", "yiq_to_rgb", "rgb_to_hls", "hls_to_rgb", "rgb_to_hsv", "hsv_to_rgb"] def rgb_to_yiq(r: float, g: float, b: float) -> tuple[float, float, float]: ... @@ -7,7 +9,7 @@ def hls_to_rgb(h: float, l: float, s: float) -> tuple[float, float, float]: ... def rgb_to_hsv(r: float, g: float, b: float) -> tuple[float, float, float]: ... def hsv_to_rgb(h: float, s: float, v: float) -> tuple[float, float, float]: ... -# TODO undocumented -ONE_SIXTH: float -ONE_THIRD: float -TWO_THIRD: float +# TODO: undocumented +ONE_SIXTH: Final[float] +ONE_THIRD: Final[float] +TWO_THIRD: Final[float] diff --git a/mypy/typeshed/stdlib/compileall.pyi b/mypy/typeshed/stdlib/compileall.pyi index 9fb3608f2979b..8972d50a4a634 100644 --- a/mypy/typeshed/stdlib/compileall.pyi +++ b/mypy/typeshed/stdlib/compileall.pyi @@ -1,10 +1,11 @@ import sys from _typeshed import StrPath from py_compile import PycInvalidationMode -from typing import Any, Protocol +from typing import Any, Protocol, type_check_only __all__ = ["compile_dir", "compile_file", "compile_path"] +@type_check_only class _SupportsSearch(Protocol): def search(self, string: str, /) -> Any: ... @@ -25,7 +26,7 @@ if sys.version_info >= (3, 10): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... def compile_file( fullname: StrPath, ddir: StrPath | None = None, @@ -40,9 +41,9 @@ if sys.version_info >= (3, 10): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... -elif sys.version_info >= (3, 9): +else: def compile_dir( dir: StrPath, maxlevels: int | None = None, @@ -59,7 +60,7 @@ elif sys.version_info >= (3, 9): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... + ) -> bool: ... def compile_file( fullname: StrPath, ddir: StrPath | None = None, @@ -74,31 +75,7 @@ elif sys.version_info >= (3, 9): prependdir: StrPath | None = None, limit_sl_dest: StrPath | None = None, hardlink_dupes: bool = False, - ) -> int: ... - -else: - def compile_dir( - dir: StrPath, - maxlevels: int = 10, - ddir: StrPath | None = None, - force: bool = False, - rx: _SupportsSearch | None = None, - quiet: int = 0, - legacy: bool = False, - optimize: int = -1, - workers: int = 1, - invalidation_mode: PycInvalidationMode | None = None, - ) -> int: ... - def compile_file( - fullname: StrPath, - ddir: StrPath | None = None, - force: bool = False, - rx: _SupportsSearch | None = None, - quiet: int = 0, - legacy: bool = False, - optimize: int = -1, - invalidation_mode: PycInvalidationMode | None = None, - ) -> int: ... + ) -> bool: ... def compile_path( skip_curdir: bool = ..., @@ -108,4 +85,4 @@ def compile_path( legacy: bool = False, optimize: int = -1, invalidation_mode: PycInvalidationMode | None = None, -) -> int: ... +) -> bool: ... diff --git a/mypy/typeshed/stdlib/compression/__init__.pyi b/mypy/typeshed/stdlib/compression/__init__.pyi new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mypy/typeshed/stdlib/compression/_common/__init__.pyi b/mypy/typeshed/stdlib/compression/_common/__init__.pyi new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mypy/typeshed/stdlib/compression/_common/_streams.pyi b/mypy/typeshed/stdlib/compression/_common/_streams.pyi new file mode 100644 index 0000000000000..96aec24d1c2d0 --- /dev/null +++ b/mypy/typeshed/stdlib/compression/_common/_streams.pyi @@ -0,0 +1,37 @@ +from _typeshed import ReadableBuffer, WriteableBuffer +from collections.abc import Callable +from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase +from typing import Any, Protocol, type_check_only + +BUFFER_SIZE = DEFAULT_BUFFER_SIZE + +@type_check_only +class _Reader(Protocol): + def read(self, n: int, /) -> bytes: ... + def seekable(self) -> bool: ... + def seek(self, n: int, /) -> Any: ... + +@type_check_only +class _Decompressor(Protocol): + def decompress(self, data: ReadableBuffer, /, max_length: int = ...) -> bytes: ... + @property + def unused_data(self) -> bytes: ... + @property + def eof(self) -> bool: ... + # `zlib._Decompress` does not have next property, but `DecompressReader` calls it: + # @property + # def needs_input(self) -> bool: ... + +class BaseStream(BufferedIOBase): ... + +class DecompressReader(RawIOBase): + def __init__( + self, + fp: _Reader, + decomp_factory: Callable[..., _Decompressor], # Consider backporting changes to _compression + trailing_error: type[Exception] | tuple[type[Exception], ...] = (), + **decomp_args: Any, # These are passed to decomp_factory. + ) -> None: ... + def readinto(self, b: WriteableBuffer) -> int: ... + def read(self, size: int = -1) -> bytes: ... + def seek(self, offset: int, whence: int = 0) -> int: ... diff --git a/mypy/typeshed/stdlib/compression/bz2.pyi b/mypy/typeshed/stdlib/compression/bz2.pyi new file mode 100644 index 0000000000000..9ddc39f27c286 --- /dev/null +++ b/mypy/typeshed/stdlib/compression/bz2.pyi @@ -0,0 +1 @@ +from bz2 import * diff --git a/mypy/typeshed/stdlib/compression/gzip.pyi b/mypy/typeshed/stdlib/compression/gzip.pyi new file mode 100644 index 0000000000000..9422a735c590e --- /dev/null +++ b/mypy/typeshed/stdlib/compression/gzip.pyi @@ -0,0 +1 @@ +from gzip import * diff --git a/mypy/typeshed/stdlib/compression/lzma.pyi b/mypy/typeshed/stdlib/compression/lzma.pyi new file mode 100644 index 0000000000000..936c3813db4f1 --- /dev/null +++ b/mypy/typeshed/stdlib/compression/lzma.pyi @@ -0,0 +1 @@ +from lzma import * diff --git a/mypy/typeshed/stdlib/compression/zlib.pyi b/mypy/typeshed/stdlib/compression/zlib.pyi new file mode 100644 index 0000000000000..78d176c03ee83 --- /dev/null +++ b/mypy/typeshed/stdlib/compression/zlib.pyi @@ -0,0 +1 @@ +from zlib import * diff --git a/mypy/typeshed/stdlib/compression/zstd/__init__.pyi b/mypy/typeshed/stdlib/compression/zstd/__init__.pyi new file mode 100644 index 0000000000000..d5da4be036129 --- /dev/null +++ b/mypy/typeshed/stdlib/compression/zstd/__init__.pyi @@ -0,0 +1,88 @@ +import enum +from _typeshed import ReadableBuffer +from collections.abc import Iterable, Mapping +from compression.zstd._zstdfile import ZstdFile, open +from typing import Final, final + +import _zstd +from _zstd import ZstdCompressor, ZstdDecompressor, ZstdDict, ZstdError, get_frame_size, zstd_version + +__all__ = ( + # compression.zstd + "COMPRESSION_LEVEL_DEFAULT", + "compress", + "CompressionParameter", + "decompress", + "DecompressionParameter", + "finalize_dict", + "get_frame_info", + "Strategy", + "train_dict", + # compression.zstd._zstdfile + "open", + "ZstdFile", + # _zstd + "get_frame_size", + "zstd_version", + "zstd_version_info", + "ZstdCompressor", + "ZstdDecompressor", + "ZstdDict", + "ZstdError", +) + +zstd_version_info: Final[tuple[int, int, int]] +COMPRESSION_LEVEL_DEFAULT: Final = _zstd.ZSTD_CLEVEL_DEFAULT + +class FrameInfo: + __slots__ = ("decompressed_size", "dictionary_id") + decompressed_size: int + dictionary_id: int + def __init__(self, decompressed_size: int, dictionary_id: int) -> None: ... + +def get_frame_info(frame_buffer: ReadableBuffer) -> FrameInfo: ... +def train_dict(samples: Iterable[ReadableBuffer], dict_size: int) -> ZstdDict: ... +def finalize_dict(zstd_dict: ZstdDict, /, samples: Iterable[ReadableBuffer], dict_size: int, level: int) -> ZstdDict: ... +def compress( + data: ReadableBuffer, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None +) -> bytes: ... +def decompress(data: ReadableBuffer, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> bytes: ... +@final +class CompressionParameter(enum.IntEnum): + compression_level = _zstd.ZSTD_c_compressionLevel + window_log = _zstd.ZSTD_c_windowLog + hash_log = _zstd.ZSTD_c_hashLog + chain_log = _zstd.ZSTD_c_chainLog + search_log = _zstd.ZSTD_c_searchLog + min_match = _zstd.ZSTD_c_minMatch + target_length = _zstd.ZSTD_c_targetLength + strategy = _zstd.ZSTD_c_strategy + enable_long_distance_matching = _zstd.ZSTD_c_enableLongDistanceMatching + ldm_hash_log = _zstd.ZSTD_c_ldmHashLog + ldm_min_match = _zstd.ZSTD_c_ldmMinMatch + ldm_bucket_size_log = _zstd.ZSTD_c_ldmBucketSizeLog + ldm_hash_rate_log = _zstd.ZSTD_c_ldmHashRateLog + content_size_flag = _zstd.ZSTD_c_contentSizeFlag + checksum_flag = _zstd.ZSTD_c_checksumFlag + dict_id_flag = _zstd.ZSTD_c_dictIDFlag + nb_workers = _zstd.ZSTD_c_nbWorkers + job_size = _zstd.ZSTD_c_jobSize + overlap_log = _zstd.ZSTD_c_overlapLog + def bounds(self) -> tuple[int, int]: ... + +@final +class DecompressionParameter(enum.IntEnum): + window_log_max = _zstd.ZSTD_d_windowLogMax + def bounds(self) -> tuple[int, int]: ... + +@final +class Strategy(enum.IntEnum): + fast = _zstd.ZSTD_fast + dfast = _zstd.ZSTD_dfast + greedy = _zstd.ZSTD_greedy + lazy = _zstd.ZSTD_lazy + lazy2 = _zstd.ZSTD_lazy2 + btlazy2 = _zstd.ZSTD_btlazy2 + btopt = _zstd.ZSTD_btopt + btultra = _zstd.ZSTD_btultra + btultra2 = _zstd.ZSTD_btultra2 diff --git a/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi b/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi new file mode 100644 index 0000000000000..e67b3d992f2f9 --- /dev/null +++ b/mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi @@ -0,0 +1,117 @@ +from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsWrite, WriteableBuffer +from collections.abc import Mapping +from compression._common import _streams +from compression.zstd import ZstdDict +from io import TextIOWrapper, _WrappedBuffer +from typing import Literal, Protocol, overload, type_check_only +from typing_extensions import TypeAlias + +from _zstd import ZstdCompressor, _ZstdCompressorFlushBlock, _ZstdCompressorFlushFrame + +__all__ = ("ZstdFile", "open") + +_ReadBinaryMode: TypeAlias = Literal["r", "rb"] +_WriteBinaryMode: TypeAlias = Literal["w", "wb", "x", "xb", "a", "ab"] +_ReadTextMode: TypeAlias = Literal["rt"] +_WriteTextMode: TypeAlias = Literal["wt", "xt", "at"] + +@type_check_only +class _FileBinaryRead(_streams._Reader, Protocol): + def close(self) -> None: ... + +@type_check_only +class _FileBinaryWrite(SupportsWrite[bytes], Protocol): + def close(self) -> None: ... + +class ZstdFile(_streams.BaseStream): + FLUSH_BLOCK = ZstdCompressor.FLUSH_BLOCK + FLUSH_FRAME = ZstdCompressor.FLUSH_FRAME + + @overload + def __init__( + self, + file: StrOrBytesPath | _FileBinaryRead, + /, + mode: _ReadBinaryMode = "r", + *, + level: None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + ) -> None: ... + @overload + def __init__( + self, + file: StrOrBytesPath | _FileBinaryWrite, + /, + mode: _WriteBinaryMode, + *, + level: int | None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + ) -> None: ... + def write(self, data: ReadableBuffer, /) -> int: ... + def flush(self, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 1) -> bytes: ... # type: ignore[override] + def read(self, size: int | None = -1) -> bytes: ... + def read1(self, size: int | None = -1) -> bytes: ... + def readinto(self, b: WriteableBuffer) -> int: ... + def readinto1(self, b: WriteableBuffer) -> int: ... + def readline(self, size: int | None = -1) -> bytes: ... + def seek(self, offset: int, whence: int = 0) -> int: ... + def peek(self, size: int = -1) -> bytes: ... + @property + def name(self) -> str | bytes: ... + @property + def mode(self) -> Literal["rb", "wb"]: ... + +@overload +def open( + file: StrOrBytesPath | _FileBinaryRead, + /, + mode: _ReadBinaryMode = "rb", + *, + level: None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, +) -> ZstdFile: ... +@overload +def open( + file: StrOrBytesPath | _FileBinaryWrite, + /, + mode: _WriteBinaryMode, + *, + level: int | None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, +) -> ZstdFile: ... +@overload +def open( + file: StrOrBytesPath | _WrappedBuffer, + /, + mode: _ReadTextMode, + *, + level: None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, +) -> TextIOWrapper: ... +@overload +def open( + file: StrOrBytesPath | _WrappedBuffer, + /, + mode: _WriteTextMode, + *, + level: int | None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, +) -> TextIOWrapper: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi index 68fd0bc5acb43..ad4d20ea54453 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi @@ -16,7 +16,27 @@ from ._base import ( from .process import ProcessPoolExecutor as ProcessPoolExecutor from .thread import ThreadPoolExecutor as ThreadPoolExecutor -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 14): + from .interpreter import InterpreterPoolExecutor as InterpreterPoolExecutor + + __all__ = [ + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "CancelledError", + "TimeoutError", + "InvalidStateError", + "BrokenExecutor", + "Future", + "Executor", + "wait", + "as_completed", + "ProcessPoolExecutor", + "ThreadPoolExecutor", + "InterpreterPoolExecutor", + ] + +elif sys.version_info >= (3, 13): __all__ = ( "FIRST_COMPLETED", "FIRST_EXCEPTION", diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 0c019457902b0..be48a6e4289c8 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -1,15 +1,12 @@ import sys import threading from _typeshed import Unused -from collections.abc import Callable, Collection, Iterable, Iterator +from collections.abc import Callable, Iterable, Iterator from logging import Logger -from types import TracebackType -from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar +from types import GenericAlias, TracebackType +from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar, type_check_only from typing_extensions import ParamSpec, Self -if sys.version_info >= (3, 9): - from types import GenericAlias - FIRST_COMPLETED: Final = "FIRST_COMPLETED" FIRST_EXCEPTION: Final = "FIRST_EXCEPTION" ALL_COMPLETED: Final = "ALL_COMPLETED" @@ -18,8 +15,7 @@ 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] +_STATE_TO_DESCRIPTION_MAP: Final[dict[str, str]] LOGGER: Logger class Error(Exception): ... @@ -53,28 +49,31 @@ class Future(Generic[_T]): def set_result(self, result: _T) -> None: ... def exception(self, timeout: float | None = None) -> BaseException | None: ... def set_exception(self, exception: BaseException | None) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Executor: - if sys.version_info >= (3, 9): - def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ... - else: - def submit(self, fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ... - - def map( - self, fn: Callable[..., _T], *iterables: Iterable[Any], timeout: float | None = None, chunksize: int = 1 - ) -> Iterator[_T]: ... - if sys.version_info >= (3, 9): - def shutdown(self, wait: bool = True, *, cancel_futures: bool = False) -> None: ... + def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ... + if sys.version_info >= (3, 14): + def map( + self, + fn: Callable[..., _T], + *iterables: Iterable[Any], + timeout: float | None = None, + chunksize: int = 1, + buffersize: int | None = None, + ) -> Iterator[_T]: ... else: - def shutdown(self, wait: bool = True) -> None: ... + def map( + self, fn: Callable[..., _T], *iterables: Iterable[Any], timeout: float | None = None, chunksize: int = 1 + ) -> Iterator[_T]: ... + def shutdown(self, wait: bool = True, *, cancel_futures: bool = False) -> None: ... def __enter__(self) -> Self: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> bool | None: ... +@type_check_only class _AsCompletedFuture(Protocol[_T_co]): # as_completed only mutates non-generic aspects of passed Futures and does not do any nominal # checks. Therefore, we can use a Protocol here to allow as_completed to act covariantly. @@ -91,15 +90,9 @@ class DoneAndNotDoneFutures(NamedTuple, Generic[_T]): done: set[Future[_T]] not_done: set[Future[_T]] -if sys.version_info >= (3, 9): - def wait( - fs: Iterable[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED" - ) -> DoneAndNotDoneFutures[_T]: ... - -else: - def wait( - fs: Collection[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED" - ) -> DoneAndNotDoneFutures[_T]: ... +def wait( + fs: Iterable[Future[_T]], timeout: float | None = None, return_when: str = "ALL_COMPLETED" +) -> DoneAndNotDoneFutures[_T]: ... class _Waiter: event: threading.Event diff --git a/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi new file mode 100644 index 0000000000000..e101022babcb6 --- /dev/null +++ b/mypy/typeshed/stdlib/concurrent/futures/interpreter.pyi @@ -0,0 +1,79 @@ +import sys +from collections.abc import Callable +from concurrent.futures import ThreadPoolExecutor +from typing import Any, Literal, Protocol, overload, type_check_only +from typing_extensions import ParamSpec, Self, TypeAlias, TypeVar, TypeVarTuple, Unpack + +_Task: TypeAlias = tuple[bytes, Literal["function", "script"]] +_Ts = TypeVarTuple("_Ts") +_P = ParamSpec("_P") +_R = TypeVar("_R") + +@type_check_only +class _TaskFunc(Protocol): + @overload + def __call__(self, fn: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> tuple[bytes, Literal["function"]]: ... + @overload + def __call__(self, fn: str) -> tuple[bytes, Literal["script"]]: ... + +if sys.version_info >= (3, 14): + from concurrent.futures.thread import BrokenThreadPool, WorkerContext as ThreadWorkerContext + from concurrent.interpreters import Interpreter, Queue + + def do_call(results: Queue, func: Callable[..., _R], args: tuple[Any, ...], kwargs: dict[str, Any]) -> _R: ... + + class WorkerContext(ThreadWorkerContext): + interp: Interpreter | None + results: Queue | None + @overload # type: ignore[override] + @classmethod + def prepare( + cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] + ) -> tuple[Callable[[], Self], _TaskFunc]: ... + @overload + @classmethod + def prepare(cls, initializer: Callable[[], object], initargs: tuple[()]) -> tuple[Callable[[], Self], _TaskFunc]: ... + def __init__(self, initdata: _Task) -> None: ... + def __del__(self) -> None: ... + def run(self, task: _Task) -> None: ... # type: ignore[override] + + class BrokenInterpreterPool(BrokenThreadPool): ... + + class InterpreterPoolExecutor(ThreadPoolExecutor): + BROKEN: type[BrokenInterpreterPool] + + @overload # type: ignore[override] + @classmethod + def prepare_context( + cls, initializer: Callable[[], object], initargs: tuple[()] + ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ... + @overload + @classmethod + def prepare_context( + cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] + ) -> tuple[Callable[[], WorkerContext], _TaskFunc]: ... + @overload + def __init__( + self, + max_workers: int | None = None, + thread_name_prefix: str = "", + initializer: Callable[[], object] | None = None, + initargs: tuple[()] = (), + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None = None, + thread_name_prefix: str = "", + *, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], + ) -> None: ... + @overload + def __init__( + self, + max_workers: int | None, + thread_name_prefix: str, + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], + ) -> None: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi index d3706a9c15a6f..071b3aba5d330 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi @@ -5,7 +5,7 @@ from multiprocessing.context import BaseContext, Process from multiprocessing.queues import Queue, SimpleQueue from threading import Lock, Semaphore, Thread from types import TracebackType -from typing import Any, Generic, TypeVar, overload +from typing import Any, Final, Generic, TypeVar, overload from typing_extensions import TypeVarTuple, Unpack from weakref import ref @@ -19,17 +19,18 @@ _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: ... def _python_exit() -> None: ... -EXTRA_QUEUED_CALLS: int +EXTRA_QUEUED_CALLS: Final = 1 -_MAX_WINDOWS_WORKERS: int +_MAX_WINDOWS_WORKERS: Final = 61 class _RemoteTraceback(Exception): tb: str @@ -71,21 +72,27 @@ class _CallItem: class _SafeQueue(Queue[Future[Any]]): pending_work_items: dict[int, _WorkItem[Any]] - shutdown_lock: Lock + if sys.version_info < (3, 12): + shutdown_lock: Lock thread_wakeup: _ThreadWakeup - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 12): def __init__( self, max_size: int | None = 0, *, ctx: BaseContext, pending_work_items: dict[int, _WorkItem[Any]], - shutdown_lock: Lock, thread_wakeup: _ThreadWakeup, ) -> None: ... else: def __init__( - self, max_size: int | None = 0, *, ctx: BaseContext, pending_work_items: dict[int, _WorkItem[Any]] + self, + max_size: int | None = 0, + *, + ctx: BaseContext, + pending_work_items: dict[int, _WorkItem[Any]], + shutdown_lock: Lock, + thread_wakeup: _ThreadWakeup, ) -> None: ... def _on_queue_feeder_error(self, e: Exception, obj: _CallItem) -> None: ... @@ -124,27 +131,26 @@ else: initargs: tuple[Unpack[_Ts]], ) -> None: ... -if sys.version_info >= (3, 9): - class _ExecutorManagerThread(Thread): - thread_wakeup: _ThreadWakeup - shutdown_lock: Lock - executor_reference: ref[Any] - processes: MutableMapping[int, Process] - call_queue: Queue[_CallItem] - result_queue: SimpleQueue[_ResultItem] - work_ids_queue: Queue[int] - pending_work_items: dict[int, _WorkItem[Any]] - def __init__(self, executor: ProcessPoolExecutor) -> None: ... - def run(self) -> None: ... - def add_call_item_to_queue(self) -> None: ... - def wait_result_broken_or_wakeup(self) -> tuple[Any, bool, str]: ... - def process_result_item(self, result_item: int | _ResultItem) -> None: ... - def is_shutting_down(self) -> bool: ... - def terminate_broken(self, cause: str) -> None: ... - def flag_executor_shutting_down(self) -> None: ... - def shutdown_workers(self) -> None: ... - def join_executor_internals(self) -> None: ... - def get_n_children_alive(self) -> int: ... +class _ExecutorManagerThread(Thread): + thread_wakeup: _ThreadWakeup + shutdown_lock: Lock + executor_reference: ref[Any] + processes: MutableMapping[int, Process] + call_queue: Queue[_CallItem] + result_queue: SimpleQueue[_ResultItem] + work_ids_queue: Queue[int] + pending_work_items: dict[int, _WorkItem[Any]] + def __init__(self, executor: ProcessPoolExecutor) -> None: ... + def run(self) -> None: ... + def add_call_item_to_queue(self) -> None: ... + def wait_result_broken_or_wakeup(self) -> tuple[Any, bool, str]: ... + def process_result_item(self, result_item: int | _ResultItem) -> None: ... + def is_shutting_down(self) -> bool: ... + def terminate_broken(self, cause: str) -> None: ... + def flag_executor_shutting_down(self) -> None: ... + def shutdown_workers(self) -> None: ... + def join_executor_internals(self) -> None: ... + def get_n_children_alive(self) -> int: ... _system_limits_checked: bool _system_limited: bool | None @@ -227,7 +233,10 @@ class ProcessPoolExecutor(Executor): initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]], ) -> None: ... - if sys.version_info >= (3, 9): - def _start_executor_manager_thread(self) -> None: ... + def _start_executor_manager_thread(self) -> None: ... def _adjust_process_count(self) -> None: ... + + if sys.version_info >= (3, 14): + def kill_workers(self) -> None: ... + def terminate_workers(self) -> None: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi index d1b7858eae026..50a6a9c6f43ea 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi @@ -2,8 +2,9 @@ import queue import sys from collections.abc import Callable, Iterable, Mapping, Set as AbstractSet from threading import Lock, Semaphore, Thread -from typing import Any, Generic, TypeVar, overload -from typing_extensions import TypeVarTuple, Unpack +from types import GenericAlias +from typing import Any, Generic, Protocol, TypeVar, overload, type_check_only +from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack from weakref import ref from ._base import BrokenExecutor, Executor, Future @@ -16,31 +17,73 @@ _global_shutdown_lock: Lock def _python_exit() -> None: ... -if sys.version_info >= (3, 9): - from types import GenericAlias - _S = TypeVar("_S") -class _WorkItem(Generic[_S]): - future: Future[_S] - fn: Callable[..., _S] - args: Iterable[Any] - kwargs: Mapping[str, Any] - def __init__(self, future: Future[_S], fn: Callable[..., _S], args: Iterable[Any], kwargs: Mapping[str, Any]) -> None: ... - def run(self) -> None: ... - if sys.version_info >= (3, 9): +_Task: TypeAlias = tuple[Callable[..., Any], tuple[Any, ...], dict[str, Any]] + +_C = TypeVar("_C", bound=Callable[..., object]) +_KT = TypeVar("_KT", bound=str) +_VT = TypeVar("_VT") + +@type_check_only +class _ResolveTaskFunc(Protocol): + def __call__( + self, func: _C, args: tuple[Unpack[_Ts]], kwargs: dict[_KT, _VT] + ) -> tuple[_C, tuple[Unpack[_Ts]], dict[_KT, _VT]]: ... + +if sys.version_info >= (3, 14): + class WorkerContext: + @overload + @classmethod + def prepare( + cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] + ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ... + @overload + @classmethod + def prepare( + cls, initializer: Callable[[], object], initargs: tuple[()] + ) -> tuple[Callable[[], Self], _ResolveTaskFunc]: ... + @overload + def __init__(self, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]]) -> None: ... + @overload + def __init__(self, initializer: Callable[[], object], initargs: tuple[()]) -> None: ... + def initialize(self) -> None: ... + def finalize(self) -> None: ... + def run(self, task: _Task) -> None: ... + +if sys.version_info >= (3, 14): + class _WorkItem(Generic[_S]): + future: Future[Any] + task: _Task + def __init__(self, future: Future[Any], task: _Task) -> None: ... + def run(self, ctx: WorkerContext) -> None: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + + def _worker(executor_reference: ref[Any], ctx: WorkerContext, work_queue: queue.SimpleQueue[Any]) -> None: ... + +else: + class _WorkItem(Generic[_S]): + future: Future[_S] + fn: Callable[..., _S] + args: Iterable[Any] + kwargs: Mapping[str, Any] + def __init__(self, future: Future[_S], fn: Callable[..., _S], args: Iterable[Any], kwargs: Mapping[str, Any]) -> None: ... + def run(self) -> None: ... def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -def _worker( - executor_reference: ref[Any], - work_queue: queue.SimpleQueue[Any], - initializer: Callable[[Unpack[_Ts]], object], - initargs: tuple[Unpack[_Ts]], -) -> None: ... + def _worker( + executor_reference: ref[Any], + work_queue: queue.SimpleQueue[Any], + initializer: Callable[[Unpack[_Ts]], object], + initargs: tuple[Unpack[_Ts]], + ) -> None: ... class BrokenThreadPool(BrokenExecutor): ... class ThreadPoolExecutor(Executor): + if sys.version_info >= (3, 14): + BROKEN: type[BrokenThreadPool] + _max_workers: int _idle_semaphore: Semaphore _threads: AbstractSet[Thread] @@ -48,9 +91,26 @@ class ThreadPoolExecutor(Executor): _shutdown: bool _shutdown_lock: Lock _thread_name_prefix: str | None - _initializer: Callable[..., None] | None - _initargs: tuple[Any, ...] + if sys.version_info >= (3, 14): + _create_worker_context: Callable[[], WorkerContext] + _resolve_work_item_task: _ResolveTaskFunc + else: + _initializer: Callable[..., None] | None + _initargs: tuple[Any, ...] _work_queue: queue.SimpleQueue[_WorkItem[Any]] + + if sys.version_info >= (3, 14): + @overload + @classmethod + def prepare_context( + cls, initializer: Callable[[], object], initargs: tuple[()] + ) -> tuple[Callable[[], WorkerContext], _ResolveTaskFunc]: ... + @overload + @classmethod + def prepare_context( + cls, initializer: Callable[[Unpack[_Ts]], object], initargs: tuple[Unpack[_Ts]] + ) -> tuple[Callable[[], WorkerContext], _ResolveTaskFunc]: ... + @overload def __init__( self, diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi new file mode 100644 index 0000000000000..3839e6bef09b6 --- /dev/null +++ b/mypy/typeshed/stdlib/concurrent/interpreters/__init__.pyi @@ -0,0 +1,68 @@ +import sys +import threading +import types +from collections.abc import Callable +from typing import Any, Literal, TypeVar +from typing_extensions import ParamSpec, Self + +if sys.version_info >= (3, 13): # needed to satisfy pyright checks for Python <3.13 + from _interpreters import ( + InterpreterError as InterpreterError, + InterpreterNotFoundError as InterpreterNotFoundError, + NotShareableError as NotShareableError, + _SharedDict, + _Whence, + is_shareable as is_shareable, + ) + + from ._queues import Queue as Queue, QueueEmpty as QueueEmpty, QueueFull as QueueFull, create as create_queue + + __all__ = [ + "ExecutionFailed", + "Interpreter", + "InterpreterError", + "InterpreterNotFoundError", + "NotShareableError", + "Queue", + "QueueEmpty", + "QueueFull", + "create", + "create_queue", + "get_current", + "get_main", + "is_shareable", + "list_all", + ] + + _R = TypeVar("_R") + _P = ParamSpec("_P") + + class ExecutionFailed(InterpreterError): + excinfo: types.SimpleNamespace + + def __init__(self, excinfo: types.SimpleNamespace) -> None: ... + + def create() -> Interpreter: ... + def list_all() -> list[Interpreter]: ... + def get_current() -> Interpreter: ... + def get_main() -> Interpreter: ... + + class Interpreter: + def __new__(cls, id: int, /, _whence: _Whence | None = None, _ownsref: bool | None = None) -> Self: ... + def __reduce__(self) -> tuple[type[Self], int]: ... + def __hash__(self) -> int: ... + def __del__(self) -> None: ... + @property + def id(self) -> int: ... + @property + def whence( + self, + ) -> Literal["unknown", "runtime init", "legacy C-API", "C-API", "cross-interpreter C-API", "_interpreters module"]: ... + def is_running(self) -> bool: ... + def close(self) -> None: ... + def prepare_main( + self, ns: _SharedDict | None = None, /, **kwargs: Any + ) -> None: ... # kwargs has same value restrictions as _SharedDict + def exec(self, code: str | types.CodeType | Callable[[], object], /) -> None: ... + def call(self, callable: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... + def call_in_thread(self, callable: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> threading.Thread: ... diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi new file mode 100644 index 0000000000000..7cf1ea34786ed --- /dev/null +++ b/mypy/typeshed/stdlib/concurrent/interpreters/_crossinterp.pyi @@ -0,0 +1,30 @@ +import sys +from collections.abc import Callable +from typing import Final, NewType +from typing_extensions import Never, Self, TypeAlias + +if sys.version_info >= (3, 13): # needed to satisfy pyright checks for Python <3.13 + from _interpqueues import _UnboundOp + + class ItemInterpreterDestroyed(Exception): ... + # Actually a descriptor that behaves similarly to classmethod but prevents + # access from instances. + classonly = classmethod + + class UnboundItem: + __slots__ = () + def __new__(cls) -> Never: ... + @classonly + def singleton(cls, kind: str, module: str, name: str = "UNBOUND") -> Self: ... + + # Sentinel types and alias that don't exist at runtime. + _UnboundErrorType = NewType("_UnboundErrorType", object) + _UnboundRemoveType = NewType("_UnboundRemoveType", object) + _AnyUnbound: TypeAlias = _UnboundErrorType | _UnboundRemoveType | UnboundItem + + UNBOUND_ERROR: Final[_UnboundErrorType] + UNBOUND_REMOVE: Final[_UnboundRemoveType] + UNBOUND: Final[UnboundItem] # analogous to UNBOUND_REPLACE in C + + def serialize_unbound(unbound: _AnyUnbound) -> tuple[_UnboundOp]: ... + def resolve_unbound(flag: _UnboundOp, exctype_destroyed: Callable[[str], BaseException]) -> UnboundItem: ... diff --git a/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi b/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi new file mode 100644 index 0000000000000..bdf08d93d1e00 --- /dev/null +++ b/mypy/typeshed/stdlib/concurrent/interpreters/_queues.pyi @@ -0,0 +1,74 @@ +import queue +import sys +from typing import Final, SupportsIndex +from typing_extensions import Self + +if sys.version_info >= (3, 13): # needed to satisfy pyright checks for Python <3.13 + from _interpqueues import QueueError as QueueError, QueueNotFoundError as QueueNotFoundError + + from . import _crossinterp + from ._crossinterp import UNBOUND_ERROR as UNBOUND_ERROR, UNBOUND_REMOVE as UNBOUND_REMOVE, UnboundItem, _AnyUnbound + + __all__ = [ + "UNBOUND", + "UNBOUND_ERROR", + "UNBOUND_REMOVE", + "ItemInterpreterDestroyed", + "Queue", + "QueueEmpty", + "QueueError", + "QueueFull", + "QueueNotFoundError", + "create", + "list_all", + ] + + class QueueEmpty(QueueError, queue.Empty): ... + class QueueFull(QueueError, queue.Full): ... + class ItemInterpreterDestroyed(QueueError, _crossinterp.ItemInterpreterDestroyed): ... + UNBOUND: Final[UnboundItem] + + def create(maxsize: int = 0, *, unbounditems: _AnyUnbound = ...) -> Queue: ... + def list_all() -> list[Queue]: ... + + class Queue: + def __new__(cls, id: int, /) -> Self: ... + def __del__(self) -> None: ... + def __hash__(self) -> int: ... + def __reduce__(self) -> tuple[type[Self], int]: ... + @property + def id(self) -> int: ... + @property + def unbounditems(self) -> _AnyUnbound: ... + @property + def maxsize(self) -> int: ... + def empty(self) -> bool: ... + def full(self) -> bool: ... + def qsize(self) -> int: ... + if sys.version_info >= (3, 14): + def put( + self, + obj: object, + block: bool = True, + timeout: SupportsIndex | None = None, + *, + unbounditems: _AnyUnbound | None = None, + _delay: float = 0.01, + ) -> None: ... + else: + def put( + self, + obj: object, + timeout: SupportsIndex | None = None, + *, + unbounditems: _AnyUnbound | None = None, + _delay: float = 0.01, + ) -> None: ... + + def put_nowait(self, obj: object, *, unbounditems: _AnyUnbound | None = None) -> None: ... + if sys.version_info >= (3, 14): + def get(self, block: bool = True, timeout: SupportsIndex | None = None, *, _delay: float = 0.01) -> object: ... + else: + def get(self, timeout: SupportsIndex | None = None, *, _delay: float = 0.01) -> object: ... + + def get_nowait(self) -> object: ... diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index ee5000196e0e7..1909d80e3d189 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -1,11 +1,37 @@ 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, Final, Literal, TypeVar, overload -from typing_extensions import TypeAlias +from typing import Any, ClassVar, Final, Literal, TypeVar, overload, type_check_only +from typing_extensions import TypeAlias, deprecated -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 14): + __all__ = ( + "NoSectionError", + "DuplicateOptionError", + "DuplicateSectionError", + "NoOptionError", + "InterpolationError", + "InterpolationDepthError", + "InterpolationMissingOptionError", + "InterpolationSyntaxError", + "ParsingError", + "MissingSectionHeaderError", + "MultilineContinuationError", + "UnnamedSectionDisabledError", + "InvalidWriteError", + "ConfigParser", + "RawConfigParser", + "Interpolation", + "BasicInterpolation", + "ExtendedInterpolation", + "SectionProxy", + "ConverterMapping", + "DEFAULTSECT", + "MAX_INTERPOLATION_DEPTH", + "UNNAMED_SECTION", + ) +elif sys.version_info >= (3, 13): __all__ = ( "NoSectionError", "DuplicateOptionError", @@ -77,6 +103,21 @@ else: "MAX_INTERPOLATION_DEPTH", ] +if sys.version_info >= (3, 13): + @type_check_only + class _UNNAMED_SECTION: ... + + UNNAMED_SECTION: _UNNAMED_SECTION + + _SectionName: TypeAlias = str | _UNNAMED_SECTION + # A list of sections can only include an unnamed section if the parser was initialized with + # allow_unnamed_section=True. Any prevents users from having to use explicit + # type checks if allow_unnamed_section is False (the default). + _SectionNameList: TypeAlias = list[Any] +else: + _SectionName: TypeAlias = str + _SectionNameList: TypeAlias = list[str] + _Section: TypeAlias = Mapping[str, str] _Parser: TypeAlias = MutableMapping[str, _Section] _ConverterCallback: TypeAlias = Callable[[str], Any] @@ -87,17 +128,20 @@ 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: ... - def before_set(self, parser: _Parser, section: str, option: str, value: str) -> str: ... - def before_read(self, parser: _Parser, section: str, option: str, value: str) -> str: ... - def before_write(self, parser: _Parser, section: str, option: str, value: str) -> str: ... + def before_get(self, parser: _Parser, section: _SectionName, option: str, value: str, defaults: _Section) -> str: ... + def before_set(self, parser: _Parser, section: _SectionName, option: str, value: str) -> str: ... + def before_read(self, parser: _Parser, section: _SectionName, option: str, value: str) -> str: ... + def before_write(self, parser: _Parser, section: _SectionName, option: str, value: str) -> str: ... class BasicInterpolation(Interpolation): ... class ExtendedInterpolation(Interpolation): ... if sys.version_info < (3, 13): + @deprecated( + "Deprecated since Python 3.2; removed in Python 3.13. Use `BasicInterpolation` or `ExtendedInterpolation` instead." + ) class LegacyInterpolation(Interpolation): - def before_get(self, parser: _Parser, section: str, option: str, value: str, vars: _Section) -> str: ... + def before_get(self, parser: _Parser, section: _SectionName, option: str, value: str, vars: _Section) -> str: ... class RawConfigParser(_Parser): _SECT_TMPL: ClassVar[str] # undocumented @@ -214,46 +258,47 @@ class RawConfigParser(_Parser): ) -> None: ... def __len__(self) -> int: ... - def __getitem__(self, key: str) -> SectionProxy: ... - def __setitem__(self, key: str, value: _Section) -> None: ... - def __delitem__(self, key: str) -> None: ... + def __getitem__(self, key: _SectionName) -> SectionProxy: ... + def __setitem__(self, key: _SectionName, value: _Section) -> None: ... + def __delitem__(self, key: _SectionName) -> None: ... def __iter__(self) -> Iterator[str]: ... def __contains__(self, key: object) -> bool: ... def defaults(self) -> _Section: ... - def sections(self) -> list[str]: ... - def add_section(self, section: str) -> None: ... - def has_section(self, section: str) -> bool: ... - def options(self, section: str) -> list[str]: ... - def has_option(self, section: str, option: str) -> bool: ... + def sections(self) -> _SectionNameList: ... + def add_section(self, section: _SectionName) -> None: ... + def has_section(self, section: _SectionName) -> bool: ... + def options(self, section: _SectionName) -> list[str]: ... + def has_option(self, section: _SectionName, option: str) -> bool: ... def read(self, filenames: StrOrBytesPath | Iterable[StrOrBytesPath], encoding: str | None = None) -> list[str]: ... def read_file(self, f: Iterable[str], source: str | None = None) -> None: ... def read_string(self, string: str, source: str = "") -> None: ... def read_dict(self, dictionary: Mapping[str, Mapping[str, Any]], source: str = "") -> None: ... if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `parser.read_file()` instead.") def readfp(self, fp: Iterable[str], filename: str | None = None) -> None: ... # These get* methods are partially applied (with the same names) in # SectionProxy; the stubs should be kept updated together @overload - def getint(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> int: ... + def getint(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> int: ... @overload def getint( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... ) -> int | _T: ... @overload - def getfloat(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> float: ... + def getfloat(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> float: ... @overload def getfloat( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... ) -> float | _T: ... @overload - def getboolean(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> bool: ... + def getboolean(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> bool: ... @overload def getboolean( - self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T = ... ) -> bool | _T: ... def _get_conv( self, - section: str, + section: _SectionName, option: str, conv: Callable[[str], _T], *, @@ -263,30 +308,35 @@ 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: _SectionName, 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: ... + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T + ) -> str | _T | MaybeNone: ... @overload def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ... @overload - def items(self, section: str, raw: bool = False, vars: _Section | None = None) -> list[tuple[str, str]]: ... - def set(self, section: str, option: str, value: str | None = None) -> None: ... + def items(self, section: _SectionName, raw: bool = False, vars: _Section | None = None) -> list[tuple[str, str]]: ... + def set(self, section: _SectionName, option: str, value: str | None = None) -> None: ... def write(self, fp: SupportsWrite[str], space_around_delimiters: bool = True) -> None: ... - def remove_option(self, section: str, option: str) -> bool: ... - def remove_section(self, section: str) -> bool: ... + def remove_option(self, section: _SectionName, option: str) -> bool: ... + def remove_section(self, section: _SectionName) -> 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 @overload # type: ignore[override] - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str: ... + def get(self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None) -> str: ... @overload - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T) -> str | _T: ... + def get( + self, section: _SectionName, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T + ) -> str | _T: ... if sys.version_info < (3, 12): - class SafeConfigParser(ConfigParser): ... # deprecated alias + @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `ConfigParser` instead.") + class SafeConfigParser(ConfigParser): ... class SectionProxy(MutableMapping[str, str]): def __init__(self, parser: RawConfigParser, name: str) -> None: ... @@ -300,30 +350,43 @@ 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, + fallback: None = None, + *, + raw: bool = False, + vars: _Section | None = None, + _impl: Any | None = None, + **kwargs: Any, # passed to the underlying parser's get() method + ) -> 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 + **kwargs: Any, # passed to the underlying parser's get() method + ) -> 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 = False, vars: _Section | None = None) -> int | None: ... @overload - def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ... + def getint(self, option: str, fallback: _T = ..., *, raw: bool = False, vars: _Section | None = None) -> int | _T: ... @overload - def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float: ... + def getfloat(self, option: str, *, raw: bool = False, vars: _Section | None = None) -> float | None: ... @overload - def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> float | _T: ... + def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = False, vars: _Section | None = None) -> float | _T: ... @overload - def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool: ... + def getboolean(self, option: str, *, raw: bool = False, vars: _Section | None = None) -> bool | None: ... @overload - def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> bool | _T: ... + def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = False, vars: _Section | None = None) -> bool | _T: ... # SectionProxy can have arbitrary attributes when custom converters are used def __getattr__(self, key: str) -> Callable[..., Any]: ... @@ -341,38 +404,38 @@ class Error(Exception): def __init__(self, msg: str = "") -> None: ... class NoSectionError(Error): - section: str - def __init__(self, section: str) -> None: ... + section: _SectionName + def __init__(self, section: _SectionName) -> None: ... class DuplicateSectionError(Error): - section: str + section: _SectionName source: str | None lineno: int | None - def __init__(self, section: str, source: str | None = None, lineno: int | None = None) -> None: ... + def __init__(self, section: _SectionName, source: str | None = None, lineno: int | None = None) -> None: ... class DuplicateOptionError(Error): - section: str + section: _SectionName option: str source: str | None lineno: int | None - def __init__(self, section: str, option: str, source: str | None = None, lineno: int | None = None) -> None: ... + def __init__(self, section: _SectionName, option: str, source: str | None = None, lineno: int | None = None) -> None: ... class NoOptionError(Error): - section: str + section: _SectionName option: str - def __init__(self, option: str, section: str) -> None: ... + def __init__(self, option: str, section: _SectionName) -> None: ... class InterpolationError(Error): - section: str + section: _SectionName option: str - def __init__(self, option: str, section: str, msg: str) -> None: ... + def __init__(self, option: str, section: _SectionName, msg: str) -> None: ... class InterpolationDepthError(InterpolationError): - def __init__(self, option: str, section: str, rawval: object) -> None: ... + def __init__(self, option: str, section: _SectionName, rawval: object) -> None: ... class InterpolationMissingOptionError(InterpolationError): reference: str - def __init__(self, option: str, section: str, rawval: object, reference: str) -> None: ... + def __init__(self, option: str, section: _SectionName, rawval: object, reference: str) -> None: ... class InterpolationSyntaxError(InterpolationError): ... @@ -385,20 +448,39 @@ class ParsingError(Error): elif sys.version_info >= (3, 12): def __init__(self, source: str) -> None: ... else: - def __init__(self, source: str | None = None, filename: str | None = None) -> None: ... + @overload + def __init__(self, source: str) -> None: ... + @overload + @deprecated("The `filename` parameter removed in Python 3.12. Use `source` instead.") + def __init__(self, source: None, filename: str | None) -> None: ... + @overload + @deprecated("The `filename` parameter removed in Python 3.12. Use `source` instead.") + def __init__(self, source: None = None, *, filename: str | None) -> None: ... def append(self, lineno: int, line: str) -> None: ... + if sys.version_info < (3, 12): + @property + @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `source` instead.") + def filename(self) -> str: ... + @filename.setter + @deprecated("Deprecated since Python 3.2; removed in Python 3.12. Use `source` instead.") + def filename(self, value: str) -> None: ... + class MissingSectionHeaderError(ParsingError): lineno: int line: str def __init__(self, filename: str, lineno: int, line: str) -> None: ... if sys.version_info >= (3, 13): - class _UNNAMED_SECTION: ... - UNNAMED_SECTION: _UNNAMED_SECTION - class MultilineContinuationError(ParsingError): lineno: int line: str def __init__(self, filename: str, lineno: int, line: str) -> None: ... + +if sys.version_info >= (3, 14): + class UnnamedSectionDisabledError(Error): + msg: Final = "Support for UNNAMED_SECTION is disabled." + def __init__(self) -> None: ... + + class InvalidWriteError(Error): ... diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index daf218d5a138d..221102ee23956 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -1,10 +1,10 @@ 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 +from typing import Any, Generic, Protocol, TypeVar, overload, runtime_checkable, type_check_only from typing_extensions import ParamSpec, Self, TypeAlias __all__ = [ @@ -30,24 +30,35 @@ if sys.version_info >= (3, 11): _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) -_T_io = TypeVar("_T_io", bound=IO[str] | None) _ExitT_co = TypeVar("_ExitT_co", covariant=True, bound=bool | None, default=bool | None) _F = TypeVar("_F", bound=Callable[..., Any]) +_G_co = TypeVar("_G_co", bound=Generator[Any, Any, Any] | AsyncGenerator[Any, Any], covariant=True) _P = ParamSpec("_P") +_SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) +_ReturnT_co = TypeVar("_ReturnT_co", covariant=True, default=None) + _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] + __slots__ = () 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] + __slots__ = () async def __aenter__(self) -> _T_co: ... @abstractmethod async def __aexit__( @@ -58,22 +69,22 @@ class ContextDecorator: def _recreate_cm(self) -> Self: ... def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManager(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 - 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]] +class _GeneratorContextManagerBase(Generic[_G_co]): + # Ideally this would use ParamSpec, but that requires (*args, **kwargs), which this isn't. see #6676 + def __init__(self, func: Callable[..., _G_co], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... + gen: _G_co + func: Callable[..., _G_co] args: tuple[Any, ...] kwds: dict[str, Any] - if sys.version_info >= (3, 9): - def __exit__( - self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None - ) -> bool | None: ... - else: - def __exit__( - self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None - ) -> bool | None: ... + +class _GeneratorContextManager( + _GeneratorContextManagerBase[Generator[_T_co, _SendT_contra, _ReturnT_co]], + AbstractContextManager[_T_co, bool | None], + ContextDecorator, +): + def __exit__( + self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None + ) -> bool | None: ... def contextmanager(func: Callable[_P, Iterator[_T_co]]) -> Callable[_P, _GeneratorContextManager[_T_co]]: ... @@ -84,31 +95,25 @@ if sys.version_info >= (3, 10): def _recreate_cm(self) -> Self: ... def __call__(self, func: _AF) -> _AF: ... - class _AsyncGeneratorContextManager(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) - 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]] - args: tuple[Any, ...] - kwds: dict[str, Any] + class _AsyncGeneratorContextManager( + _GeneratorContextManagerBase[AsyncGenerator[_T_co, _SendT_contra]], + AbstractAsyncContextManager[_T_co, bool | None], + AsyncContextDecorator, + ): async def __aexit__( self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> bool | None: ... else: - class _AsyncGeneratorContextManager(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]] - args: tuple[Any, ...] - kwds: dict[str, Any] + class _AsyncGeneratorContextManager( + _GeneratorContextManagerBase[AsyncGenerator[_T_co, _SendT_contra]], AbstractAsyncContextManager[_T_co, bool | None] + ): async def __aexit__( self, typ: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> bool | None: ... def asynccontextmanager(func: Callable[_P, AsyncIterator[_T_co]]) -> Callable[_P, _AsyncGeneratorContextManager[_T_co]]: ... - +@type_check_only class _SupportsClose(Protocol): def close(self) -> object: ... @@ -119,6 +124,7 @@ class closing(AbstractContextManager[_SupportsCloseT, None]): def __exit__(self, *exc_info: Unused) -> None: ... if sys.version_info >= (3, 10): + @type_check_only class _SupportsAclose(Protocol): def aclose(self) -> Awaitable[object]: ... @@ -134,22 +140,34 @@ class suppress(AbstractContextManager[None, bool]): self, exctype: type[BaseException] | None, excinst: BaseException | None, exctb: TracebackType | None ) -> bool: ... -class _RedirectStream(AbstractContextManager[_T_io, None]): - def __init__(self, new_target: _T_io) -> None: ... +# This is trying to describe what is needed for (most?) uses +# of `redirect_stdout` and `redirect_stderr`. +# https://github.com/python/typeshed/issues/14903 +@type_check_only +class _SupportsRedirect(Protocol): + def write(self, s: str, /) -> int: ... + def flush(self) -> None: ... + +_SupportsRedirectT = TypeVar("_SupportsRedirectT", bound=_SupportsRedirect | None) + +class _RedirectStream(AbstractContextManager[_SupportsRedirectT, None]): + def __init__(self, new_target: _SupportsRedirectT) -> None: ... def __exit__( self, exctype: type[BaseException] | None, excinst: BaseException | None, exctb: TracebackType | None ) -> None: ... -class redirect_stdout(_RedirectStream[_T_io]): ... -class redirect_stderr(_RedirectStream[_T_io]): ... +class redirect_stdout(_RedirectStream[_SupportsRedirectT]): ... +class redirect_stderr(_RedirectStream[_SupportsRedirectT]): ... -# 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__( @@ -163,21 +181,17 @@ _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__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / - ) -> bool: ... + ) -> _ExitT_co: ... if sys.version_info >= (3, 10): class nullcontext(AbstractContextManager[_T, None], AbstractAsyncContextManager[_T, None]): 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 2cceec6a22509..373899ea2635f 100644 --- a/mypy/typeshed/stdlib/copy.pyi +++ b/mypy/typeshed/stdlib/copy.pyi @@ -1,15 +1,15 @@ import sys -from typing import Any, Protocol, TypeVar -from typing_extensions import Self +from typing import Any, Protocol, TypeVar, type_check_only __all__ = ["Error", "copy", "deepcopy"] _T = TypeVar("_T") -_SR = TypeVar("_SR", bound=_SupportsReplace) +_RT_co = TypeVar("_RT_co", covariant=True) -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: ... +@type_check_only +class _SupportsReplace(Protocol[_RT_co]): + # In reality doesn't support args, but there's no great way to express this. + def __replace__(self, /, *_: Any, **changes: Any) -> _RT_co: ... # None in CPython but non-None in Jython PyStringMap: Any @@ -20,7 +20,8 @@ def copy(x: _T) -> _T: ... if sys.version_info >= (3, 13): __all__ += ["replace"] - def replace(obj: _SR, /, **changes: Any) -> _SR: ... + # The types accepted by `**changes` match those of `obj.__replace__`. + def replace(obj: _SupportsReplace[_RT_co], /, **changes: Any) -> _RT_co: ... class Error(Exception): ... diff --git a/mypy/typeshed/stdlib/crypt.pyi b/mypy/typeshed/stdlib/crypt.pyi index 2940038592861..f926321969897 100644 --- a/mypy/typeshed/stdlib/crypt.pyi +++ b/mypy/typeshed/stdlib/crypt.pyi @@ -1,8 +1,21 @@ import sys -from typing import Final +from typing import Final, NamedTuple, type_check_only +from typing_extensions import disjoint_base if sys.platform != "win32": - class _Method: ... + @type_check_only + class _MethodBase(NamedTuple): + name: str + ident: str | None + salt_chars: int + total_size: int + + if sys.version_info >= (3, 12): + class _Method(_MethodBase): ... + else: + @disjoint_base + class _Method(_MethodBase): ... + METHOD_CRYPT: Final[_Method] METHOD_MD5: Final[_Method] METHOD_SHA256: Final[_Method] diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 24f0db3321653..4ed0ab1d83b82 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,15 +19,17 @@ 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 +from types import GenericAlias from typing import Any, Generic, Literal, TypeVar, overload from typing_extensions import Self -if sys.version_info >= (3, 12): - from types import GenericAlias - __all__ = [ "QUOTE_MINIMAL", "QUOTE_ALL", @@ -61,6 +58,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 +77,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 +125,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 40a073d107c76..19bd261c67e06 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -1,6 +1,5 @@ import sys from _ctypes import ( - POINTER as POINTER, RTLD_GLOBAL as RTLD_GLOBAL, RTLD_LOCAL as RTLD_LOCAL, Array as Array, @@ -10,51 +9,82 @@ from _ctypes import ( _CanCastTo as _CanCastTo, _CArgObject as _CArgObject, _CData as _CData, - _CDataMeta as _CDataMeta, + _CDataType as _CDataType, _CField as _CField, + _CTypeBaseType, _Pointer as _Pointer, _PointerLike as _PointerLike, _SimpleCData as _SimpleCData, - _StructUnionBase as _StructUnionBase, - _StructUnionMeta as _StructUnionMeta, addressof as addressof, alignment as alignment, byref as byref, get_errno as get_errno, - pointer as pointer, resize as resize, set_errno as set_errno, sizeof as sizeof, ) +from _typeshed import StrPath, SupportsBool, SupportsLen from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure -from typing import Any, ClassVar, Generic, TypeVar -from typing_extensions import TypeAlias +from types import GenericAlias +from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload, type_check_only +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 + if sys.version_info >= (3, 14): + from _ctypes import COMError as COMError, CopyComPointer as CopyComPointer + if sys.version_info >= (3, 11): from ctypes._endian import BigEndianUnion as BigEndianUnion, LittleEndianUnion as LittleEndianUnion -if sys.version_info >= (3, 9): - from types import GenericAlias - -_T = TypeVar("_T") +_CT = TypeVar("_CT", bound=_CData) +_T = TypeVar("_T", default=Any) _DLLT = TypeVar("_DLLT", bound=CDLL) -DEFAULT_MODE: int +if sys.version_info >= (3, 14): + @overload + @deprecated("ctypes.POINTER with string") + def POINTER(cls: str) -> type[Any]: ... + @overload + def POINTER(cls: None) -> type[c_void_p]: ... + @overload + def POINTER(cls: type[_CT]) -> type[_Pointer[_CT]]: ... + def pointer(obj: _CT) -> _Pointer[_CT]: ... + +else: + from _ctypes import POINTER as POINTER, pointer as pointer + +DEFAULT_MODE: Final[int] class ArgumentError(Exception): ... +# defined within CDLL.__init__ +# Runtime name is ctypes.CDLL.__init__.._FuncPtr +@type_check_only +class _CDLLFuncPointer(_CFuncPtr): + _flags_: ClassVar[int] + _restype_: ClassVar[type[_CDataType]] + +# Not a real class; _CDLLFuncPointer with a __name__ set on it. +@type_check_only +class _NamedFuncPointer(_CDLLFuncPointer): + __name__: str + +if sys.version_info >= (3, 12): + _NameTypes: TypeAlias = StrPath | None +else: + _NameTypes: TypeAlias = str | None + class CDLL: _func_flags_: ClassVar[int] - _func_restype_: ClassVar[_CData] + _func_restype_: ClassVar[type[_CDataType]] _name: str _handle: int - _FuncPtr: type[_FuncPointer] + _FuncPtr: type[_CDLLFuncPointer] def __init__( self, - name: str | None, + name: _NameTypes, mode: int = ..., handle: int | None = None, use_errno: bool = False, @@ -75,8 +105,7 @@ class LibraryLoader(Generic[_DLLT]): def __getattr__(self, name: str) -> _DLLT: ... def __getitem__(self, name: str) -> _DLLT: ... def LoadLibrary(self, name: str) -> _DLLT: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... cdll: LibraryLoader[CDLL] if sys.platform == "win32": @@ -85,21 +114,36 @@ if sys.platform == "win32": pydll: LibraryLoader[PyDLL] pythonapi: PyDLL -class _FuncPointer(_CFuncPtr): ... +# Class definition within CFUNCTYPE / WINFUNCTYPE / PYFUNCTYPE +# Names at runtime are +# ctypes.CFUNCTYPE..CFunctionType +# ctypes.WINFUNCTYPE..WinFunctionType +# ctypes.PYFUNCTYPE..CFunctionType +@type_check_only +class _CFunctionType(_CFuncPtr): + _argtypes_: ClassVar[list[type[_CData | _CDataType]]] + _restype_: ClassVar[type[_CData | _CDataType] | None] + _flags_: ClassVar[int] -class _NamedFuncPointer(_FuncPointer): - __name__: str +# Alias for either function pointer type +_FuncPointer: TypeAlias = _CDLLFuncPointer | _CFunctionType # noqa: Y047 # not used here def CFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... -) -> type[_FuncPointer]: ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = False, + use_last_error: bool = False, +) -> type[_CFunctionType]: ... if sys.platform == "win32": def WINFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... - ) -> type[_FuncPointer]: ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = False, + use_last_error: bool = False, + ) -> type[_CFunctionType]: ... -def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ... +def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_CFunctionType]: ... # 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,81 +156,177 @@ _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]: ... +if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def SetPointerType(pointer: type[_Pointer[Any]], cls: _CTypeBaseType) -> None: ... + +else: + def SetPointerType(pointer: type[_Pointer[Any]], cls: _CTypeBaseType) -> None: ... + +def ARRAY(typ: _CT, len: int) -> Array[_CT]: ... # Soft Deprecated, no plans to remove + if sys.platform == "win32": def DllCanUnloadNow() -> int: ... - def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ... # TODO not documented - def GetLastError() -> int: ... + def DllGetClassObject(rclsid: Any, riid: Any, ppv: Any) -> int: ... # TODO: not documented + + # Actually just an instance of _NamedFuncPointer (aka _CDLLFuncPointer), + # but we want to set a more specific __call__ + @type_check_only + class _GetLastErrorFunctionType(_NamedFuncPointer): + def __call__(self) -> int: ... + + GetLastError: _GetLastErrorFunctionType + +# Actually just an instance of _CFunctionType, but we want to set a more +# specific __call__. +@type_check_only +class _MemmoveFunctionType(_CFunctionType): + def __call__(self, dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... -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: ... +memmove: _MemmoveFunctionType + +# Actually just an instance of _CFunctionType, but we want to set a more +# specific __call__. +@type_check_only +class _MemsetFunctionType(_CFunctionType): + def __call__(self, dst: _CVoidPLike, c: int, count: int) -> int: ... + +memset: _MemsetFunctionType + +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: ... + +if sys.version_info >= (3, 14): + def memoryview_at(ptr: _CVoidConstPLike, size: int, readonly: bool = False) -> memoryview: ... + +class py_object(_CanCastTo, _SimpleCData[_T]): + _type_: ClassVar[Literal["O"]] + if sys.version_info >= (3, 14): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +class c_bool(_SimpleCData[bool]): + _type_: ClassVar[Literal["?"]] + def __init__(self, value: SupportsBool | SupportsLen | None = ...) -> None: ... + +class c_byte(_SimpleCData[int]): + _type_: ClassVar[Literal["b"]] + +class c_ubyte(_SimpleCData[int]): + _type_: ClassVar[Literal["B"]] + +class c_short(_SimpleCData[int]): + _type_: ClassVar[Literal["h"]] -class c_byte(_SimpleCData[int]): ... +class c_ushort(_SimpleCData[int]): + _type_: ClassVar[Literal["H"]] + +class c_long(_SimpleCData[int]): + _type_: ClassVar[Literal["l"]] + +class c_ulong(_SimpleCData[int]): + _type_: ClassVar[Literal["L"]] + +class c_int(_SimpleCData[int]): # can be an alias for c_long + _type_: ClassVar[Literal["i", "l"]] + +class c_uint(_SimpleCData[int]): # can be an alias for c_ulong + _type_: ClassVar[Literal["I", "L"]] + +class c_longlong(_SimpleCData[int]): # can be an alias for c_long + _type_: ClassVar[Literal["q", "l"]] + +class c_ulonglong(_SimpleCData[int]): # can be an alias for c_ulong + _type_: ClassVar[Literal["Q", "L"]] + +c_int8 = c_byte +c_uint8 = c_ubyte + +class c_int16(_SimpleCData[int]): # can be an alias for c_short or c_int + _type_: ClassVar[Literal["h", "i"]] + +class c_uint16(_SimpleCData[int]): # can be an alias for c_ushort or c_uint + _type_: ClassVar[Literal["H", "I"]] + +class c_int32(_SimpleCData[int]): # can be an alias for c_int or c_long + _type_: ClassVar[Literal["i", "l"]] + +class c_uint32(_SimpleCData[int]): # can be an alias for c_uint or c_ulong + _type_: ClassVar[Literal["I", "L"]] + +class c_int64(_SimpleCData[int]): # can be an alias for c_long or c_longlong + _type_: ClassVar[Literal["l", "q"]] + +class c_uint64(_SimpleCData[int]): # can be an alias for c_ulong or c_ulonglong + _type_: ClassVar[Literal["L", "Q"]] + +class c_ssize_t(_SimpleCData[int]): # alias for c_int, c_long, or c_longlong + _type_: ClassVar[Literal["i", "l", "q"]] + +class c_size_t(_SimpleCData[int]): # alias for c_uint, c_ulong, or c_ulonglong + _type_: ClassVar[Literal["I", "L", "Q"]] + +class c_float(_SimpleCData[float]): + _type_: ClassVar[Literal["f"]] + +class c_double(_SimpleCData[float]): + _type_: ClassVar[Literal["d"]] + +class c_longdouble(_SimpleCData[float]): # can be an alias for c_double + _type_: ClassVar[Literal["d", "g"]] + +if sys.version_info >= (3, 14) and sys.platform != "win32": + class c_double_complex(_SimpleCData[complex]): + _type_: ClassVar[Literal["D"]] + + class c_float_complex(_SimpleCData[complex]): + _type_: ClassVar[Literal["F"]] + + class c_longdouble_complex(_SimpleCData[complex]): + _type_: ClassVar[Literal["G"]] class c_char(_SimpleCData[bytes]): + _type_: ClassVar[Literal["c"]] def __init__(self, value: int | bytes | bytearray = ...) -> None: ... class c_char_p(_PointerLike, _SimpleCData[bytes | None]): + _type_: ClassVar[Literal["z"]] 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 -class c_float(_SimpleCData[float]): ... -class c_int(_SimpleCData[int]): ... # can be an alias for c_long -class c_long(_SimpleCData[int]): ... -class c_longlong(_SimpleCData[int]): ... # can be an alias for c_long -class c_short(_SimpleCData[int]): ... -class c_size_t(_SimpleCData[int]): ... # alias for c_uint, c_ulong, or c_ulonglong -class c_ssize_t(_SimpleCData[int]): ... # alias for c_int, c_long, or c_longlong -class c_ubyte(_SimpleCData[int]): ... -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_wchar(_SimpleCData[str]): ... - -c_int8 = c_byte - -# these are actually dynamic aliases for c_short, c_int, c_long, or c_longlong -class c_int16(_SimpleCData[int]): ... -class c_int32(_SimpleCData[int]): ... -class c_int64(_SimpleCData[int]): ... +class c_void_p(_PointerLike, _SimpleCData[int | None]): + _type_: ClassVar[Literal["P"]] + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... -c_uint8 = c_ubyte +c_voidp = c_void_p # backwards compatibility (to a bug) -# these are actually dynamic aliases for c_ushort, c_uint, c_ulong, or c_ulonglong -class c_uint16(_SimpleCData[int]): ... -class c_uint32(_SimpleCData[int]): ... -class c_uint64(_SimpleCData[int]): ... +class c_wchar(_SimpleCData[str]): + _type_: ClassVar[Literal["u"]] class c_wchar_p(_PointerLike, _SimpleCData[str | None]): + _type_: ClassVar[Literal["Z"]] def __init__(self, value: int | str | None = ...) -> None: ... - -class c_bool(_SimpleCData[bool]): - def __init__(self, value: bool = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... if sys.platform == "win32": - class HRESULT(_SimpleCData[int]): ... # TODO undocumented + class HRESULT(_SimpleCData[int]): # TODO: undocumented + _type_: ClassVar[Literal["l"]] 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]): ... + # At runtime, this is an alias for either c_int32 or c_int64, + # which are themselves an alias for one of c_int, c_long, or c_longlong + # This covers all our bases. + c_time_t: type[c_int32 | c_int64 | c_int | c_long | c_longlong] diff --git a/mypy/typeshed/stdlib/ctypes/_endian.pyi b/mypy/typeshed/stdlib/ctypes/_endian.pyi index 144f5ba5dd40f..97852f67aa6eb 100644 --- a/mypy/typeshed/stdlib/ctypes/_endian.pyi +++ b/mypy/typeshed/stdlib/ctypes/_endian.pyi @@ -3,10 +3,14 @@ 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. -class BigEndianStructure(Structure): ... +class BigEndianStructure(Structure): + __slots__ = () + class LittleEndianStructure(Structure): ... # Same thing for these: one is an alias of Union at runtime if sys.version_info >= (3, 11): - class BigEndianUnion(Union): ... + class BigEndianUnion(Union): + __slots__ = () + class LittleEndianUnion(Union): ... diff --git a/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi new file mode 100644 index 0000000000000..c5dd954660638 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi @@ -0,0 +1,3 @@ +from typing import Final + +__version__: Final[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..4f18c1d8db345 100644 --- a/mypy/typeshed/stdlib/ctypes/util.pyi +++ b/mypy/typeshed/stdlib/ctypes/util.pyi @@ -4,3 +4,8 @@ def find_library(name: str) -> str | None: ... if sys.platform == "win32": def find_msvcrt() -> str | None: ... + +if sys.version_info >= (3, 14): + def dllist() -> list[str]: ... + +def test() -> None: ... diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index 8847860f2002b..0f0d61a396d5f 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -1,10 +1,10 @@ +import sys +from _ctypes import _CArgObject, _CField from ctypes import ( Array, Structure, - _CField, _Pointer, _SimpleCData, - c_byte, c_char, c_char_p, c_double, @@ -21,10 +21,18 @@ from ctypes import ( c_wchar, c_wchar_p, ) -from typing import TypeVar -from typing_extensions import TypeAlias +from typing import Any, Final, TypeVar +from typing_extensions import Self, TypeAlias + +if sys.version_info >= (3, 12): + from ctypes import c_ubyte + + BYTE = c_ubyte +else: + from ctypes import c_byte + + BYTE = c_byte -BYTE = c_byte WORD = c_ushort DWORD = c_ulong CHAR = c_char @@ -75,6 +83,15 @@ HACCEL = HANDLE HBITMAP = HANDLE HBRUSH = HANDLE HCOLORSPACE = HANDLE +if sys.version_info >= (3, 14): + HCONV = HANDLE + HCONVLIST = HANDLE + HCURSOR = HANDLE + HDDEDATA = HANDLE + HDROP = HANDLE + HFILE = INT + HRESULT = LONG + HSZ = HANDLE HDC = HANDLE HDESK = HANDLE HDWP = HANDLE @@ -160,7 +177,7 @@ class MSG(Structure): pt: _CField[POINT, POINT, POINT] tagMSG = MSG -MAX_PATH: int +MAX_PATH: Final = 260 class WIN32_FIND_DATAA(Structure): dwFileAttributes: _CIntLikeField[DWORD] @@ -241,10 +258,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..3e32487ad99f2 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 @@ -12,11 +14,28 @@ _T = TypeVar("_T") _P = ParamSpec("_P") # available after calling `curses.initscr()` +# not `Final` as it can change during the terminal resize: LINES: int COLS: int # available after calling `curses.start_color()` -COLORS: int -COLOR_PAIRS: int +COLORS: Final[int] +COLOR_PAIRS: Final[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: ... + +# 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/ascii.pyi b/mypy/typeshed/stdlib/curses/ascii.pyi index 66efbe36a7df2..0234434b8c3de 100644 --- a/mypy/typeshed/stdlib/curses/ascii.pyi +++ b/mypy/typeshed/stdlib/curses/ascii.pyi @@ -1,45 +1,45 @@ -from typing import TypeVar +from typing import Final, TypeVar _CharT = TypeVar("_CharT", str, int) -NUL: int -SOH: int -STX: int -ETX: int -EOT: int -ENQ: int -ACK: int -BEL: int -BS: int -TAB: int -HT: int -LF: int -NL: int -VT: int -FF: int -CR: int -SO: int -SI: int -DLE: int -DC1: int -DC2: int -DC3: int -DC4: int -NAK: int -SYN: int -ETB: int -CAN: int -EM: int -SUB: int -ESC: int -FS: int -GS: int -RS: int -US: int -SP: int -DEL: int +NUL: Final = 0x00 +SOH: Final = 0x01 +STX: Final = 0x02 +ETX: Final = 0x03 +EOT: Final = 0x04 +ENQ: Final = 0x05 +ACK: Final = 0x06 +BEL: Final = 0x07 +BS: Final = 0x08 +TAB: Final = 0x09 +HT: Final = 0x09 +LF: Final = 0x0A +NL: Final = 0x0A +VT: Final = 0x0B +FF: Final = 0x0C +CR: Final = 0x0D +SO: Final = 0x0E +SI: Final = 0x0F +DLE: Final = 0x10 +DC1: Final = 0x11 +DC2: Final = 0x12 +DC3: Final = 0x13 +DC4: Final = 0x14 +NAK: Final = 0x15 +SYN: Final = 0x16 +ETB: Final = 0x17 +CAN: Final = 0x18 +EM: Final = 0x19 +SUB: Final = 0x1A +ESC: Final = 0x1B +FS: Final = 0x1C +GS: Final = 0x1D +RS: Final = 0x1E +US: Final = 0x1F +SP: Final = 0x20 +DEL: Final = 0x7F -controlnames: list[int] +controlnames: Final[list[int]] def isalnum(c: str | int) -> bool: ... def isalpha(c: str | int) -> bool: ... 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 3295b1c1f8356..3a1c8cb5d62dd 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -4,11 +4,9 @@ import types 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 Never, TypeAlias, TypeIs - -if sys.version_info >= (3, 9): - from types import GenericAlias +from types import GenericAlias +from typing import Any, Final, Generic, Literal, Protocol, TypeVar, overload, type_check_only +from typing_extensions import Never, TypeIs _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) @@ -33,6 +31,25 @@ if sys.version_info >= (3, 10): _DataclassT = TypeVar("_DataclassT", bound=DataclassInstance) +@type_check_only +class _DataclassFactory(Protocol): + def __call__( + self, + cls: type[_T], + /, + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + match_args: bool = True, + kw_only: bool = False, + slots: bool = False, + weakref_slot: bool = False, + ) -> type[_T]: ... + # define _MISSING_TYPE as an enum within the type stubs, # even though that is not really its type at runtime # this allows us to use Literal[_MISSING_TYPE.MISSING] @@ -41,7 +58,7 @@ _DataclassT = TypeVar("_DataclassT", bound=DataclassInstance) class _MISSING_TYPE(enum.Enum): MISSING = enum.auto() -MISSING = _MISSING_TYPE.MISSING +MISSING: Final = _MISSING_TYPE.MISSING if sys.version_info >= (3, 10): class KW_ONLY: ... @@ -54,14 +71,28 @@ def asdict(obj: DataclassInstance, *, dict_factory: Callable[[list[tuple[str, An def astuple(obj: DataclassInstance) -> tuple[Any, ...]: ... @overload def astuple(obj: DataclassInstance, *, tuple_factory: Callable[[list[Any]], _T]) -> _T: ... -@overload -def dataclass(cls: None, /) -> Callable[[type[_T]], type[_T]]: ... -@overload -def dataclass(cls: type[_T], /) -> type[_T]: ... if sys.version_info >= (3, 11): @overload def dataclass( + cls: type[_T], + /, + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + match_args: bool = True, + kw_only: bool = False, + slots: bool = False, + weakref_slot: bool = False, + ) -> type[_T]: ... + @overload + def dataclass( + cls: None = None, + /, *, init: bool = True, repr: bool = True, @@ -78,6 +109,23 @@ if sys.version_info >= (3, 11): elif sys.version_info >= (3, 10): @overload def dataclass( + cls: type[_T], + /, + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + match_args: bool = True, + kw_only: bool = False, + slots: bool = False, + ) -> type[_T]: ... + @overload + def dataclass( + cls: None = None, + /, *, init: bool = True, repr: bool = True, @@ -93,6 +141,20 @@ elif sys.version_info >= (3, 10): else: @overload def dataclass( + cls: type[_T], + /, + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + ) -> type[_T]: ... + @overload + def dataclass( + cls: None = None, + /, *, init: bool = True, repr: bool = True, @@ -103,10 +165,42 @@ else: ) -> Callable[[type[_T]], type[_T]]: ... # See https://github.com/python/mypy/issues/10750 +@type_check_only class _DefaultFactory(Protocol[_T_co]): def __call__(self) -> _T_co: ... class Field(Generic[_T]): + if sys.version_info >= (3, 14): + __slots__ = ( + "name", + "type", + "default", + "default_factory", + "repr", + "hash", + "init", + "compare", + "metadata", + "kw_only", + "doc", + "_field_type", + ) + elif sys.version_info >= (3, 10): + __slots__ = ( + "name", + "type", + "default", + "default_factory", + "repr", + "hash", + "init", + "compare", + "metadata", + "kw_only", + "_field_type", + ) + else: + __slots__ = ("name", "type", "default", "default_factory", "repr", "hash", "init", "compare", "metadata", "_field_type") name: str type: Type[_T] | str | Any default: _T | Literal[_MISSING_TYPE.MISSING] @@ -116,8 +210,27 @@ class Field(Generic[_T]): init: bool compare: bool metadata: types.MappingProxyType[Any, Any] + + if sys.version_info >= (3, 14): + doc: str | None + if sys.version_info >= (3, 10): kw_only: bool | Literal[_MISSING_TYPE.MISSING] + + if sys.version_info >= (3, 14): + def __init__( + self, + default: _T, + default_factory: Callable[[], _T], + init: bool, + repr: bool, + hash: bool | None, + compare: bool, + metadata: Mapping[Any, Any], + kw_only: bool, + doc: str | None, + ) -> None: ... + elif sys.version_info >= (3, 10): def __init__( self, default: _T, @@ -142,43 +255,87 @@ class Field(Generic[_T]): ) -> None: ... def __set_name__(self, owner: Type[Any], name: str) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # NOTE: Actual return type is 'Field[_T]', but we want to help type checkers # to understand the magic that happens at runtime. -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 14): @overload # `default` and `default_factory` are optional and mutually exclusive. def field( *, default: _T, + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + doc: str | None = None, ) -> _T: ... @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + doc: str | None = None, ) -> _T: ... @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + doc: str | None = None, + ) -> Any: ... + +elif sys.version_info >= (3, 10): + @overload # `default` and `default_factory` are optional and mutually exclusive. + def field( + *, + default: _T, + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., + init: bool = True, + repr: bool = True, + hash: bool | None = None, + compare: bool = True, + metadata: Mapping[Any, Any] | None = None, + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + ) -> _T: ... + @overload + def field( + *, + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Callable[[], _T], + init: bool = True, + repr: bool = True, + hash: bool | None = None, + compare: bool = True, + metadata: Mapping[Any, Any] | None = None, + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + ) -> _T: ... + @overload + def field( + *, + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., + init: bool = True, + repr: bool = True, + hash: bool | None = None, + compare: bool = True, + metadata: Mapping[Any, Any] | None = None, + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> Any: ... else: @@ -186,6 +343,7 @@ else: def field( *, default: _T, + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, @@ -195,6 +353,7 @@ else: @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, @@ -205,6 +364,8 @@ else: @overload def field( *, + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, @@ -224,24 +385,37 @@ def is_dataclass(obj: object) -> TypeIs[DataclassInstance | type[DataclassInstan class FrozenInstanceError(AttributeError): ... -if sys.version_info >= (3, 9): - _InitVarMeta: TypeAlias = type -else: - class _InitVarMeta(type): - # Not used, instead `InitVar.__class_getitem__` is called. - # 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): +class InitVar(Generic[_T]): + __slots__ = ("type",) 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[reportInvalidTypeForm] - @overload - def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm] + @overload + def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... # pyright: ignore[reportInvalidTypeForm] + @overload + def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm] + +if sys.version_info >= (3, 14): + def make_dataclass( + cls_name: str, + fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]], + *, + bases: tuple[type, ...] = (), + namespace: dict[str, Any] | None = None, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + match_args: bool = True, + kw_only: bool = False, + slots: bool = False, + weakref_slot: bool = False, + module: str | None = None, + decorator: _DataclassFactory = ..., + ) -> type: ... -if sys.version_info >= (3, 12): +elif sys.version_info >= (3, 12): def make_dataclass( cls_name: str, fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]], diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index e8a4efdc61f3a..8a0536c006d57 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -1,12 +1,12 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Final, 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, disjoint_base if sys.version_info >= (3, 11): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") -elif sys.version_info >= (3, 9): +else: __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR") MINYEAR: Final = 1 @@ -29,7 +29,7 @@ class timezone(tzinfo): utc: ClassVar[timezone] min: ClassVar[timezone] max: ClassVar[timezone] - def __init__(self, offset: timedelta, name: str = ...) -> None: ... + def __new__(cls, offset: timedelta, name: str = ...) -> Self: ... def tzname(self, dt: datetime | None, /) -> str: ... def utcoffset(self, dt: datetime | None, /) -> timedelta: ... def dst(self, dt: datetime | None, /) -> None: ... @@ -39,12 +39,19 @@ class timezone(tzinfo): 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: ... +@disjoint_base class date: min: ClassVar[date] max: ClassVar[date] @@ -67,6 +74,11 @@ class date: @property def day(self) -> int: ... def ctime(self) -> str: ... + + if sys.version_info >= (3, 14): + @classmethod + def strptime(cls, date_string: str, format: str, /) -> Self: ... + # On <3.12, the name of the parameter in the pure-Python implementation # didn't match the name in the C implementation, # meaning it is only *safe* to pass it as a keyword argument on 3.12+ @@ -99,24 +111,22 @@ class date: def __hash__(self) -> int: ... def weekday(self) -> int: ... def isoweekday(self) -> int: ... - if sys.version_info >= (3, 9): - def isocalendar(self) -> _IsoCalendarDate: ... - else: - def isocalendar(self) -> tuple[int, int, int]: ... + def isocalendar(self) -> _IsoCalendarDate: ... +@disjoint_base class time: min: ClassVar[time] max: ClassVar[time] resolution: ClassVar[timedelta] def __new__( cls, - hour: SupportsIndex = ..., - minute: SupportsIndex = ..., - second: SupportsIndex = ..., - microsecond: SupportsIndex = ..., - tzinfo: _TzInfo | None = ..., + hour: SupportsIndex = 0, + minute: SupportsIndex = 0, + second: SupportsIndex = 0, + microsecond: SupportsIndex = 0, + tzinfo: _TzInfo | None = None, *, - fold: int = ..., + fold: int = 0, ) -> Self: ... @property def hour(self) -> int: ... @@ -136,9 +146,14 @@ class time: def __gt__(self, value: time, /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... - def isoformat(self, timespec: str = ...) -> str: ... + def isoformat(self, timespec: str = "auto") -> str: ... @classmethod def fromisoformat(cls, time_string: str, /) -> Self: ... + + if sys.version_info >= (3, 14): + @classmethod + def strptime(cls, date_string: str, format: str, /) -> Self: ... + # On <3.12, the name of the parameter in the pure-Python implementation # didn't match the name in the C implementation, # meaning it is only *safe* to pass it as a keyword argument on 3.12+ @@ -178,19 +193,20 @@ class time: _Date: TypeAlias = date _Time: TypeAlias = time +@disjoint_base class timedelta: min: ClassVar[timedelta] max: ClassVar[timedelta] resolution: ClassVar[timedelta] def __new__( cls, - days: float = ..., - seconds: float = ..., - microseconds: float = ..., - milliseconds: float = ..., - minutes: float = ..., - hours: float = ..., - weeks: float = ..., + days: float = 0, + seconds: float = 0, + microseconds: float = 0, + milliseconds: float = 0, + minutes: float = 0, + hours: float = 0, + weeks: float = 0, ) -> Self: ... @property def days(self) -> int: ... @@ -226,6 +242,7 @@ class timedelta: def __bool__(self) -> bool: ... def __hash__(self) -> int: ... +@disjoint_base class datetime(date): min: ClassVar[datetime] max: ClassVar[datetime] @@ -234,13 +251,13 @@ class datetime(date): year: SupportsIndex, month: SupportsIndex, day: SupportsIndex, - hour: SupportsIndex = ..., - minute: SupportsIndex = ..., - second: SupportsIndex = ..., - microsecond: SupportsIndex = ..., - tzinfo: _TzInfo | None = ..., + hour: SupportsIndex = 0, + minute: SupportsIndex = 0, + second: SupportsIndex = 0, + microsecond: SupportsIndex = 0, + tzinfo: _TzInfo | None = None, *, - fold: int = ..., + fold: int = 0, ) -> Self: ... @property def hour(self) -> int: ... @@ -259,10 +276,10 @@ class datetime(date): # meaning it is only *safe* to pass it as a keyword argument on 3.12+ if sys.version_info >= (3, 12): @classmethod - def fromtimestamp(cls, timestamp: float, tz: _TzInfo | None = ...) -> Self: ... + def fromtimestamp(cls, timestamp: float, tz: _TzInfo | None = None) -> Self: ... else: @classmethod - def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = ...) -> Self: ... + def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = None) -> Self: ... @classmethod @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.timezone.utc)") @@ -308,8 +325,8 @@ class datetime(date): *, fold: int = ..., ) -> Self: ... - def astimezone(self, tz: _TzInfo | None = ...) -> Self: ... - def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ... + def astimezone(self, tz: _TzInfo | None = None) -> Self: ... + def isoformat(self, sep: str = "T", timespec: str = "auto") -> str: ... @classmethod def strptime(cls, date_string: str, format: str, /) -> Self: ... def utcoffset(self) -> timedelta | None: ... @@ -325,3 +342,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..7cbb63cf2f06e 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"] @@ -76,6 +76,7 @@ _TFlags: TypeAlias = Literal[ "nusf", ] +@type_check_only class _Database(MutableMapping[_KeyType, bytes]): def close(self) -> None: ... def __getitem__(self, key: _KeyType) -> bytes: ... @@ -89,6 +90,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 1d1d541f54770..2dac3d12b0ca4 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -1,47 +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: ... - 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: ... +from _gdbm import * diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 4113a7e3ffb9b..66c943ab640be 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -1,43 +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: ... - 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 __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 index 446a0cf155fa7..e2fba93b20017 100644 --- a/mypy/typeshed/stdlib/dbm/sqlite3.pyi +++ b/mypy/typeshed/stdlib/dbm/sqlite3.pyi @@ -26,4 +26,4 @@ class _Database(MutableMapping[bytes, bytes]): 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: ... +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..2e06c2d1b724a 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -1,2 +1,274 @@ -from _decimal import * -from _decimal import __libmpdec_version__ as __libmpdec_version__, __version__ as __version__ +import numbers +import sys +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, disjoint_base + +if sys.version_info >= (3, 14): + from _decimal import IEEE_CONTEXT_MAX_BITS as IEEE_CONTEXT_MAX_BITS, IEEEContext as IEEEContext + +_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): ... + +@disjoint_base +class Decimal: + def __new__(cls, value: _DecimalNew = "0", context: Context | None = None) -> Self: ... + if sys.version_info >= (3, 14): + @classmethod + def from_number(cls, number: Decimal | float, /) -> 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 = None, /) -> str: ... + +@disjoint_base +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 inexpressible 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 = None, + rounding: str | None = None, + Emin: int | None = None, + Emax: int | None = None, + capitals: int | None = None, + clamp: int | None = None, + flags: dict[_TrapType, bool] | Container[_TrapType] | None = None, + traps: dict[_TrapType, bool] | Container[_TrapType] | None = 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/difflib.pyi b/mypy/typeshed/stdlib/difflib.pyi index 50154d785c2f5..6efe68322bb65 100644 --- a/mypy/typeshed/stdlib/difflib.pyi +++ b/mypy/typeshed/stdlib/difflib.pyi @@ -1,10 +1,9 @@ +import re import sys from collections.abc import Callable, Iterable, Iterator, Sequence +from types import GenericAlias from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = [ "get_close_matches", "ndiff", @@ -43,19 +42,14 @@ class SequenceMatcher(Generic[_T]): def set_seqs(self, a: Sequence[_T], b: Sequence[_T]) -> None: ... def set_seq1(self, a: Sequence[_T]) -> None: ... def set_seq2(self, b: Sequence[_T]) -> None: ... - if sys.version_info >= (3, 9): - def find_longest_match(self, alo: int = 0, ahi: int | None = None, blo: int = 0, bhi: int | None = None) -> Match: ... - else: - def find_longest_match(self, alo: int, ahi: int, blo: int, bhi: int) -> Match: ... - + def find_longest_match(self, alo: int = 0, ahi: int | None = None, blo: int = 0, bhi: int | None = None) -> Match: ... def get_matching_blocks(self) -> list[Match]: ... def get_opcodes(self) -> list[tuple[Literal["replace", "delete", "insert", "equal"], int, int, int, int]]: ... def get_grouped_opcodes(self, n: int = 3) -> Iterable[list[tuple[str, int, int, int, int]]]: ... def ratio(self) -> float: ... def quick_ratio(self) -> float: ... def real_quick_ratio(self) -> float: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @overload def get_close_matches(word: AnyStr, possibilities: Iterable[AnyStr], n: int = 3, cutoff: float = 0.6) -> list[AnyStr]: ... @@ -68,7 +62,12 @@ class Differ: def __init__(self, linejunk: Callable[[str], bool] | None = None, charjunk: Callable[[str], bool] | None = None) -> None: ... def compare(self, a: Sequence[str], b: Sequence[str]) -> Iterator[str]: ... -def IS_LINE_JUNK(line: str, pat: Any = ...) -> bool: ... # pat is undocumented +if sys.version_info >= (3, 14): + def IS_LINE_JUNK(line: str, pat: Callable[[str], re.Match[str] | None] | None = None) -> bool: ... + +else: + def IS_LINE_JUNK(line: str, pat: Callable[[str], re.Match[str] | None] = ...) -> bool: ... + def IS_CHARACTER_JUNK(ch: str, ws: str = " \t") -> bool: ... # ws is undocumented def unified_diff( a: Sequence[str], diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index cb69eac89c920..896b50fa93847 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -2,8 +2,8 @@ import sys import types from collections.abc import Callable, Iterator from opcode import * # `dis` re-exports it as a part of public API -from typing import IO, Any, NamedTuple -from typing_extensions import Self, TypeAlias +from typing import IO, Any, Final, NamedTuple +from typing_extensions import Self, TypeAlias, disjoint_base __all__ = [ "code_info", @@ -88,29 +88,64 @@ else: starts_line: int | None is_jump_target: bool -class Instruction(_Instruction): - if sys.version_info < (3, 13): +if sys.version_info >= (3, 12): + class Instruction(_Instruction): + 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: ... + if sys.version_info >= (3, 14): + @staticmethod + def make( + opname: str, + arg: int | None, + argval: Any, + argrepr: str, + offset: int, + start_offset: int, + starts_line: bool, + line_number: int | None, + label: int | None = None, + positions: Positions | None = None, + cache_info: list[tuple[str, int, Any]] | None = None, + ) -> Instruction: ... + +else: + @disjoint_base + 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): - @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, 13): + if sys.version_info >= (3, 14): + show_positions: bool + # 3.14 added `show_positions` + 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, + show_positions: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 13): show_offsets: bool # 3.13 added `show_offsets` def __init__( @@ -149,14 +184,46 @@ class Bytecode: def info(self) -> str: ... def dis(self) -> str: ... -COMPILER_FLAG_NAMES: dict[int, str] +COMPILER_FLAG_NAMES: Final[dict[int, str]] def findlabels(code: _HaveCodeType) -> list[int]: ... 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, 13): +if sys.version_info >= (3, 14): + # 3.14 added `show_positions` + def dis( + x: _HaveCodeType | str | bytes | bytearray | None = None, + *, + file: IO[str] | None = None, + depth: int | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, + show_positions: 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, + show_positions: 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, + show_positions: bool = False, + ) -> None: ... + +elif sys.version_info >= (3, 13): # 3.13 added `show_offsets` def dis( x: _HaveCodeType | str | bytes | bytearray | None = None, @@ -184,10 +251,6 @@ if sys.version_info >= (3, 13): 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]: ... elif sys.version_info >= (3, 11): # 3.11 added `show_caches` and `adaptive` @@ -205,9 +268,6 @@ elif sys.version_info >= (3, 11): def distb( tb: types.TracebackType | None = None, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False ) -> None: ... - def get_instructions( - x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool = False, adaptive: bool = False - ) -> Iterator[Instruction]: ... else: def dis( @@ -215,6 +275,19 @@ else: ) -> None: ... def disassemble(co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None) -> None: ... def distb(tb: types.TracebackType | None = None, *, file: IO[str] | None = None) -> None: ... + +if sys.version_info >= (3, 13): + # 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]: ... + +elif sys.version_info >= (3, 11): + def get_instructions( + x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool = False, adaptive: bool = False + ) -> Iterator[Instruction]: ... + +else: def get_instructions(x: _HaveCodeType, *, first_line: int | None = None) -> Iterator[Instruction]: ... def show_code(co: _HaveCodeType, *, file: IO[str] | None = None) -> None: ... 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 e0f33f430e5a0..5bff209807eef 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -1,5 +1,5 @@ from _typeshed import BytesPath, StrPath, Unused -from collections.abc import Callable, Iterable +from collections.abc import Callable, Iterable, Sequence from distutils.file_util import _BytesPathT, _StrPathT from typing import Literal, overload from typing_extensions import TypeAlias, TypeVarTuple, Unpack @@ -63,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, @@ -165,7 +165,7 @@ class CCompiler: def execute( self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1 ) -> None: ... - def spawn(self, cmd: list[str]) -> None: ... + def 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 dcb423a49b090..7f97bc3a2c9e0 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -1,4 +1,4 @@ -from _typeshed import BytesPath, Incomplete, StrOrBytesPath, StrPath, Unused +from _typeshed import BytesPath, StrOrBytesPath, StrPath, Unused from abc import abstractmethod from collections.abc import Callable, Iterable from distutils.command.bdist import bdist @@ -30,7 +30,7 @@ _CommandT = TypeVar("_CommandT", bound=Command) _Ts = TypeVarTuple("_Ts") class Command: - dry_run: Literal[0, 1] # Exposed from __getattr_. Same as Distribution.dry_run + dry_run: bool | 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]]] @@ -226,4 +226,4 @@ class Command: level: Unused = 1, ) -> None: ... def ensure_finalized(self) -> None: ... - def dump_options(self, header: Incomplete | None = None, indent: str = "") -> None: ... + def dump_options(self, header=None, indent: str = "") -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist.pyi b/mypy/typeshed/stdlib/distutils/command/bdist.pyi index 43d77087f7d8a..6f996207077e0 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist.pyi @@ -1,6 +1,6 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable -from typing import Any, ClassVar +from typing import ClassVar from ..cmd import Command @@ -15,13 +15,13 @@ class bdist(Command): default_format: ClassVar[dict[str, str]] format_commands: ClassVar[list[str]] format_command: ClassVar[dict[str, tuple[str, str]]] - bdist_base: Any - plat_name: Any - formats: Any - dist_dir: Any + 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 19997882dd537..297a0c39ed430 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -7,15 +8,15 @@ class bdist_dumb(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] default_format: ClassVar[dict[str, str]] - bdist_dir: Any - plat_name: Any - format: Any + 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 d0eac1a3be5bb..d677f81d14251 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi @@ -1,42 +1,42 @@ import sys -from typing import Any, ClassVar, 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: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - all_versions: Any + all_versions: Incomplete other_version: str - if sys.version_info >= (3, 9): - def __init__(self, *args, **kw) -> None: ... - bdist_dir: Any - plat_name: Any + def __init__(self, *args, **kw) -> None: ... + 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 89c43e1b974cd..83b4161094c51 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -7,44 +8,44 @@ class bdist_rpm(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - bdist_base: Any - rpm_base: Any - dist_dir: Any - 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 + 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/build.pyi b/mypy/typeshed/stdlib/distutils/command/build.pyi index 78ba6b7042dc1..3ec0c9614d62a 100644 --- a/mypy/typeshed/stdlib/distutils/command/build.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build.pyi @@ -1,4 +1,4 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable from typing import Any, ClassVar @@ -12,17 +12,17 @@ class build(Command): 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 1f66e2efc20c7..69cfbe7120d8e 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi @@ -1,6 +1,6 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable -from typing import Any, ClassVar +from typing import ClassVar from ..cmd import Command @@ -11,15 +11,15 @@ class build_clib(Command): user_options: ClassVar[list[tuple[str, str, str]]] boolean_options: ClassVar[list[str]] help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] - build_clib: Any - build_temp: Any - libraries: Any - include_dirs: Any - define: Any - undef: Any - debug: Any + 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 a0813c314021d..c5a9b5d508f0d 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi @@ -1,40 +1,40 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable -from typing import Any, ClassVar +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 + 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: Any - build_lib: Any - plat_name: Any - build_temp: Any + 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 90f06751416a6..23ed230bb2d8c 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_py.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_py.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar, Literal +from _typeshed import Incomplete +from typing import ClassVar, Literal from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 @@ -8,17 +9,17 @@ class build_py(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - build_lib: Any - py_modules: Any - package: Any - package_data: Any - package_dir: Any + 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 7871bb8a57197..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, ClassVar +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: ClassVar[list[tuple[str, str, str]]] boolean_options: ClassVar[list[str]] - build_dir: Any - scripts: Any - force: Any - executable: Any - outfiles: Any + 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 e69627d20c7a0..2c807fd2c4396 100644 --- a/mypy/typeshed/stdlib/distutils/command/check.pyi +++ b/mypy/typeshed/stdlib/distutils/command/check.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete from typing import Any, ClassVar, Final, Literal from typing_extensions import TypeAlias @@ -9,13 +10,13 @@ _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 = ..., diff --git a/mypy/typeshed/stdlib/distutils/command/clean.pyi b/mypy/typeshed/stdlib/distutils/command/clean.pyi index 55f0a0eeaf10b..0f3768d6dcf4d 100644 --- a/mypy/typeshed/stdlib/distutils/command/clean.pyi +++ b/mypy/typeshed/stdlib/distutils/command/clean.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,12 +7,12 @@ class clean(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - build_base: Any - build_lib: Any - build_temp: Any - build_scripts: Any - bdist_base: Any - all: Any + 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 b0910091d5b6d..381e8e466bf16 100644 --- a/mypy/typeshed/stdlib/distutils/command/config.pyi +++ b/mypy/typeshed/stdlib/distutils/command/config.pyi @@ -1,7 +1,7 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence from re import Pattern -from typing import Any, ClassVar, Final, Literal +from typing import ClassVar, Final, Literal from ..ccompiler import CCompiler from ..cmd import Command @@ -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=None) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install.pyi b/mypy/typeshed/stdlib/distutils/command/install.pyi index 24a4eff2fb10f..1714e01a2c284 100644 --- a/mypy/typeshed/stdlib/distutils/command/install.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install.pyi @@ -1,4 +1,5 @@ import sys +from _typeshed import Incomplete from collections.abc import Callable from typing import Any, ClassVar, Final, Literal @@ -18,33 +19,33 @@ class install(Command): 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: ... @@ -53,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 342c7a7ccca4a..609de62b04b52 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_data.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_data.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,11 +7,11 @@ class install_data(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - install_dir: Any - outfiles: Any - root: Any + 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 3fd54989d14f3..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, str]]] - install_dir: Any + 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 7854d2393a987..3caad8a07dca4 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,9 +7,9 @@ class install_headers(Command): description: str user_options: ClassVar[list[tuple[str, str, str]]] boolean_options: ClassVar[list[str]] - install_dir: Any + 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 149ecae897817..a537e254904aa 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar, Final +from _typeshed import Incomplete +from typing import ClassVar, Final from ..cmd import Command @@ -9,12 +10,12 @@ class install_lib(Command): user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - install_dir: Any - build_dir: Any + 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 5ee5589ad33d0..658594f32e43c 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command @@ -6,13 +7,13 @@ class install_scripts(Command): description: str user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] - install_dir: Any + 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..c3bd62aaa7aa0 100644 --- a/mypy/typeshed/stdlib/distutils/command/register.pyi +++ b/mypy/typeshed/stdlib/distutils/command/register.pyi @@ -17,4 +17,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=None): ... diff --git a/mypy/typeshed/stdlib/distutils/command/sdist.pyi b/mypy/typeshed/stdlib/distutils/command/sdist.pyi index 5b7fe24195519..48a140714dda7 100644 --- a/mypy/typeshed/stdlib/distutils/command/sdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/sdist.pyi @@ -1,4 +1,4 @@ -from _typeshed import Unused +from _typeshed import Incomplete, Unused from collections.abc import Callable from typing import Any, ClassVar @@ -16,22 +16,22 @@ class sdist(Command): # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] READMES: ClassVar[tuple[str, ...]] - template: Any - manifest: Any + 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 a4d21f8ddd7b8..174f249913514 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -1,4 +1,4 @@ -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 @@ -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/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index 75fc7dbb388d6..412b94131b54e 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -88,9 +88,9 @@ class Distribution: display_options: ClassVar[_OptionsList] display_option_names: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - verbose: Literal[0, 1] - dry_run: Literal[0, 1] - help: Literal[0, 1] + verbose: bool | Literal[0, 1] + dry_run: bool | Literal[0, 1] + help: bool | Literal[0, 1] command_packages: list[str] | None script_name: str | None script_args: list[str] | None @@ -112,9 +112,7 @@ class Distribution: command_obj: Incomplete have_run: Incomplete want_user_cfg: bool - def dump_option_dicts( - self, header: Incomplete | None = None, commands: Incomplete | None = None, indent: str = "" - ) -> None: ... + def dump_option_dicts(self, header=None, commands=None, indent: str = "") -> None: ... def find_config_files(self): ... commands: Incomplete def parse_command_line(self): ... diff --git a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi index c4d37419ed06c..f3fa2a1255a6d 100644 --- a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi +++ b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi @@ -1,10 +1,10 @@ from collections.abc import Iterable, Mapping +from getopt import _SliceableT, _StrSequenceT_co from re import Pattern 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: Final = r"[a-zA-Z](?:[a-zA-Z0-9-]*)" longopt_re: Final[Pattern[str]] @@ -13,17 +13,27 @@ longopt_xlate: Final[dict[int, int]] class FancyGetopt: def __init__(self, option_table: list[_Option] | None = None) -> None: ... - # TODO kinda wrong, `getopt(object=object())` is invalid + # TODO: kinda wrong, `getopt(object=object())` is invalid @overload - def getopt(self, args: list[str] | None = None) -> _GR: ... + def getopt( + self, args: _SliceableT[_StrSequenceT_co] | None = None, object: None = None + ) -> tuple[_StrSequenceT_co, OptionDummy]: ... @overload - def getopt(self, args: list[str] | None, object: Any) -> list[str]: ... + def getopt( + self, args: _SliceableT[_StrSequenceT_co] | None, object: Any + ) -> _StrSequenceT_co: ... # object is an arbitrary non-slotted object def get_option_order(self) -> list[tuple[str, str]]: ... def generate_help(self, header: str | None = None) -> list[str]: ... +# Same note as FancyGetopt.getopt +@overload def fancy_getopt( - options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: list[str] | None -) -> list[str] | _GR: ... + options: list[_Option], negative_opt: Mapping[_Option, _Option], object: None, args: _SliceableT[_StrSequenceT_co] | None +) -> tuple[_StrSequenceT_co, OptionDummy]: ... +@overload +def fancy_getopt( + options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: _SliceableT[_StrSequenceT_co] | None +) -> _StrSequenceT_co: ... WS_TRANS: Final[dict[int, str]] diff --git a/mypy/typeshed/stdlib/distutils/file_util.pyi b/mypy/typeshed/stdlib/distutils/file_util.pyi index 873d23ea7e500..c763f91a958d7 100644 --- a/mypy/typeshed/stdlib/distutils/file_util.pyi +++ b/mypy/typeshed/stdlib/distutils/file_util.pyi @@ -29,10 +29,10 @@ def copy_file( ) -> tuple[_BytesPathT | bytes, bool]: ... @overload def move_file( - src: StrPath, dst: _StrPathT, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 + src: StrPath, dst: _StrPathT, verbose: bool | Literal[0, 1] = 1, dry_run: bool | Literal[0, 1] = 0 ) -> _StrPathT | str: ... @overload def move_file( - src: BytesPath, dst: _BytesPathT, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 + src: BytesPath, dst: _BytesPathT, verbose: bool | Literal[0, 1] = 1, dry_run: bool | Literal[0, 1] = 0 ) -> _BytesPathT | bytes: ... def write_file(filename: StrOrBytesPath, contents: Iterable[str]) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/log.pyi b/mypy/typeshed/stdlib/distutils/log.pyi index 0ea135c283718..7246dd6be0cdf 100644 --- a/mypy/typeshed/stdlib/distutils/log.pyi +++ b/mypy/typeshed/stdlib/distutils/log.pyi @@ -8,6 +8,7 @@ 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/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index 4380083027a6b..1bb96e1a77868 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -3,7 +3,7 @@ import types import unittest from _typeshed import ExcInfo from collections.abc import Callable -from typing import Any, ClassVar, NamedTuple +from typing import Any, Final, NamedTuple, type_check_only from typing_extensions import Self, TypeAlias __all__ = [ @@ -42,46 +42,44 @@ __all__ = [ "debug", ] -# MyPy errors on conditionals within named tuples. - if sys.version_info >= (3, 13): - class TestResults(NamedTuple): - def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... # type: ignore[misc] - skipped: int + @type_check_only + class _TestResultsBase(NamedTuple): failed: int attempted: int - _fields: ClassVar = ("failed", "attempted") # type: ignore[misc] - __match_args__ = ("failed", "attempted") # type: ignore[misc] - __doc__: None # type: ignore[misc] + + 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] +OPTIONFLAGS_BY_NAME: Final[dict[str, int]] def register_optionflag(name: str) -> int: ... -DONT_ACCEPT_TRUE_FOR_1: int -DONT_ACCEPT_BLANKLINE: int -NORMALIZE_WHITESPACE: int -ELLIPSIS: int -SKIP: int -IGNORE_EXCEPTION_DETAIL: int +DONT_ACCEPT_TRUE_FOR_1: Final = 1 +DONT_ACCEPT_BLANKLINE: Final = 2 +NORMALIZE_WHITESPACE: Final = 4 +ELLIPSIS: Final = 8 +SKIP: Final = 16 +IGNORE_EXCEPTION_DETAIL: Final = 32 -COMPARISON_FLAGS: int +COMPARISON_FLAGS: Final = 63 -REPORT_UDIFF: int -REPORT_CDIFF: int -REPORT_NDIFF: int -REPORT_ONLY_FIRST_FAILURE: int -FAIL_FAST: int +REPORT_UDIFF: Final = 64 +REPORT_CDIFF: Final = 128 +REPORT_NDIFF: Final = 256 +REPORT_ONLY_FIRST_FAILURE: Final = 512 +FAIL_FAST: Final = 1024 -REPORTING_FLAGS: int +REPORTING_FLAGS: Final = 1984 -BLANKLINE_MARKER: str -ELLIPSIS_MARKER: str +BLANKLINE_MARKER: Final = "" +ELLIPSIS_MARKER: Final = "..." class Example: source: str diff --git a/mypy/typeshed/stdlib/dummy_threading.pyi b/mypy/typeshed/stdlib/dummy_threading.pyi deleted file mode 100644 index 757cb8d4bd4ce..0000000000000 --- a/mypy/typeshed/stdlib/dummy_threading.pyi +++ /dev/null @@ -1,2 +0,0 @@ -from _dummy_threading import * -from _dummy_threading import __all__ as __all__ diff --git a/mypy/typeshed/stdlib/email/__init__.pyi b/mypy/typeshed/stdlib/email/__init__.pyi index fca302f5f1a7d..53f8c350b01e3 100644 --- a/mypy/typeshed/stdlib/email/__init__.pyi +++ b/mypy/typeshed/stdlib/email/__init__.pyi @@ -1,29 +1,60 @@ from collections.abc import Callable +from email._policybase import _MessageT from email.message import Message from email.policy import Policy -from typing import IO +from typing import IO, overload 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 -def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... -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 +@overload +def message_from_string(s: str) -> Message: ... +@overload +def message_from_string(s: str, _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_string(s: str, _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT]) -> _MessageT: ... +@overload +def message_from_bytes(s: bytes | bytearray) -> Message: ... +@overload +def message_from_bytes(s: bytes | bytearray, _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_bytes( + s: bytes | bytearray, _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT] +) -> _MessageT: ... +@overload +def message_from_file(fp: IO[str]) -> Message: ... +@overload +def message_from_file(fp: IO[str], _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_file(fp: IO[str], _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT]) -> _MessageT: ... +@overload +def message_from_binary_file(fp: IO[bytes]) -> Message: ... +@overload +def message_from_binary_file(fp: IO[bytes], _class: Callable[[], _MessageT]) -> _MessageT: ... +@overload +def message_from_binary_file(fp: IO[bytes], _class: Callable[[], _MessageT] = ..., *, policy: Policy[_MessageT]) -> _MessageT: ... diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi index ff405a8b61d22..dededd006e5b5 100644 --- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi +++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi @@ -16,14 +16,16 @@ 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 +# Added in Python 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 +# Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5 SPECIALSNL: Final[set[str]] +# Added in Python 3.9.23, 3.10.17, 3.11.12, 3.12.9, 3.13.2 +def make_quoted_pairs(value: Any) -> str: ... def quote_string(value: Any) -> str: ... -rfc2047_matcher: Pattern[str] +rfc2047_matcher: Final[Pattern[str]] class TokenList(list[TokenList | Terminal]): token_type: str | None @@ -345,7 +347,7 @@ ListSeparator: Final[ValueTerminal] RouteComponentMarker: Final[ValueTerminal] def get_fws(value: str) -> tuple[WhiteSpaceTerminal, str]: ... -def get_encoded_word(value: str) -> tuple[EncodedWord, str]: ... +def get_encoded_word(value: str, terminal_type: str = "vtext") -> tuple[EncodedWord, str]: ... def get_unstructured(value: str) -> UnstructuredTokenList: ... def get_qp_ctext(value: str) -> tuple[WhiteSpaceTerminal, str]: ... def get_qcontent(value: str) -> tuple[ValueTerminal, str]: ... diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi index 9e1f653c9d78f..0fb890d424b10 100644 --- a/mypy/typeshed/stdlib/email/_policybase.pyi +++ b/mypy/typeshed/stdlib/email/_policybase.pyi @@ -1,11 +1,32 @@ 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, Generic, Protocol, TypeVar, type_check_only from typing_extensions import Self -class _PolicyBase: +__all__ = ["Policy", "Compat32", "compat32"] + +_MessageT = TypeVar("_MessageT", bound=Message[Any, Any], default=Message[str, str]) +_MessageT_co = TypeVar("_MessageT_co", covariant=True, bound=Message[Any, Any], default=Message[str, str]) + +@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_co]): + max_line_length: int | None + linesep: str + cte_type: str + raise_on_defect: bool + mangle_from_: bool + message_factory: _MessageFactory[_MessageT_co] | None + # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool + def __init__( self, *, @@ -14,8 +35,8 @@ class _PolicyBase: cte_type: str = "8bit", raise_on_defect: bool = False, mangle_from_: bool = ..., # default depends on sub-class - message_factory: Callable[[Policy], Message] | None = None, - # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + message_factory: _MessageFactory[_MessageT_co] | None = None, + # Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5 verify_generated_headers: bool = True, ) -> None: ... def clone( @@ -26,24 +47,17 @@ class _PolicyBase: cte_type: str = ..., raise_on_defect: bool = ..., mangle_from_: bool = ..., - message_factory: Callable[[Policy], Message] | None = ..., - # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + message_factory: _MessageFactory[_MessageT_co] | None = ..., + # Added in Python 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, metaclass=ABCMeta): - max_line_length: int | None - linesep: str - cte_type: str - raise_on_defect: bool - mangle_from_: bool - message_factory: Callable[[Policy], Message] | None - # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 - verify_generated_headers: bool - - def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... - def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... +class Policy(_PolicyBase[_MessageT_co], metaclass=ABCMeta): + # Every Message object has a `defects` attribute, so the following + # methods will work for any Message object. + def handle_defect(self, obj: Message[Any, Any], defect: MessageDefect) -> None: ... + def register_defect(self, obj: Message[Any, Any], defect: MessageDefect) -> None: ... def header_max_count(self, name: str) -> int | None: ... @abstractmethod def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... @@ -56,11 +70,11 @@ class Policy(_PolicyBase, metaclass=ABCMeta): @abstractmethod def fold_binary(self, name: str, value: str) -> bytes: ... -class Compat32(Policy): +class Compat32(Policy[_MessageT_co]): 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[str, str]] diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index 2939192c95264..e1930835bbd11 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -1,12 +1,19 @@ from collections.abc import Callable, Iterator from email.message import Message -from typing import Final, overload +from typing import ClassVar, Final, overload __all__ = ["Charset", "add_alias", "add_charset", "add_codec"] -QP: Final[int] # undocumented -BASE64: Final[int] # undocumented -SHORTEST: Final[int] # undocumented +QP: Final = 1 # undocumented +BASE64: Final = 2 # undocumented +SHORTEST: Final = 3 # undocumented +RFC2047_CHROME_LEN: Final = 7 # undocumented +DEFAULT_CHARSET: Final = "us-ascii" # undocumented +UNKNOWN8BIT: Final = "unknown-8bit" # undocumented +EMPTYSTRING: Final = "" # undocumented +CHARSETS: Final[dict[str, tuple[int | None, int | None, str | None]]] +ALIASES: Final[dict[str, str]] +CODEC_MAP: Final[dict[str, str | None]] # undocumented class Charset: input_charset: str @@ -24,6 +31,7 @@ class Charset: def body_encode(self, string: None) -> None: ... @overload def body_encode(self, string: str | bytes) -> str: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... def __ne__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/errors.pyi b/mypy/typeshed/stdlib/email/errors.pyi index f105576c5ee49..b501a58665560 100644 --- a/mypy/typeshed/stdlib/email/errors.pyi +++ b/mypy/typeshed/stdlib/email/errors.pyi @@ -7,7 +7,7 @@ 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 +# Added in Python 3.9.20, 3.10.15, 3.11.10, 3.12.5 class HeaderWriteError(MessageError): ... class MessageDefect(ValueError): diff --git a/mypy/typeshed/stdlib/email/feedparser.pyi b/mypy/typeshed/stdlib/email/feedparser.pyi index 22920fc99c69a..d9279e9cd996d 100644 --- a/mypy/typeshed/stdlib/email/feedparser.pyi +++ b/mypy/typeshed/stdlib/email/feedparser.pyi @@ -1,23 +1,22 @@ from collections.abc import Callable +from email._policybase import _MessageT from email.message import Message from email.policy import Policy -from typing import Generic, TypeVar, overload +from typing import Generic, overload __all__ = ["FeedParser", "BytesFeedParser"] -_MessageT = TypeVar("_MessageT", bound=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..d30e686299fab 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[Any, Any], 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/header.pyi b/mypy/typeshed/stdlib/email/header.pyi index 212132c6be18f..a26bbb516e096 100644 --- a/mypy/typeshed/stdlib/email/header.pyi +++ b/mypy/typeshed/stdlib/email/header.pyi @@ -1,6 +1,6 @@ from collections.abc import Iterable from email.charset import Charset -from typing import Any +from typing import Any, ClassVar __all__ = ["Header", "decode_header", "make_header"] @@ -16,6 +16,7 @@ class Header: ) -> None: ... def append(self, s: bytes | bytearray | str, charset: Charset | str | None = None, errors: str = "strict") -> None: ... def encode(self, splitchars: str = ";, \t", maxlinelen: int | None = None, linesep: str = "\n") -> str: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... def __ne__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/headerregistry.pyi b/mypy/typeshed/stdlib/email/headerregistry.pyi index 2ffdca9b2f229..bea68307e0091 100644 --- a/mypy/typeshed/stdlib/email/headerregistry.pyi +++ b/mypy/typeshed/stdlib/email/headerregistry.pyi @@ -13,7 +13,7 @@ from email._header_value_parser import ( ) from email.errors import MessageDefect from email.policy import Policy -from typing import Any, ClassVar, Literal, Protocol +from typing import Any, ClassVar, Literal, Protocol, type_check_only from typing_extensions import Self class BaseHeader(str): @@ -41,7 +41,7 @@ class DateHeader: max_count: ClassVar[Literal[1] | None] def init(self, name: str, *, parse_tree: TokenList, defects: Iterable[MessageDefect], datetime: _datetime) -> None: ... @property - def datetime(self) -> _datetime: ... + def datetime(self) -> _datetime | None: ... @staticmethod def value_parser(value: str) -> UnstructuredTokenList: ... @classmethod @@ -137,6 +137,7 @@ class MessageIDHeader: @staticmethod def value_parser(value: str) -> MessageID: ... +@type_check_only class _HeaderParser(Protocol): max_count: ClassVar[Literal[1] | None] @staticmethod @@ -167,6 +168,7 @@ class Address: def __init__( self, display_name: str = "", username: str | None = "", domain: str | None = "", addr_spec: str | None = None ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... class Group: @@ -175,4 +177,5 @@ class Group: @property def addresses(self) -> tuple[Address, ...]: ... def __init__(self, display_name: str | None = None, addresses: Iterable[Address] | None = None) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __eq__(self, other: object) -> bool: ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 7e80f13adb8fb..794882b140e61 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -1,55 +1,61 @@ +from _typeshed import MaybeNone from collections.abc import Generator, Iterator, Sequence from email import _ParamsType, _ParamType from email.charset import Charset from email.contentmanager import ContentManager from email.errors import MessageDefect from email.policy import Policy -from typing import Any, Generic, Literal, Protocol, TypeVar, overload +from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only from typing_extensions import Self, TypeAlias __all__ = ["Message", "EmailMessage"] _T = TypeVar("_T") # Type returned by Policy.header_fetch_parse, often str or Header. -_HeaderT = TypeVar("_HeaderT", default=str) -_HeaderParamT = TypeVar("_HeaderParamT", default=str) +_HeaderT_co = TypeVar("_HeaderT_co", covariant=True, default=str) +_HeaderParamT_contra = TypeVar("_HeaderParamT_contra", contravariant=True, default=str) # Represents headers constructed by HeaderRegistry. Those are sub-classes # of BaseHeader and another header type. -_HeaderRegistryT = TypeVar("_HeaderRegistryT", default=Any) -_HeaderRegistryParamT = TypeVar("_HeaderRegistryParamT", default=Any) +_HeaderRegistryT_co = TypeVar("_HeaderRegistryT_co", covariant=True, default=Any) +_HeaderRegistryParamT_contra = TypeVar("_HeaderRegistryParamT_contra", contravariant=True, default=Any) _PayloadType: TypeAlias = Message | str _EncodedPayloadType: TypeAlias = Message | bytes _MultipartPayloadType: TypeAlias = list[_PayloadType] _CharsetType: TypeAlias = Charset | str | None +@type_check_only class _SupportsEncodeToPayload(Protocol): def encode(self, encoding: str, /) -> _PayloadType | _MultipartPayloadType | _SupportsDecodeToPayload: ... +@type_check_only class _SupportsDecodeToPayload(Protocol): def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ... -class Message(Generic[_HeaderT, _HeaderParamT]): - policy: Policy # undocumented +class Message(Generic[_HeaderT_co, _HeaderParamT_contra]): + # 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: ... + 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] @@ -72,22 +78,22 @@ 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 __setitem__(self, name: str, val: _HeaderParamT) -> None: ... + def __getitem__(self, name: str) -> _HeaderT_co | MaybeNone: ... + def __setitem__(self, name: str, val: _HeaderParamT_contra) -> None: ... def __delitem__(self, name: str) -> None: ... def keys(self) -> list[str]: ... - def values(self) -> list[_HeaderT]: ... - def items(self) -> list[tuple[str, _HeaderT]]: ... + def values(self) -> list[_HeaderT_co]: ... + def items(self) -> list[tuple[str, _HeaderT_co]]: ... @overload - def get(self, name: str, failobj: None = None) -> _HeaderT | None: ... + def get(self, name: str, failobj: None = None) -> _HeaderT_co | None: ... @overload - def get(self, name: str, failobj: _T) -> _HeaderT | _T: ... + def get(self, name: str, failobj: _T) -> _HeaderT_co | _T: ... @overload - def get_all(self, name: str, failobj: None = None) -> list[_HeaderT] | None: ... + def get_all(self, name: str, failobj: None = None) -> list[_HeaderT_co] | None: ... @overload - def get_all(self, name: str, failobj: _T) -> list[_HeaderT] | _T: ... + def get_all(self, name: str, failobj: _T) -> list[_HeaderT_co] | _T: ... def add_header(self, _name: str, _value: str, **_params: _ParamsType) -> None: ... - def replace_header(self, _name: str, _value: _HeaderParamT) -> None: ... + def replace_header(self, _name: str, _value: _HeaderParamT_contra) -> None: ... def get_content_type(self) -> str: ... def get_content_maintype(self) -> str: ... def get_content_subtype(self) -> str: ... @@ -126,8 +132,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, @@ -139,20 +145,19 @@ 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]]: ... + def set_raw(self, name: str, value: _HeaderParamT_contra) -> None: ... + def raw_items(self) -> Iterator[tuple[str, _HeaderT_co]]: ... -class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): - def __init__(self, policy: Policy | None = None) -> None: ... - def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT] | None: ... +class MIMEPart(Message[_HeaderRegistryT_co, _HeaderRegistryParamT_contra]): + def __init__(self, policy: Policy[Any] | None = None) -> None: ... + def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT_co] | None: ... 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 iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT_co]]: ... 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: ... def make_related(self, boundary: str | None = None) -> None: ... @@ -163,7 +168,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/mime/message.pyi b/mypy/typeshed/stdlib/email/mime/message.pyi index 23cf58619ad96..a1e370e2eab51 100644 --- a/mypy/typeshed/stdlib/email/mime/message.pyi +++ b/mypy/typeshed/stdlib/email/mime/message.pyi @@ -1,8 +1,8 @@ -from email.message import Message +from email._policybase import _MessageT from email.mime.nonmultipart import MIMENonMultipart from email.policy import Policy __all__ = ["MIMEMessage"] class MIMEMessage(MIMENonMultipart): - def __init__(self, _msg: Message, _subtype: str = "rfc822", *, policy: Policy | None = None) -> None: ... + def __init__(self, _msg: _MessageT, _subtype: str = "rfc822", *, policy: Policy[_MessageT] | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/email/mime/multipart.pyi b/mypy/typeshed/stdlib/email/mime/multipart.pyi index 6163810ed94ae..fb9599edbcb8f 100644 --- a/mypy/typeshed/stdlib/email/mime/multipart.pyi +++ b/mypy/typeshed/stdlib/email/mime/multipart.pyi @@ -1,6 +1,6 @@ from collections.abc import Sequence from email import _ParamsType -from email.message import Message +from email._policybase import _MessageT from email.mime.base import MIMEBase from email.policy import Policy @@ -11,8 +11,8 @@ class MIMEMultipart(MIMEBase): self, _subtype: str = "mixed", boundary: str | None = None, - _subparts: Sequence[Message] | None = None, + _subparts: Sequence[_MessageT] | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT] | None = None, **_params: _ParamsType, ) -> None: ... diff --git a/mypy/typeshed/stdlib/email/mime/text.pyi b/mypy/typeshed/stdlib/email/mime/text.pyi index 74d5ef4c5caea..edfa67a092427 100644 --- a/mypy/typeshed/stdlib/email/mime/text.pyi +++ b/mypy/typeshed/stdlib/email/mime/text.pyi @@ -1,5 +1,5 @@ +from email._policybase import Policy from email.mime.nonmultipart import MIMENonMultipart -from email.policy import Policy __all__ = ["MIMEText"] diff --git a/mypy/typeshed/stdlib/email/parser.pyi b/mypy/typeshed/stdlib/email/parser.pyi index fecb29d90b2e4..a4924a6cbd88f 100644 --- a/mypy/typeshed/stdlib/email/parser.pyi +++ b/mypy/typeshed/stdlib/email/parser.pyi @@ -1,20 +1,21 @@ from _typeshed import SupportsRead from collections.abc import Callable +from email._policybase import _MessageT from email.feedparser import BytesFeedParser as BytesFeedParser, FeedParser as FeedParser from email.message import Message from email.policy import Policy from io import _WrappedBuffer -from typing import Generic, TypeVar, overload +from typing import Generic, overload __all__ = ["Parser", "HeaderParser", "BytesParser", "BytesHeaderParser", "FeedParser", "BytesFeedParser"] -_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) -> None: ... + @overload + def __init__(self, _class: None = None, *, policy: Policy[_MessageT]) -> None: ... @overload - def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _class: Callable[[], _MessageT] | None, *, 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 +26,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) -> None: ... + @overload + def __init__(self, _class: None = None, *, policy: Policy[_MessageT]) -> 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..35c999919eede 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -1,16 +1,35 @@ 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, _MessageT, compat32 as compat32 from email.contentmanager import ContentManager -from email.message import Message -from typing import Any +from email.message import EmailMessage +from typing import Any, overload +from typing_extensions import Self __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] -class EmailPolicy(Policy): +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.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 +38,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.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 +51,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.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 dc3eecb5ef7fb..efc32a7abce29 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -30,11 +30,11 @@ _PDTZ: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int | None def quote(str: str) -> str: ... def unquote(str: str) -> str: ... -# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +# `strict` parameter added in Python 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: ... -# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +# `strict` parameter added in Python 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: ... diff --git a/mypy/typeshed/stdlib/encodings/__init__.pyi b/mypy/typeshed/stdlib/encodings/__init__.pyi index 2e83f0f65a71a..61f86d243c720 100644 --- a/mypy/typeshed/stdlib/encodings/__init__.pyi +++ b/mypy/typeshed/stdlib/encodings/__init__.pyi @@ -1,4 +1,4 @@ -from _typeshed import Incomplete +import sys from codecs import CodecInfo class CodecRegistryError(LookupError, SystemError): ... @@ -6,5 +6,8 @@ class CodecRegistryError(LookupError, SystemError): ... def normalize_encoding(encoding: str | bytes) -> str: ... def search_function(encoding: str) -> CodecInfo | None: ... +if sys.version_info >= (3, 14) and sys.platform == "win32": + def win32_code_page_search_function(encoding: str) -> CodecInfo | None: ... + # Needed for submodules -def __getattr__(name: str) -> Incomplete: ... +def __getattr__(name: str): ... # incomplete module 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_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..2887739468f24 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi @@ -0,0 +1,23 @@ +import codecs +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 + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> 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: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... + +class StreamReader(Codec, codecs.StreamReader): + 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..ceaa39a3859ae --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi @@ -0,0 +1,23 @@ +import codecs +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 + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> 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: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... + +class StreamReader(Codec, codecs.StreamReader): + 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..c131c93923937 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -4,8 +4,8 @@ import types from _typeshed import SupportsKeysAndGetItem, Unused from builtins import property as _builtins_property from collections.abc import Callable, Iterable, Iterator, Mapping -from typing import Any, Generic, Literal, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing import Any, Final, Generic, Literal, TypeVar, overload +from typing_extensions import Self, TypeAlias, disjoint_base __all__ = ["EnumMeta", "Enum", "IntEnum", "Flag", "IntFlag", "auto", "unique"] @@ -53,6 +53,7 @@ _EnumerationT = TypeVar("_EnumerationT", bound=type[Enum]) # >>> Enum('Foo', names={'RED': 1, 'YELLOW': 2}) # _EnumNames: TypeAlias = str | Iterable[str] | Iterable[Iterable[str | Any]] | Mapping[str, Any] +_Signature: TypeAlias = Any # TODO: Unable to import Signature from inspect module if sys.version_info >= (3, 11): class nonmember(Generic[_EnumMemberT]): @@ -64,7 +65,11 @@ if sys.version_info >= (3, 11): def __init__(self, value: _EnumMemberT) -> None: ... class _EnumDict(dict[str, Any]): - def __init__(self) -> None: ... + if sys.version_info >= (3, 13): + def __init__(self, cls_name: str | None = None) -> None: ... + else: + def __init__(self) -> None: ... + def __setitem__(self, key: str, value: Any) -> None: ... if sys.version_info >= (3, 11): # See comment above `typing.MutableMapping.update` @@ -96,20 +101,13 @@ class EnumMeta(type): _simple: bool = False, **kwds: Any, ) -> _typeshed.Self: ... - elif sys.version_info >= (3, 9): + else: def __new__( metacls: type[_typeshed.Self], cls: str, bases: tuple[type, ...], classdict: _EnumDict, **kwds: Any ) -> _typeshed.Self: ... - else: - def __new__(metacls: type[_typeshed.Self], cls: str, bases: tuple[type, ...], classdict: _EnumDict) -> _typeshed.Self: ... - - if sys.version_info >= (3, 9): - @classmethod - def __prepare__(metacls, cls: str, bases: tuple[type, ...], **kwds: Any) -> _EnumDict: ... # type: ignore[override] - else: - @classmethod - def __prepare__(metacls, cls: str, bases: tuple[type, ...]) -> _EnumDict: ... # type: ignore[override] + @classmethod + def __prepare__(metacls, cls: str, bases: tuple[type, ...], **kwds: Any) -> _EnumDict: ... # type: ignore[override] def __iter__(self: type[_EnumMemberT]) -> Iterator[_EnumMemberT]: ... def __reversed__(self: type[_EnumMemberT]) -> Iterator[_EnumMemberT]: ... if sys.version_info >= (3, 12): @@ -169,6 +167,9 @@ class EnumMeta(type): if sys.version_info >= (3, 12): @overload def __call__(cls: type[_EnumMemberT], value: Any, *values: Any) -> _EnumMemberT: ... + if sys.version_info >= (3, 14): + @property + def __signature__(cls) -> _Signature: ... _member_names_: list[str] # undocumented _member_map_: dict[str, Enum] # undocumented @@ -215,23 +216,37 @@ class Enum(metaclass=EnumMeta): if sys.version_info >= (3, 11): def __copy__(self) -> Self: ... def __deepcopy__(self, memo: Any) -> Self: ... - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 12) and sys.version_info < (3, 14): @classmethod def __signature__(cls) -> str: ... + if sys.version_info >= (3, 13): + # Value may be any type, even in special enums. Enabling Enum parsing from + # multiple value types + def _add_value_alias_(self, value: Any) -> None: ... + def _add_alias_(self, name: str) -> None: ... if sys.version_info >= (3, 11): class ReprEnum(Enum): ... -if sys.version_info >= (3, 11): - _IntEnumBase = ReprEnum +if sys.version_info >= (3, 12): + class IntEnum(int, ReprEnum): + _value_: int + @_magic_enum_attr + def value(self) -> int: ... + def __new__(cls, value: int) -> Self: ... + else: - _IntEnumBase = Enum + if sys.version_info >= (3, 11): + _IntEnumBase = ReprEnum + else: + _IntEnumBase = Enum -class IntEnum(int, _IntEnumBase): - _value_: int - @_magic_enum_attr - def value(self) -> int: ... - def __new__(cls, value: int) -> Self: ... + @disjoint_base + class IntEnum(int, _IntEnumBase): + _value_: int + @_magic_enum_attr + def value(self) -> int: ... + def __new__(cls, value: int) -> Self: ... def unique(enumeration: _EnumerationT) -> _EnumerationT: ... @@ -271,9 +286,9 @@ if sys.version_info >= (3, 11): NAMED_FLAGS = "multi-flag aliases may not contain unnamed flags" UNIQUE = "one name per value" - CONTINUOUS = EnumCheck.CONTINUOUS - NAMED_FLAGS = EnumCheck.NAMED_FLAGS - UNIQUE = EnumCheck.UNIQUE + CONTINUOUS: Final = EnumCheck.CONTINUOUS + NAMED_FLAGS: Final = EnumCheck.NAMED_FLAGS + UNIQUE: Final = EnumCheck.UNIQUE class verify: def __init__(self, *checks: EnumCheck) -> None: ... @@ -285,44 +300,72 @@ if sys.version_info >= (3, 11): EJECT = "eject" KEEP = "keep" - STRICT = FlagBoundary.STRICT - CONFORM = FlagBoundary.CONFORM - EJECT = FlagBoundary.EJECT - KEEP = FlagBoundary.KEEP + STRICT: Final = FlagBoundary.STRICT + CONFORM: Final = FlagBoundary.CONFORM + EJECT: Final = FlagBoundary.EJECT + KEEP: Final = FlagBoundary.KEEP def global_str(self: Enum) -> str: ... def global_enum(cls: _EnumerationT, update_str: bool = False) -> _EnumerationT: ... def global_enum_repr(self: Enum) -> str: ... def global_flag_repr(self: Flag) -> str: ... + def show_flag_values(value: int) -> list[int]: ... -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 12): + # The body of the class is the same, but the base classes are different. + class IntFlag(int, ReprEnum, Flag, boundary=KEEP): # type: ignore[misc] # complaints about incompatible bases + def __new__(cls, value: int) -> Self: ... + def __or__(self, other: int) -> Self: ... + def __and__(self, other: int) -> Self: ... + def __xor__(self, other: int) -> Self: ... + def __invert__(self) -> Self: ... + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ + +elif sys.version_info >= (3, 11): # The body of the class is the same, but the base classes are different. + @disjoint_base class IntFlag(int, ReprEnum, Flag, boundary=KEEP): # type: ignore[misc] # complaints about incompatible bases def __new__(cls, value: int) -> Self: ... def __or__(self, other: int) -> Self: ... def __and__(self, other: int) -> Self: ... def __xor__(self, other: int) -> Self: ... + def __invert__(self) -> Self: ... __ror__ = __or__ __rand__ = __and__ __rxor__ = __xor__ else: + @disjoint_base class IntFlag(int, Flag): # type: ignore[misc] # complaints about incompatible bases def __new__(cls, value: int) -> Self: ... def __or__(self, other: int) -> Self: ... def __and__(self, other: int) -> Self: ... def __xor__(self, other: int) -> Self: ... + def __invert__(self) -> Self: ... __ror__ = __or__ __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/errno.pyi b/mypy/typeshed/stdlib/errno.pyi index 84d2b44a6a61b..e025e1fd13b9b 100644 --- a/mypy/typeshed/stdlib/errno.pyi +++ b/mypy/typeshed/stdlib/errno.pyi @@ -1,222 +1,227 @@ import sys from collections.abc import Mapping +from typing import Final errorcode: Mapping[int, str] -EPERM: int -ENOENT: int -ESRCH: int -EINTR: int -EIO: int -ENXIO: int -E2BIG: int -ENOEXEC: int -EBADF: int -ECHILD: int -EAGAIN: int -ENOMEM: int -EACCES: int -EFAULT: int -EBUSY: int -EEXIST: int -EXDEV: int -ENODEV: int -ENOTDIR: int -EISDIR: int -EINVAL: int -ENFILE: int -EMFILE: int -ENOTTY: int -ETXTBSY: int -EFBIG: int -ENOSPC: int -ESPIPE: int -EROFS: int -EMLINK: int -EPIPE: int -EDOM: int -ERANGE: int -EDEADLK: int -ENAMETOOLONG: int -ENOLCK: int -ENOSYS: int -ENOTEMPTY: int -ELOOP: int -EWOULDBLOCK: int -ENOMSG: int -EIDRM: int -ENOSTR: int -ENODATA: int -ETIME: int -ENOSR: int -EREMOTE: int -ENOLINK: int -EPROTO: int -EBADMSG: int -EOVERFLOW: int -EILSEQ: int -EUSERS: int -ENOTSOCK: int -EDESTADDRREQ: int -EMSGSIZE: int -EPROTOTYPE: int -ENOPROTOOPT: int -EPROTONOSUPPORT: int -ESOCKTNOSUPPORT: int -ENOTSUP: int -EOPNOTSUPP: int -EPFNOSUPPORT: int -EAFNOSUPPORT: int -EADDRINUSE: int -EADDRNOTAVAIL: int -ENETDOWN: int -ENETUNREACH: int -ENETRESET: int -ECONNABORTED: int -ECONNRESET: int -ENOBUFS: int -EISCONN: int -ENOTCONN: int -ESHUTDOWN: int -ETOOMANYREFS: int -ETIMEDOUT: int -ECONNREFUSED: int -EHOSTDOWN: int -EHOSTUNREACH: int -EALREADY: int -EINPROGRESS: int -ESTALE: int -EDQUOT: int -ECANCELED: int # undocumented -ENOTRECOVERABLE: int # undocumented -EOWNERDEAD: int # undocumented +EPERM: Final[int] +ENOENT: Final[int] +ESRCH: Final[int] +EINTR: Final[int] +EIO: Final[int] +ENXIO: Final[int] +E2BIG: Final[int] +ENOEXEC: Final[int] +EBADF: Final[int] +ECHILD: Final[int] +EAGAIN: Final[int] +ENOMEM: Final[int] +EACCES: Final[int] +EFAULT: Final[int] +EBUSY: Final[int] +EEXIST: Final[int] +EXDEV: Final[int] +ENODEV: Final[int] +ENOTDIR: Final[int] +EISDIR: Final[int] +EINVAL: Final[int] +ENFILE: Final[int] +EMFILE: Final[int] +ENOTTY: Final[int] +ETXTBSY: Final[int] +EFBIG: Final[int] +ENOSPC: Final[int] +ESPIPE: Final[int] +EROFS: Final[int] +EMLINK: Final[int] +EPIPE: Final[int] +EDOM: Final[int] +ERANGE: Final[int] +EDEADLK: Final[int] +ENAMETOOLONG: Final[int] +ENOLCK: Final[int] +ENOSYS: Final[int] +ENOTEMPTY: Final[int] +ELOOP: Final[int] +EWOULDBLOCK: Final[int] +ENOMSG: Final[int] +EIDRM: Final[int] +ENOSTR: Final[int] +ENODATA: Final[int] +ETIME: Final[int] +ENOSR: Final[int] +EREMOTE: Final[int] +ENOLINK: Final[int] +EPROTO: Final[int] +EBADMSG: Final[int] +EOVERFLOW: Final[int] +EILSEQ: Final[int] +EUSERS: Final[int] +ENOTSOCK: Final[int] +EDESTADDRREQ: Final[int] +EMSGSIZE: Final[int] +EPROTOTYPE: Final[int] +ENOPROTOOPT: Final[int] +EPROTONOSUPPORT: Final[int] +ESOCKTNOSUPPORT: Final[int] +ENOTSUP: Final[int] +EOPNOTSUPP: Final[int] +EPFNOSUPPORT: Final[int] +EAFNOSUPPORT: Final[int] +EADDRINUSE: Final[int] +EADDRNOTAVAIL: Final[int] +ENETDOWN: Final[int] +ENETUNREACH: Final[int] +ENETRESET: Final[int] +ECONNABORTED: Final[int] +ECONNRESET: Final[int] +ENOBUFS: Final[int] +EISCONN: Final[int] +ENOTCONN: Final[int] +ESHUTDOWN: Final[int] +ETOOMANYREFS: Final[int] +ETIMEDOUT: Final[int] +ECONNREFUSED: Final[int] +EHOSTDOWN: Final[int] +EHOSTUNREACH: Final[int] +EALREADY: Final[int] +EINPROGRESS: Final[int] +ESTALE: Final[int] +EDQUOT: Final[int] +ECANCELED: Final[int] # undocumented +ENOTRECOVERABLE: Final[int] # undocumented +EOWNERDEAD: Final[int] # undocumented if sys.platform == "sunos5" or sys.platform == "solaris": # noqa: Y008 - ELOCKUNMAPPED: int - ENOTACTIVE: int + ELOCKUNMAPPED: Final[int] + ENOTACTIVE: Final[int] if sys.platform != "win32": - ENOTBLK: int - EMULTIHOP: int + ENOTBLK: Final[int] + EMULTIHOP: Final[int] if sys.platform == "darwin": # All of the below are undocumented - EAUTH: int - EBADARCH: int - EBADEXEC: int - EBADMACHO: int - EBADRPC: int - EDEVERR: int - EFTYPE: int - ENEEDAUTH: int - ENOATTR: int - ENOPOLICY: int - EPROCLIM: int - EPROCUNAVAIL: int - EPROGMISMATCH: int - EPROGUNAVAIL: int - EPWROFF: int - ERPCMISMATCH: int - ESHLIBVERS: int + EAUTH: Final[int] + EBADARCH: Final[int] + EBADEXEC: Final[int] + EBADMACHO: Final[int] + EBADRPC: Final[int] + EDEVERR: Final[int] + EFTYPE: Final[int] + ENEEDAUTH: Final[int] + ENOATTR: Final[int] + ENOPOLICY: Final[int] + EPROCLIM: Final[int] + EPROCUNAVAIL: Final[int] + EPROGMISMATCH: Final[int] + EPROGUNAVAIL: Final[int] + EPWROFF: Final[int] + ERPCMISMATCH: Final[int] + ESHLIBVERS: Final[int] if sys.version_info >= (3, 11): - EQFULL: int + EQFULL: Final[int] + ENOTCAPABLE: Final[int] # available starting with 3.11.1 if sys.platform != "darwin": - EDEADLOCK: int + EDEADLOCK: Final[int] if sys.platform != "win32" and sys.platform != "darwin": - ECHRNG: int - EL2NSYNC: int - EL3HLT: int - EL3RST: int - ELNRNG: int - EUNATCH: int - ENOCSI: int - EL2HLT: int - EBADE: int - EBADR: int - EXFULL: int - ENOANO: int - EBADRQC: int - EBADSLT: int - EBFONT: int - ENONET: int - ENOPKG: int - EADV: int - ESRMNT: int - ECOMM: int - EDOTDOT: int - ENOTUNIQ: int - EBADFD: int - EREMCHG: int - ELIBACC: int - ELIBBAD: int - ELIBSCN: int - ELIBMAX: int - ELIBEXEC: int - ERESTART: int - ESTRPIPE: int - EUCLEAN: int - ENOTNAM: int - ENAVAIL: int - EISNAM: int - EREMOTEIO: int + ECHRNG: Final[int] + EL2NSYNC: Final[int] + EL3HLT: Final[int] + EL3RST: Final[int] + ELNRNG: Final[int] + EUNATCH: Final[int] + ENOCSI: Final[int] + EL2HLT: Final[int] + EBADE: Final[int] + EBADR: Final[int] + EXFULL: Final[int] + ENOANO: Final[int] + EBADRQC: Final[int] + EBADSLT: Final[int] + EBFONT: Final[int] + ENONET: Final[int] + ENOPKG: Final[int] + EADV: Final[int] + ESRMNT: Final[int] + ECOMM: Final[int] + EDOTDOT: Final[int] + ENOTUNIQ: Final[int] + EBADFD: Final[int] + EREMCHG: Final[int] + ELIBACC: Final[int] + ELIBBAD: Final[int] + ELIBSCN: Final[int] + ELIBMAX: Final[int] + ELIBEXEC: Final[int] + ERESTART: Final[int] + ESTRPIPE: Final[int] + EUCLEAN: Final[int] + ENOTNAM: Final[int] + ENAVAIL: Final[int] + EISNAM: Final[int] + EREMOTEIO: Final[int] # All of the below are undocumented - EKEYEXPIRED: int - EKEYREJECTED: int - EKEYREVOKED: int - EMEDIUMTYPE: int - ENOKEY: int - ENOMEDIUM: int - ERFKILL: int + EKEYEXPIRED: Final[int] + EKEYREJECTED: Final[int] + EKEYREVOKED: Final[int] + EMEDIUMTYPE: Final[int] + ENOKEY: Final[int] + ENOMEDIUM: Final[int] + ERFKILL: Final[int] + + if sys.version_info >= (3, 14): + EHWPOISON: Final[int] if sys.platform == "win32": # All of these are undocumented - WSABASEERR: int - WSAEACCES: int - WSAEADDRINUSE: int - WSAEADDRNOTAVAIL: int - WSAEAFNOSUPPORT: int - WSAEALREADY: int - WSAEBADF: int - WSAECONNABORTED: int - WSAECONNREFUSED: int - WSAECONNRESET: int - WSAEDESTADDRREQ: int - WSAEDISCON: int - WSAEDQUOT: int - WSAEFAULT: int - WSAEHOSTDOWN: int - WSAEHOSTUNREACH: int - WSAEINPROGRESS: int - WSAEINTR: int - WSAEINVAL: int - WSAEISCONN: int - WSAELOOP: int - WSAEMFILE: int - WSAEMSGSIZE: int - WSAENAMETOOLONG: int - WSAENETDOWN: int - WSAENETRESET: int - WSAENETUNREACH: int - WSAENOBUFS: int - WSAENOPROTOOPT: int - WSAENOTCONN: int - WSAENOTEMPTY: int - WSAENOTSOCK: int - WSAEOPNOTSUPP: int - WSAEPFNOSUPPORT: int - WSAEPROCLIM: int - WSAEPROTONOSUPPORT: int - WSAEPROTOTYPE: int - WSAEREMOTE: int - WSAESHUTDOWN: int - WSAESOCKTNOSUPPORT: int - WSAESTALE: int - WSAETIMEDOUT: int - WSAETOOMANYREFS: int - WSAEUSERS: int - WSAEWOULDBLOCK: int - WSANOTINITIALISED: int - WSASYSNOTREADY: int - WSAVERNOTSUPPORTED: int + WSABASEERR: Final[int] + WSAEACCES: Final[int] + WSAEADDRINUSE: Final[int] + WSAEADDRNOTAVAIL: Final[int] + WSAEAFNOSUPPORT: Final[int] + WSAEALREADY: Final[int] + WSAEBADF: Final[int] + WSAECONNABORTED: Final[int] + WSAECONNREFUSED: Final[int] + WSAECONNRESET: Final[int] + WSAEDESTADDRREQ: Final[int] + WSAEDISCON: Final[int] + WSAEDQUOT: Final[int] + WSAEFAULT: Final[int] + WSAEHOSTDOWN: Final[int] + WSAEHOSTUNREACH: Final[int] + WSAEINPROGRESS: Final[int] + WSAEINTR: Final[int] + WSAEINVAL: Final[int] + WSAEISCONN: Final[int] + WSAELOOP: Final[int] + WSAEMFILE: Final[int] + WSAEMSGSIZE: Final[int] + WSAENAMETOOLONG: Final[int] + WSAENETDOWN: Final[int] + WSAENETRESET: Final[int] + WSAENETUNREACH: Final[int] + WSAENOBUFS: Final[int] + WSAENOPROTOOPT: Final[int] + WSAENOTCONN: Final[int] + WSAENOTEMPTY: Final[int] + WSAENOTSOCK: Final[int] + WSAEOPNOTSUPP: Final[int] + WSAEPFNOSUPPORT: Final[int] + WSAEPROCLIM: Final[int] + WSAEPROTONOSUPPORT: Final[int] + WSAEPROTOTYPE: Final[int] + WSAEREMOTE: Final[int] + WSAESHUTDOWN: Final[int] + WSAESOCKTNOSUPPORT: Final[int] + WSAESTALE: Final[int] + WSAETIMEDOUT: Final[int] + WSAETOOMANYREFS: Final[int] + WSAEUSERS: Final[int] + WSAEWOULDBLOCK: Final[int] + WSANOTINITIALISED: Final[int] + WSASYSNOTREADY: Final[int] + WSAVERNOTSUPPORTED: Final[int] diff --git a/mypy/typeshed/stdlib/faulthandler.pyi b/mypy/typeshed/stdlib/faulthandler.pyi index 320a8b6fad150..33d08995eb759 100644 --- a/mypy/typeshed/stdlib/faulthandler.pyi +++ b/mypy/typeshed/stdlib/faulthandler.pyi @@ -4,8 +4,18 @@ from _typeshed import FileDescriptorLike def cancel_dump_traceback_later() -> None: ... def disable() -> None: ... def dump_traceback(file: FileDescriptorLike = ..., all_threads: bool = ...) -> None: ... + +if sys.version_info >= (3, 14): + def dump_c_stack(file: FileDescriptorLike = ...) -> None: ... + def dump_traceback_later(timeout: float, repeat: bool = ..., file: FileDescriptorLike = ..., exit: bool = ...) -> None: ... -def enable(file: FileDescriptorLike = ..., all_threads: bool = ...) -> None: ... + +if sys.version_info >= (3, 14): + def enable(file: FileDescriptorLike = ..., all_threads: bool = ..., c_stack: bool = True) -> None: ... + +else: + def enable(file: FileDescriptorLike = ..., all_threads: bool = ...) -> None: ... + def is_enabled() -> bool: ... if sys.platform != "win32": diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index 376611f166b85..5a3e89b0c6766 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -4,107 +4,107 @@ from typing import Any, Final, Literal, overload from typing_extensions import Buffer if sys.platform != "win32": - FASYNC: int - FD_CLOEXEC: int - F_DUPFD: int - F_DUPFD_CLOEXEC: int - F_GETFD: int - F_GETFL: int - F_GETLK: int - F_GETOWN: int - F_RDLCK: int - F_SETFD: int - F_SETFL: int - F_SETLK: int - F_SETLKW: int - F_SETOWN: int - F_UNLCK: int - F_WRLCK: int + FASYNC: Final[int] + FD_CLOEXEC: Final[int] + F_DUPFD: Final[int] + F_DUPFD_CLOEXEC: Final[int] + F_GETFD: Final[int] + F_GETFL: Final[int] + F_GETLK: Final[int] + F_GETOWN: Final[int] + F_RDLCK: Final[int] + F_SETFD: Final[int] + F_SETFL: Final[int] + F_SETLK: Final[int] + F_SETLKW: Final[int] + F_SETOWN: Final[int] + F_UNLCK: Final[int] + F_WRLCK: Final[int] - F_GETLEASE: int - F_SETLEASE: int + F_GETLEASE: Final[int] + F_SETLEASE: Final[int] if sys.platform == "darwin": - F_FULLFSYNC: int - F_NOCACHE: int - if sys.version_info >= (3, 9): - F_GETPATH: int + F_FULLFSYNC: Final[int] + F_NOCACHE: Final[int] + F_GETPATH: Final[int] if sys.platform == "linux": - F_SETLKW64: int - F_SETSIG: int - F_SHLCK: int - F_SETLK64: int - F_GETSIG: int - F_NOTIFY: int - F_EXLCK: int - F_GETLK64: int - F_ADD_SEALS: int - F_GET_SEALS: int - F_SEAL_GROW: int - F_SEAL_SEAL: int - F_SEAL_SHRINK: int - F_SEAL_WRITE: int - if sys.version_info >= (3, 9): - F_OFD_GETLK: Final[int] - F_OFD_SETLK: Final[int] - F_OFD_SETLKW: Final[int] + F_SETLKW64: Final[int] + F_SETSIG: Final[int] + F_SHLCK: Final[int] + F_SETLK64: Final[int] + F_GETSIG: Final[int] + F_NOTIFY: Final[int] + F_EXLCK: Final[int] + F_GETLK64: Final[int] + F_ADD_SEALS: Final[int] + F_GET_SEALS: Final[int] + F_SEAL_GROW: Final[int] + F_SEAL_SEAL: Final[int] + F_SEAL_SHRINK: Final[int] + F_SEAL_WRITE: Final[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 + F_GETPIPE_SZ: Final[int] + F_SETPIPE_SZ: Final[int] - DN_ACCESS: int - DN_ATTRIB: int - DN_CREATE: int - DN_DELETE: int - DN_MODIFY: int - DN_MULTISHOT: int - DN_RENAME: int + DN_ACCESS: Final[int] + DN_ATTRIB: Final[int] + DN_CREATE: Final[int] + DN_DELETE: Final[int] + DN_MODIFY: Final[int] + DN_MULTISHOT: Final[int] + DN_RENAME: Final[int] - LOCK_EX: int - LOCK_NB: int - LOCK_SH: int - LOCK_UN: int + LOCK_EX: Final[int] + LOCK_NB: Final[int] + LOCK_SH: Final[int] + LOCK_UN: Final[int] if sys.platform == "linux": - LOCK_MAND: int - LOCK_READ: int - LOCK_RW: int - LOCK_WRITE: int + LOCK_MAND: Final[int] + LOCK_READ: Final[int] + LOCK_RW: Final[int] + LOCK_WRITE: Final[int] - # These are highly problematic, they might be present or not, depends on the specific OS. if sys.platform == "linux": - I_ATMARK: int - I_CANPUT: int - I_CKBAND: int - I_FDINSERT: int - I_FIND: int - I_FLUSH: int - I_FLUSHBAND: int - I_GETBAND: int - I_GETCLTIME: int - I_GETSIG: int - I_GRDOPT: int - I_GWROPT: int - I_LINK: int - I_LIST: int - I_LOOK: int - I_NREAD: int - I_PEEK: int - I_PLINK: int - I_POP: int - I_PUNLINK: int - I_PUSH: int - I_RECVFD: int - I_SENDFD: int - I_SETCLTIME: int - I_SETSIG: int - I_SRDOPT: int - I_STR: int - I_SWROPT: int - I_UNLINK: int + # 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: Final[int] + I_CANPUT: Final[int] + I_CKBAND: Final[int] + I_FDINSERT: Final[int] + I_FIND: Final[int] + I_FLUSH: Final[int] + I_FLUSHBAND: Final[int] + I_GETBAND: Final[int] + I_GETCLTIME: Final[int] + I_GETSIG: Final[int] + I_GRDOPT: Final[int] + I_GWROPT: Final[int] + I_LINK: Final[int] + I_LIST: Final[int] + I_LOOK: Final[int] + I_NREAD: Final[int] + I_PEEK: Final[int] + I_PLINK: Final[int] + I_POP: Final[int] + I_PUNLINK: Final[int] + I_PUSH: Final[int] + I_RECVFD: Final[int] + I_SENDFD: Final[int] + I_SETCLTIME: Final[int] + I_SETSIG: Final[int] + I_SRDOPT: Final[int] + I_STR: Final[int] + I_SWROPT: Final[int] + I_UNLINK: Final[int] if sys.version_info >= (3, 12) and sys.platform == "linux": - FICLONE: int - FICLONERANGE: int + FICLONE: Final[int] + FICLONERANGE: Final[int] if sys.version_info >= (3, 13) and sys.platform == "linux": F_OWNER_TID: Final = 0 diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index cb7b945960773..620cc177a415a 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -1,14 +1,12 @@ import sys from _typeshed import GenericPath, StrOrBytesPath from collections.abc import Callable, Iterable, Sequence +from types import GenericAlias from typing import Any, AnyStr, Final, Generic, Literal -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["clear_cache", "cmp", "dircmp", "cmpfiles", "DEFAULT_IGNORES"] -DEFAULT_IGNORES: list[str] +DEFAULT_IGNORES: Final[list[str]] BUFSIZE: Final = 8192 def cmp(f1: StrOrBytesPath, f2: StrOrBytesPath, shallow: bool | Literal[0, 1] = True) -> bool: ... @@ -62,7 +60,6 @@ class dircmp(Generic[AnyStr]): def phase3(self) -> None: ... def phase4(self) -> None: ... def phase4_closure(self) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def clear_cache() -> None: ... diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index 1e6aa78e26077..eb942bc551770 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -1,13 +1,10 @@ import sys from _typeshed import AnyStr_co, StrOrBytesPath from collections.abc import Callable, Iterable, Iterator -from types import TracebackType -from typing import IO, Any, AnyStr, Literal, Protocol, overload +from types import GenericAlias, TracebackType +from typing import IO, Any, AnyStr, Literal, Protocol, overload, type_check_only from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = [ "input", "close", @@ -28,6 +25,7 @@ if sys.version_info >= (3, 11): else: _TextMode: TypeAlias = Literal["r", "rU", "U"] +@type_check_only class _HasReadlineAndFileno(Protocol[AnyStr_co]): def readline(self) -> AnyStr_co: ... def fileno(self) -> int: ... @@ -199,8 +197,7 @@ class FileInput(Iterator[AnyStr]): def fileno(self) -> int: ... def isfirstline(self) -> bool: ... def isstdin(self) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 10): def hook_compressed( diff --git a/mypy/typeshed/stdlib/fnmatch.pyi b/mypy/typeshed/stdlib/fnmatch.pyi index 7051c999c4305..345c4576497de 100644 --- a/mypy/typeshed/stdlib/fnmatch.pyi +++ b/mypy/typeshed/stdlib/fnmatch.pyi @@ -1,9 +1,15 @@ +import sys from collections.abc import Iterable from typing import AnyStr __all__ = ["filter", "fnmatch", "fnmatchcase", "translate"] +if sys.version_info >= (3, 14): + __all__ += ["filterfalse"] def fnmatch(name: AnyStr, pat: AnyStr) -> bool: ... def fnmatchcase(name: AnyStr, pat: AnyStr) -> bool: ... def filter(names: Iterable[AnyStr], pat: AnyStr) -> list[AnyStr]: ... def translate(pat: str) -> str: ... + +if sys.version_info >= (3, 14): + def filterfalse(names: Iterable[AnyStr], pat: AnyStr) -> list[AnyStr]: ... diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 086aff50344c7..ef4066aa65b52 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -1,30 +1,29 @@ 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 numbers import Rational, Real +from typing import Any, Literal, Protocol, SupportsIndex, overload, type_check_only from typing_extensions import Self, TypeAlias _ComparableNum: TypeAlias = int | float | Decimal | Real -if sys.version_info >= (3, 9): - __all__ = ["Fraction"] -else: - __all__ = ["Fraction", "gcd"] - @overload - def gcd(a: int, b: int) -> int: ... - @overload - def gcd(a: Integral, b: int) -> Integral: ... - @overload - def gcd(a: int, b: Integral) -> Integral: ... - @overload - def gcd(a: Integral, b: Integral) -> Integral: ... +__all__ = ["Fraction"] + +@type_check_only +class _ConvertibleToIntegerRatio(Protocol): + def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ... class Fraction(Rational): + __slots__ = ("_numerator", "_denominator") @overload def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ... @overload - def __new__(cls, value: float | Decimal | str, /) -> Self: ... + def __new__(cls, numerator: float | Decimal | str) -> Self: ... + + if sys.version_info >= (3, 14): + @overload + def __new__(cls, numerator: _ConvertibleToIntegerRatio) -> Self: ... + @classmethod def from_float(cls, f: float) -> Self: ... @classmethod @@ -110,16 +109,31 @@ class Fraction(Rational): def __rdivmod__(a, b: int | Fraction) -> tuple[int, Fraction]: ... @overload def __rdivmod__(a, b: float) -> tuple[float, Fraction]: ... - @overload - def __pow__(a, b: int) -> Fraction: ... - @overload - def __pow__(a, b: float | Fraction) -> float: ... - @overload - def __pow__(a, b: complex) -> complex: ... - @overload - def __rpow__(b, a: float | Fraction) -> float: ... - @overload - def __rpow__(b, a: complex) -> complex: ... + if sys.version_info >= (3, 14): + @overload + def __pow__(a, b: int, modulo: None = None) -> Fraction: ... + @overload + def __pow__(a, b: float | Fraction, modulo: None = None) -> float: ... + @overload + def __pow__(a, b: complex, modulo: None = None) -> complex: ... + else: + @overload + def __pow__(a, b: int) -> Fraction: ... + @overload + def __pow__(a, b: float | Fraction) -> float: ... + @overload + def __pow__(a, b: complex) -> complex: ... + if sys.version_info >= (3, 14): + @overload + def __rpow__(b, a: float | Fraction, modulo: None = None) -> float: ... + @overload + def __rpow__(b, a: complex, modulo: None = None) -> complex: ... + else: + @overload + def __rpow__(b, a: float | Fraction) -> float: ... + @overload + def __rpow__(b, a: complex) -> complex: ... + def __pos__(a) -> Fraction: ... def __neg__(a) -> Fraction: ... def __abs__(a) -> Fraction: ... @@ -130,7 +144,7 @@ class Fraction(Rational): def __round__(self, ndigits: None = None) -> int: ... @overload def __round__(self, ndigits: int) -> Fraction: ... - def __hash__(self) -> int: ... + def __hash__(self) -> int: ... # type: ignore[override] def __eq__(a, b: object) -> bool: ... def __lt__(a, b: _ComparableNum) -> bool: ... def __gt__(a, b: _ComparableNum) -> bool: ... @@ -148,3 +162,6 @@ class Fraction(Rational): @property def imag(self) -> Literal[0]: ... def conjugate(self) -> Fraction: ... + if sys.version_info >= (3, 14): + @classmethod + def from_number(cls, number: float | Rational | _ConvertibleToIntegerRatio) -> Self: ... diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 3693d7c52a26c..44bc2165fe0e3 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -41,29 +41,17 @@ class FTP: self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... source_address: tuple[str, int] | None - if sys.version_info >= (3, 9): - def __init__( - self, - host: str = "", - user: str = "", - passwd: str = "", - acct: str = "", - timeout: float | None = ..., - source_address: tuple[str, int] | None = None, - *, - encoding: str = "utf-8", - ) -> None: ... - else: - def __init__( - self, - host: str = "", - user: str = "", - passwd: str = "", - acct: str = "", - timeout: float | None = ..., - source_address: tuple[str, int] | None = None, - ) -> None: ... - + def __init__( + self, + host: str = "", + user: str = "", + passwd: str = "", + acct: str = "", + timeout: float | None = ..., + source_address: tuple[str, int] | None = None, + *, + encoding: str = "utf-8", + ) -> None: ... def connect( self, host: str = "", port: int = 0, timeout: float = -999, source_address: tuple[str, int] | None = None ) -> str: ... @@ -131,7 +119,7 @@ class FTP_TLS(FTP): source_address: tuple[str, int] | None = None, encoding: str = "utf-8", ) -> None: ... - elif sys.version_info >= (3, 9): + else: def __init__( self, host: str = "", @@ -146,19 +134,6 @@ class FTP_TLS(FTP): *, encoding: str = "utf-8", ) -> None: ... - else: - def __init__( - self, - host: str = "", - user: str = "", - passwd: str = "", - acct: str = "", - keyfile: str | None = None, - certfile: str | None = None, - context: SSLContext | None = None, - timeout: float | None = ..., - source_address: tuple[str, int] | None = None, - ) -> None: ... ssl_version: int keyfile: str | None certfile: str | None diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 9957fa8f16349..47baf917294da 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,12 +1,10 @@ import sys import types from _typeshed import SupportsAllComparisons, SupportsItems -from collections.abc import Callable, Hashable, Iterable, Sequence, Sized -from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload -from typing_extensions import ParamSpec, Self, TypeAlias - -if sys.version_info >= (3, 9): - from types import GenericAlias +from collections.abc import Callable, Hashable, Iterable, Sized +from types import GenericAlias +from typing import Any, Final, Generic, Literal, NamedTuple, TypedDict, TypeVar, final, overload, type_check_only +from typing_extensions import ParamSpec, Self, TypeAlias, disjoint_base __all__ = [ "update_wrapper", @@ -22,11 +20,9 @@ __all__ = [ "singledispatch", "cached_property", "singledispatchmethod", + "cache", ] -if sys.version_info >= (3, 9): - __all__ += ["cache"] - _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _S = TypeVar("_S") @@ -35,10 +31,16 @@ _RWrapped = TypeVar("_RWrapped") _PWrapper = ParamSpec("_PWrapper") _RWrapper = TypeVar("_RWrapper") +if sys.version_info >= (3, 14): + @overload + def reduce(function: Callable[[_T, _S], _T], iterable: Iterable[_S], /, initial: _T) -> _T: ... + +else: + @overload + def reduce(function: Callable[[_T, _S], _T], iterable: Iterable[_S], initial: _T, /) -> _T: ... + @overload -def reduce(function: Callable[[_T, _S], _T], sequence: Iterable[_S], initial: _T, /) -> _T: ... -@overload -def reduce(function: Callable[[_T, _T], _T], sequence: Iterable[_T], /) -> _T: ... +def reduce(function: Callable[[_T, _T], _T], iterable: Iterable[_T], /) -> _T: ... class _CacheInfo(NamedTuple): hits: int @@ -46,10 +48,10 @@ class _CacheInfo(NamedTuple): maxsize: int | None currsize: int -if sys.version_info >= (3, 9): - class _CacheParameters(TypedDict): - maxsize: int - typed: bool +@type_check_only +class _CacheParameters(TypedDict): + maxsize: int + typed: bool @final class _lru_cache_wrapper(Generic[_T]): @@ -57,9 +59,7 @@ class _lru_cache_wrapper(Generic[_T]): def __call__(self, *args: Hashable, **kwargs: Hashable) -> _T: ... def cache_info(self) -> _CacheInfo: ... def cache_clear(self) -> None: ... - if sys.version_info >= (3, 9): - def cache_parameters(self) -> _CacheParameters: ... - + def cache_parameters(self) -> _CacheParameters: ... def __copy__(self) -> _lru_cache_wrapper[_T]: ... def __deepcopy__(self, memo: Any, /) -> _lru_cache_wrapper[_T]: ... @@ -68,21 +68,36 @@ def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Calla @overload def lru_cache(maxsize: Callable[..., _T], typed: bool = False) -> _lru_cache_wrapper[_T]: ... -if sys.version_info >= (3, 12): - WRAPPER_ASSIGNMENTS: tuple[ - Literal["__module__"], - Literal["__name__"], - Literal["__qualname__"], - Literal["__doc__"], - Literal["__annotations__"], - Literal["__type_params__"], +if sys.version_info >= (3, 14): + WRAPPER_ASSIGNMENTS: Final[ + tuple[ + Literal["__module__"], + Literal["__name__"], + Literal["__qualname__"], + Literal["__doc__"], + Literal["__annotate__"], + Literal["__type_params__"], + ] + ] +elif sys.version_info >= (3, 12): + WRAPPER_ASSIGNMENTS: Final[ + tuple[ + Literal["__module__"], + Literal["__name__"], + Literal["__qualname__"], + Literal["__doc__"], + Literal["__annotations__"], + Literal["__type_params__"], + ] ] else: - WRAPPER_ASSIGNMENTS: tuple[ - Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"] + WRAPPER_ASSIGNMENTS: Final[ + tuple[Literal["__module__"], Literal["__name__"], Literal["__qualname__"], Literal["__doc__"], Literal["__annotations__"]] ] -WRAPPER_UPDATES: tuple[Literal["__dict__"]] +WRAPPER_UPDATES: Final[tuple[Literal["__dict__"]]] + +@type_check_only class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): __wrapped__: Callable[_PWrapped, _RWrapped] def __call__(self, *args: _PWrapper.args, **kwargs: _PWrapper.kwargs) -> _RWrapper: ... @@ -90,38 +105,52 @@ class _Wrapped(Generic[_PWrapped, _RWrapped, _PWrapper, _RWrapper]): __name__: str __qualname__: str +@type_check_only class _Wrapper(Generic[_PWrapped, _RWrapped]): def __call__(self, f: Callable[_PWrapper, _RWrapper]) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 14): def update_wrapper( wrapper: Callable[_PWrapper, _RWrapper], wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotate__", "__type_params__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotate__", "__type_params__"), + updated: Iterable[str] = ("__dict__",), + ) -> _Wrapper[_PWrapped, _RWrapped]: ... + +elif sys.version_info >= (3, 12): + def update_wrapper( + wrapper: Callable[_PWrapper, _RWrapper], + wrapped: Callable[_PWrapped, _RWrapped], + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), + updated: Iterable[str] = ("__dict__",), + ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... + def wraps( + wrapped: Callable[_PWrapped, _RWrapped], + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__", "__type_params__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapper[_PWrapped, _RWrapped]: ... else: def update_wrapper( wrapper: Callable[_PWrapper, _RWrapper], wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapped[_PWrapped, _RWrapped, _PWrapper, _RWrapper]: ... def wraps( wrapped: Callable[_PWrapped, _RWrapped], - assigned: Sequence[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), - updated: Sequence[str] = ("__dict__",), + assigned: Iterable[str] = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__"), + updated: Iterable[str] = ("__dict__",), ) -> _Wrapper[_PWrapped, _RWrapped]: ... def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... - +@disjoint_base class partial(Generic[_T]): @property def func(self) -> Callable[..., _T]: ... @@ -131,8 +160,7 @@ class partial(Generic[_T]): def keywords(self) -> dict[str, Any]: ... def __new__(cls, func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> Self: ... def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # With protocols, this could change into a generic protocol that defines __get__ and returns _T _Descriptor: TypeAlias = Any @@ -141,30 +169,42 @@ class partialmethod(Generic[_T]): func: Callable[..., _T] | _Descriptor args: tuple[Any, ...] keywords: dict[str, Any] - @overload - def __init__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> None: ... - @overload - def __init__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> None: ... + if sys.version_info >= (3, 14): + @overload + def __new__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> Self: ... + @overload + def __new__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> Self: ... + else: + @overload + def __init__(self, func: Callable[..., _T], /, *args: Any, **keywords: Any) -> None: ... + @overload + def __init__(self, func: _Descriptor, /, *args: Any, **keywords: Any) -> None: ... + def __get__(self, obj: Any, cls: type[Any] | None = None) -> Callable[..., _T]: ... @property def __isabstractmethod__(self) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +if sys.version_info >= (3, 11): + _RegType: TypeAlias = type[Any] | types.UnionType +else: + _RegType: TypeAlias = type[Any] +@type_check_only class _SingleDispatchCallable(Generic[_T]): registry: types.MappingProxyType[Any, Callable[..., _T]] def dispatch(self, cls: Any) -> Callable[..., _T]: ... # @fun.register(complex) # def _(arg, verbose=False): ... @overload - def register(self, cls: type[Any], func: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... + def register(self, cls: _RegType, func: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... # @fun.register # def _(arg: int, verbose=False): @overload def register(self, cls: Callable[..., _T], func: None = None) -> Callable[..., _T]: ... # fun.register(int, lambda x: x) @overload - def register(self, cls: type[Any], func: Callable[..., _T]) -> Callable[..., _T]: ... + def register(self, cls: _RegType, func: Callable[..., _T]) -> Callable[..., _T]: ... def _clear_cache(self) -> None: ... def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... @@ -177,11 +217,11 @@ class singledispatchmethod(Generic[_T]): @property def __isabstractmethod__(self) -> bool: ... @overload - def register(self, cls: type[Any], method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... + def register(self, cls: _RegType, method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... @overload def register(self, cls: Callable[..., _T], method: None = None) -> Callable[..., _T]: ... @overload - def register(self, cls: type[Any], method: Callable[..., _T]) -> Callable[..., _T]: ... + def register(self, cls: _RegType, method: Callable[..., _T]) -> Callable[..., _T]: ... def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]: ... class cached_property(Generic[_T_co]): @@ -195,12 +235,9 @@ class cached_property(Generic[_T_co]): def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable def __set__(self, instance: object, value: _T_co) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -if sys.version_info >= (3, 9): - def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... +def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ... def _make_key( args: tuple[Hashable, ...], kwds: SupportsItems[Any, Any], @@ -211,3 +248,11 @@ def _make_key( type: Any = ..., len: Callable[[Sized], int] = ..., ) -> Hashable: ... + +if sys.version_info >= (3, 14): + @final + class _PlaceholderType: ... + + Placeholder: Final[_PlaceholderType] + + __all__ += ["Placeholder"] diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi index 9d34e0d6213a3..06fb6b47c2d1d 100644 --- a/mypy/typeshed/stdlib/gc.pyi +++ b/mypy/typeshed/stdlib/gc.pyi @@ -1,4 +1,3 @@ -import sys from collections.abc import Callable from typing import Any, Final, Literal from typing_extensions import TypeAlias @@ -28,10 +27,7 @@ def get_referrers(*objs: Any) -> list[Any]: ... def get_stats() -> list[dict[str, Any]]: ... def get_threshold() -> tuple[int, int, int]: ... def is_tracked(obj: Any, /) -> bool: ... - -if sys.version_info >= (3, 9): - def is_finalized(obj: Any, /) -> bool: ... - +def is_finalized(obj: Any, /) -> bool: ... def isenabled() -> bool: ... def set_debug(flags: int, /) -> None: ... def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ..., /) -> None: ... diff --git a/mypy/typeshed/stdlib/genericpath.pyi b/mypy/typeshed/stdlib/genericpath.pyi index 9d87c48fd5200..3caed77a661ac 100644 --- a/mypy/typeshed/stdlib/genericpath.pyi +++ b/mypy/typeshed/stdlib/genericpath.pyi @@ -2,7 +2,7 @@ import os import sys from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRichComparisonT from collections.abc import Sequence -from typing import Literal, overload +from typing import Literal, NewType, overload from typing_extensions import LiteralString __all__ = [ @@ -17,6 +17,7 @@ __all__ = [ "samefile", "sameopenfile", "samestat", + "ALLOW_MISSING", ] if sys.version_info >= (3, 12): __all__ += ["islink"] @@ -57,3 +58,7 @@ if sys.version_info >= (3, 13): def isjunction(path: StrOrBytesPath) -> bool: ... def isdevdrive(path: StrOrBytesPath) -> bool: ... def lexists(path: StrOrBytesPath) -> bool: ... + +# Added in Python 3.9.23, 3.10.18, 3.11.13, 3.12.11, 3.13.4 +_AllowMissingType = NewType("_AllowMissingType", object) +ALLOW_MISSING: _AllowMissingType diff --git a/mypy/typeshed/stdlib/getopt.pyi b/mypy/typeshed/stdlib/getopt.pyi index bc9d4da4796b2..c15db8122cfcf 100644 --- a/mypy/typeshed/stdlib/getopt.pyi +++ b/mypy/typeshed/stdlib/getopt.pyi @@ -1,7 +1,23 @@ +from collections.abc import Iterable, Sequence +from typing import Protocol, TypeVar, overload, type_check_only + +_StrSequenceT_co = TypeVar("_StrSequenceT_co", covariant=True, bound=Sequence[str]) + +@type_check_only +class _SliceableT(Protocol[_StrSequenceT_co]): + @overload + def __getitem__(self, key: int, /) -> str: ... + @overload + def __getitem__(self, key: slice, /) -> _StrSequenceT_co: ... + __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: _SliceableT[_StrSequenceT_co], shortopts: str, longopts: Iterable[str] | str = [] +) -> tuple[list[tuple[str, str]], _StrSequenceT_co]: ... +def gnu_getopt( + args: Sequence[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/getpass.pyi b/mypy/typeshed/stdlib/getpass.pyi index 6104e0dedfee4..bb3013dfbf393 100644 --- a/mypy/typeshed/stdlib/getpass.pyi +++ b/mypy/typeshed/stdlib/getpass.pyi @@ -1,8 +1,14 @@ +import sys from typing import TextIO __all__ = ["getpass", "getuser", "GetPassWarning"] -def getpass(prompt: str = "Password: ", stream: TextIO | None = None) -> str: ... +if sys.version_info >= (3, 14): + def getpass(prompt: str = "Password: ", stream: TextIO | None = None, *, echo_char: str | None = None) -> str: ... + +else: + def getpass(prompt: str = "Password: ", stream: TextIO | None = None) -> str: ... + def getuser() -> str: ... class GetPassWarning(UserWarning): ... diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi index 4c17c0dc5de42..e9ffd7a4a4a42 100644 --- a/mypy/typeshed/stdlib/gettext.pyi +++ b/mypy/typeshed/stdlib/gettext.pyi @@ -2,7 +2,8 @@ import io import sys from _typeshed import StrPath from collections.abc import Callable, Container, Iterable, Sequence -from typing import Any, Final, Literal, Protocol, TypeVar, overload +from typing import Any, Final, Literal, Protocol, TypeVar, overload, type_check_only +from typing_extensions import deprecated __all__ = [ "NullTranslations", @@ -26,6 +27,7 @@ __all__ = [ if sys.version_info < (3, 11): __all__ += ["bind_textdomain_codeset", "ldgettext", "ldngettext", "lgettext", "lngettext"] +@type_check_only class _TranslationsReader(Protocol): def read(self) -> bytes: ... # optional: @@ -42,9 +44,13 @@ class NullTranslations: def info(self) -> dict[str, str]: ... def charset(self) -> str | None: ... if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.8; removed in Python 3.11.") def output_charset(self) -> str | None: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11.") def set_output_charset(self, charset: str) -> None: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `gettext()` instead.") def lgettext(self, message: str) -> str: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `ngettext()` instead.") def lngettext(self, msgid1: str, msgid2: str, n: int) -> str: ... def install(self, names: Container[str] | None = None) -> None: ... @@ -114,7 +120,7 @@ else: languages: Iterable[str] | None = None, class_: None = None, fallback: Literal[False] = False, - codeset: str | None = None, + codeset: str | None = ..., ) -> GNUTranslations: ... @overload def translation( @@ -124,7 +130,7 @@ else: *, class_: Callable[[io.BufferedReader], _NullTranslationsT], fallback: Literal[False] = False, - codeset: str | None = None, + codeset: str | None = ..., ) -> _NullTranslationsT: ... @overload def translation( @@ -133,7 +139,7 @@ else: languages: Iterable[str] | None, class_: Callable[[io.BufferedReader], _NullTranslationsT], fallback: Literal[False] = False, - codeset: str | None = None, + codeset: str | None = ..., ) -> _NullTranslationsT: ... @overload def translation( @@ -142,10 +148,17 @@ else: languages: Iterable[str] | None = None, class_: Callable[[io.BufferedReader], NullTranslations] | None = None, fallback: bool = False, - codeset: str | None = None, + codeset: str | None = ..., ) -> NullTranslations: ... + @overload + def install(domain: str, localedir: StrPath | None = None, names: Container[str] | None = None) -> None: ... + @overload + @deprecated("The `codeset` parameter is deprecated since Python 3.8; removed in Python 3.11.") + def install(domain: str, localedir: StrPath | None, codeset: str | None, /, names: Container[str] | None = None) -> None: ... + @overload + @deprecated("The `codeset` parameter is deprecated since Python 3.8; removed in Python 3.11.") def install( - domain: str, localedir: StrPath | None = None, codeset: str | None = None, names: Container[str] | None = None + domain: str, localedir: StrPath | None = None, *, codeset: str | None, names: Container[str] | None = None ) -> None: ... def textdomain(domain: str | None = None) -> str: ... @@ -160,10 +173,17 @@ def npgettext(context: str, msgid1: str, msgid2: str, n: int) -> str: ... def dnpgettext(domain: str, context: str, msgid1: str, msgid2: str, n: int) -> str: ... if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `gettext()` instead.") def lgettext(message: str) -> str: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `dgettext()` instead.") def ldgettext(domain: str, message: str) -> str: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `ngettext()` instead.") def lngettext(msgid1: str, msgid2: str, n: int) -> str: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `dngettext()` instead.") def ldngettext(domain: str, msgid1: str, msgid2: str, n: int) -> str: ... + @deprecated("Deprecated since Python 3.8; removed in Python 3.11. Use `bindtextdomain()` instead.") 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/glob.pyi b/mypy/typeshed/stdlib/glob.pyi index 03cb5418e2565..942fd73961963 100644 --- a/mypy/typeshed/stdlib/glob.pyi +++ b/mypy/typeshed/stdlib/glob.pyi @@ -2,14 +2,26 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Iterator, Sequence from typing import AnyStr +from typing_extensions import deprecated __all__ = ["escape", "glob", "iglob"] if sys.version_info >= (3, 13): __all__ += ["translate"] -def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... -def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... +if sys.version_info >= (3, 10): + @deprecated( + "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." + ) + def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... + @deprecated( + "Deprecated since Python 3.10; will be removed in Python 3.15. Use `glob.glob()` with the *root_dir* argument instead." + ) + def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... + +else: + def glob0(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... + def glob1(dirname: AnyStr, pattern: AnyStr) -> list[AnyStr]: ... if sys.version_info >= (3, 11): def glob( diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 9b32008dcbf65..b18f76f06e3ee 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -1,10 +1,14 @@ -import _compression import sys import zlib -from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath -from io import FileIO -from typing import Final, Literal, Protocol, TextIO, overload -from typing_extensions import TypeAlias +from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath, WriteableBuffer +from io import FileIO, TextIOWrapper +from typing import Final, Literal, Protocol, overload, type_check_only +from typing_extensions import TypeAlias, deprecated + +if sys.version_info >= (3, 14): + from compression._common._streams import BaseStream, DecompressReader +else: + from _compression import BaseStream, DecompressReader __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] @@ -21,6 +25,7 @@ FEXTRA: Final[int] # actually Literal[4] # undocumented FNAME: Final[int] # actually Literal[8] # undocumented FCOMMENT: Final[int] # actually Literal[16] # undocumented +@type_check_only class _ReadableFileobj(Protocol): def read(self, n: int, /) -> bytes: ... def seek(self, n: int, /) -> object: ... @@ -29,6 +34,7 @@ class _ReadableFileobj(Protocol): # mode: str # def fileno() -> int: ... +@type_check_only class _WritableFileobj(Protocol): def write(self, b: bytes, /) -> object: ... def flush(self) -> object: ... @@ -57,13 +63,13 @@ def open( ) -> GzipFile: ... @overload def open( - filename: StrOrBytesPath, + filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj, mode: _OpenTextMode, compresslevel: int = 9, encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj, @@ -72,7 +78,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> GzipFile | TextIO: ... +) -> GzipFile | TextIOWrapper: ... class _PaddedFile: file: _ReadableFileobj @@ -84,7 +90,7 @@ class _PaddedFile: class BadGzipFile(OSError): ... -class GzipFile(_compression.BaseStream): +class GzipFile(BaseStream): myfileobj: FileIO | None mode: object name: str @@ -137,6 +143,7 @@ class GzipFile(_compression.BaseStream): ) -> None: ... if sys.version_info < (3, 12): @property + @deprecated("Deprecated since Python 2.6; removed in Python 3.12. Use `name` attribute instead.") def filename(self) -> str: ... @property @@ -153,8 +160,17 @@ class GzipFile(_compression.BaseStream): def seek(self, offset: int, whence: int = 0) -> int: ... def readline(self, size: int | None = -1) -> bytes: ... -class _GzipReader(_compression.DecompressReader): + if sys.version_info >= (3, 14): + def readinto(self, b: WriteableBuffer) -> int: ... + def readinto1(self, b: WriteableBuffer) -> int: ... + +class _GzipReader(DecompressReader): def __init__(self, fp: _ReadableFileobj) -> None: ... -def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ... +if sys.version_info >= (3, 14): + def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float = 0) -> bytes: ... + +else: + def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ... + def decompress(data: ReadableBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 93bd986c9d314..924136301b215 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -1,8 +1,26 @@ import sys +from _blake2 import blake2b as blake2b, blake2s as blake2s +from _hashlib import ( + HASH, + _HashObject, + openssl_md5 as md5, + openssl_sha1 as sha1, + 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_sha224 as sha224, + openssl_sha256 as sha256, + openssl_sha384 as sha384, + openssl_sha512 as sha512, + openssl_shake_128 as shake_128, + openssl_shake_256 as shake_256, + 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,120 +66,24 @@ 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: ... - -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: ... +def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> HASH: ... 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): + @type_check_only class _BytesIOLike(Protocol): def getbuffer(self) -> ReadableBuffer: ... + @type_check_only class _FileDigestFileObj(Protocol): def readinto(self, buf: bytearray, /) -> int: ... def readable(self) -> bool: ... def file_digest( - fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], _Hash], /, *, _bufsize: int = 262144 - ) -> _Hash: ... + fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], _HashObject], /, *, _bufsize: int = 262144 + ) -> HASH: ... + +# Legacy typing-only alias +_Hash = HASH diff --git a/mypy/typeshed/stdlib/heapq.pyi b/mypy/typeshed/stdlib/heapq.pyi index 7a3aa8b442a51..220c41f303fba 100644 --- a/mypy/typeshed/stdlib/heapq.pyi +++ b/mypy/typeshed/stdlib/heapq.pyi @@ -1,6 +1,6 @@ from _heapq import * from _typeshed import SupportsRichComparison -from collections.abc import Callable, Iterable +from collections.abc import Callable, Generator, Iterable from typing import Any, Final, TypeVar __all__ = ["heappush", "heappop", "heapify", "heapreplace", "merge", "nlargest", "nsmallest", "heappushpop"] @@ -11,7 +11,7 @@ __about__: Final[str] def merge( *iterables: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None, reverse: bool = False -) -> Iterable[_S]: ... +) -> Generator[_S]: ... def nlargest(n: int, iterable: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None) -> list[_S]: ... def nsmallest(n: int, iterable: Iterable[_S], key: Callable[[_S], SupportsRichComparison] | None = None) -> list[_S]: ... def _heapify_max(heap: list[Any], /) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index ac1372dd1e9c2..070c59b1c166d 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,12 +1,11 @@ +from _hashlib import _HashObject, compare_digest as compare_digest from _typeshed import ReadableBuffer, SizedBuffer from collections.abc import Callable from types import ModuleType -from typing import Any, AnyStr, overload +from typing import 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[[], _HashObject] | ModuleType trans_5C: bytes trans_36: bytes @@ -21,6 +20,7 @@ def new(key: bytes | bytearray, msg: ReadableBuffer | None, digestmod: _DigestMo def new(key: bytes | bytearray, *, digestmod: _DigestMod) -> HMAC: ... class HMAC: + __slots__ = ("_hmac", "_inner", "_outer", "block_size", "digest_size") digest_size: int block_size: int @property @@ -31,8 +31,4 @@ 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: ... diff --git a/mypy/typeshed/stdlib/html/entities.pyi b/mypy/typeshed/stdlib/html/entities.pyi index be83fd1135be2..e5890d1ecfbd8 100644 --- a/mypy/typeshed/stdlib/html/entities.pyi +++ b/mypy/typeshed/stdlib/html/entities.pyi @@ -1,6 +1,8 @@ +from typing import Final + __all__ = ["html5", "name2codepoint", "codepoint2name", "entitydefs"] -name2codepoint: dict[str, int] -html5: dict[str, str] -codepoint2name: dict[int, str] -entitydefs: dict[str, str] +name2codepoint: Final[dict[str, int]] +html5: Final[dict[str, str]] +codepoint2name: Final[dict[int, str]] +entitydefs: Final[dict[str, str]] diff --git a/mypy/typeshed/stdlib/html/parser.pyi b/mypy/typeshed/stdlib/html/parser.pyi index d322ade965d94..08dc7b9369228 100644 --- a/mypy/typeshed/stdlib/html/parser.pyi +++ b/mypy/typeshed/stdlib/html/parser.pyi @@ -1,10 +1,16 @@ from _markupbase import ParserBase from re import Pattern +from typing import Final __all__ = ["HTMLParser"] class HTMLParser(ParserBase): - def __init__(self, *, convert_charrefs: bool = True) -> None: ... + CDATA_CONTENT_ELEMENTS: Final[tuple[str, ...]] + # Added in Python 3.9.23, 3.10.18, 3.11.13, 3.12.11, 3.13.6 + RCDATA_CONTENT_ELEMENTS: Final[tuple[str, ...]] + + # `scripting` parameter added in Python 3.9.25, 3.10.20, 3.11.15, 3.12.13, 3.13.10, 3.14.1 + def __init__(self, *, convert_charrefs: bool = True, scripting: bool = False) -> None: ... def feed(self, data: str) -> None: ... def close(self) -> None: ... def get_starttag_text(self) -> str | None: ... @@ -17,16 +23,16 @@ class HTMLParser(ParserBase): def handle_comment(self, data: str) -> None: ... def handle_decl(self, decl: str) -> None: ... def handle_pi(self, data: str) -> None: ... - CDATA_CONTENT_ELEMENTS: tuple[str, ...] def check_for_whole_start_tag(self, i: int) -> int: ... # undocumented def clear_cdata_mode(self) -> None: ... # undocumented def goahead(self, end: bool) -> None: ... # undocumented - def parse_bogus_comment(self, i: int, report: bool = ...) -> int: ... # undocumented + def parse_bogus_comment(self, i: int, report: bool = True) -> int: ... # undocumented def parse_endtag(self, i: int) -> int: ... # undocumented def parse_html_declaration(self, i: int) -> int: ... # undocumented def parse_pi(self, i: int) -> int: ... # undocumented def parse_starttag(self, i: int) -> int: ... # undocumented - def set_cdata_mode(self, elem: str) -> None: ... # undocumented + # `escapable` parameter added in Python 3.9.23, 3.10.18, 3.11.13, 3.12.11, 3.13.6 + def set_cdata_mode(self, elem: str, *, escapable: bool = False) -> None: ... # undocumented rawdata: str # undocumented cdata_elem: str | None # undocumented convert_charrefs: bool # undocumented diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index d455283948d19..f60c3909736d3 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -14,9 +14,13 @@ class HTTPStatus(IntEnum): def phrase(self) -> str: ... @property def description(self) -> str: ... + + # Keep these synced with the global constants in http/client.pyi. CONTINUE = 100 SWITCHING_PROTOCOLS = 101 PROCESSING = 102 + EARLY_HINTS = 103 + OK = 200 CREATED = 201 ACCEPTED = 202 @@ -27,6 +31,7 @@ class HTTPStatus(IntEnum): MULTI_STATUS = 207 ALREADY_REPORTED = 208 IM_USED = 226 + MULTIPLE_CHOICES = 300 MOVED_PERMANENTLY = 301 FOUND = 302 @@ -35,6 +40,7 @@ class HTTPStatus(IntEnum): USE_PROXY = 305 TEMPORARY_REDIRECT = 307 PERMANENT_REDIRECT = 308 + BAD_REQUEST = 400 UNAUTHORIZED = 401 PAYMENT_REQUIRED = 402 @@ -59,15 +65,20 @@ class HTTPStatus(IntEnum): RANGE_NOT_SATISFIABLE = 416 REQUESTED_RANGE_NOT_SATISFIABLE = 416 EXPECTATION_FAILED = 417 + IM_A_TEAPOT = 418 + MISDIRECTED_REQUEST = 421 if sys.version_info >= (3, 13): UNPROCESSABLE_CONTENT = 422 UNPROCESSABLE_ENTITY = 422 LOCKED = 423 FAILED_DEPENDENCY = 424 + TOO_EARLY = 425 UPGRADE_REQUIRED = 426 PRECONDITION_REQUIRED = 428 TOO_MANY_REQUESTS = 429 REQUEST_HEADER_FIELDS_TOO_LARGE = 431 + UNAVAILABLE_FOR_LEGAL_REASONS = 451 + INTERNAL_SERVER_ERROR = 500 NOT_IMPLEMENTED = 501 BAD_GATEWAY = 502 @@ -79,12 +90,7 @@ class HTTPStatus(IntEnum): LOOP_DETECTED = 508 NOT_EXTENDED = 510 NETWORK_AUTHENTICATION_REQUIRED = 511 - MISDIRECTED_REQUEST = 421 - UNAVAILABLE_FOR_LEGAL_REASONS = 451 - if sys.version_info >= (3, 9): - EARLY_HINTS = 103 - IM_A_TEAPOT = 418 - TOO_EARLY = 425 + if sys.version_info >= (3, 12): @property def is_informational(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index f68d9d0ca7d74..1568567d58541 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -3,10 +3,11 @@ 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 email._policybase import _MessageT from socket import socket -from typing import Any, BinaryIO, TypeVar, overload +from typing import BinaryIO, Final, TypeVar, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -33,68 +34,87 @@ __all__ = [ _DataType: TypeAlias = SupportsRead[bytes] | Iterable[ReadableBuffer] | ReadableBuffer _T = TypeVar("_T") -_MessageT = TypeVar("_MessageT", bound=email.message.Message) - -HTTP_PORT: int -HTTPS_PORT: int - -CONTINUE: int -SWITCHING_PROTOCOLS: int -PROCESSING: int - -OK: int -CREATED: int -ACCEPTED: int -NON_AUTHORITATIVE_INFORMATION: int -NO_CONTENT: int -RESET_CONTENT: int -PARTIAL_CONTENT: int -MULTI_STATUS: int -IM_USED: int - -MULTIPLE_CHOICES: int -MOVED_PERMANENTLY: int -FOUND: int -SEE_OTHER: int -NOT_MODIFIED: int -USE_PROXY: int -TEMPORARY_REDIRECT: int - -BAD_REQUEST: int -UNAUTHORIZED: int -PAYMENT_REQUIRED: int -FORBIDDEN: int -NOT_FOUND: int -METHOD_NOT_ALLOWED: int -NOT_ACCEPTABLE: int -PROXY_AUTHENTICATION_REQUIRED: int -REQUEST_TIMEOUT: int -CONFLICT: int -GONE: int -LENGTH_REQUIRED: int -PRECONDITION_FAILED: int -REQUEST_ENTITY_TOO_LARGE: int -REQUEST_URI_TOO_LONG: int -UNSUPPORTED_MEDIA_TYPE: int -REQUESTED_RANGE_NOT_SATISFIABLE: int -EXPECTATION_FAILED: int -UNPROCESSABLE_ENTITY: int -LOCKED: int -FAILED_DEPENDENCY: int -UPGRADE_REQUIRED: int -PRECONDITION_REQUIRED: int -TOO_MANY_REQUESTS: int -REQUEST_HEADER_FIELDS_TOO_LARGE: int - -INTERNAL_SERVER_ERROR: int -NOT_IMPLEMENTED: int -BAD_GATEWAY: int -SERVICE_UNAVAILABLE: int -GATEWAY_TIMEOUT: int -HTTP_VERSION_NOT_SUPPORTED: int -INSUFFICIENT_STORAGE: int -NOT_EXTENDED: int -NETWORK_AUTHENTICATION_REQUIRED: int +_HeaderValue: TypeAlias = ReadableBuffer | str | int + +HTTP_PORT: Final = 80 +HTTPS_PORT: Final = 443 + +# Keep these global constants in sync with http.HTTPStatus (http/__init__.pyi). +# They are present for backward compatibility reasons. +CONTINUE: Final = 100 +SWITCHING_PROTOCOLS: Final = 101 +PROCESSING: Final = 102 +EARLY_HINTS: Final = 103 + +OK: Final = 200 +CREATED: Final = 201 +ACCEPTED: Final = 202 +NON_AUTHORITATIVE_INFORMATION: Final = 203 +NO_CONTENT: Final = 204 +RESET_CONTENT: Final = 205 +PARTIAL_CONTENT: Final = 206 +MULTI_STATUS: Final = 207 +ALREADY_REPORTED: Final = 208 +IM_USED: Final = 226 + +MULTIPLE_CHOICES: Final = 300 +MOVED_PERMANENTLY: Final = 301 +FOUND: Final = 302 +SEE_OTHER: Final = 303 +NOT_MODIFIED: Final = 304 +USE_PROXY: Final = 305 +TEMPORARY_REDIRECT: Final = 307 +PERMANENT_REDIRECT: Final = 308 + +BAD_REQUEST: Final = 400 +UNAUTHORIZED: Final = 401 +PAYMENT_REQUIRED: Final = 402 +FORBIDDEN: Final = 403 +NOT_FOUND: Final = 404 +METHOD_NOT_ALLOWED: Final = 405 +NOT_ACCEPTABLE: Final = 406 +PROXY_AUTHENTICATION_REQUIRED: Final = 407 +REQUEST_TIMEOUT: Final = 408 +CONFLICT: Final = 409 +GONE: Final = 410 +LENGTH_REQUIRED: Final = 411 +PRECONDITION_FAILED: Final = 412 +if sys.version_info >= (3, 13): + CONTENT_TOO_LARGE: Final = 413 +REQUEST_ENTITY_TOO_LARGE: Final = 413 +if sys.version_info >= (3, 13): + URI_TOO_LONG: Final = 414 +REQUEST_URI_TOO_LONG: Final = 414 +UNSUPPORTED_MEDIA_TYPE: Final = 415 +if sys.version_info >= (3, 13): + RANGE_NOT_SATISFIABLE: Final = 416 +REQUESTED_RANGE_NOT_SATISFIABLE: Final = 416 +EXPECTATION_FAILED: Final = 417 +IM_A_TEAPOT: Final = 418 +MISDIRECTED_REQUEST: Final = 421 +if sys.version_info >= (3, 13): + UNPROCESSABLE_CONTENT: Final = 422 +UNPROCESSABLE_ENTITY: Final = 422 +LOCKED: Final = 423 +FAILED_DEPENDENCY: Final = 424 +TOO_EARLY: Final = 425 +UPGRADE_REQUIRED: Final = 426 +PRECONDITION_REQUIRED: Final = 428 +TOO_MANY_REQUESTS: Final = 429 +REQUEST_HEADER_FIELDS_TOO_LARGE: Final = 431 +UNAVAILABLE_FOR_LEGAL_REASONS: Final = 451 + +INTERNAL_SERVER_ERROR: Final = 500 +NOT_IMPLEMENTED: Final = 501 +BAD_GATEWAY: Final = 502 +SERVICE_UNAVAILABLE: Final = 503 +GATEWAY_TIMEOUT: Final = 504 +HTTP_VERSION_NOT_SUPPORTED: Final = 505 +VARIANT_ALSO_NEGOTIATES: Final = 506 +INSUFFICIENT_STORAGE: Final = 507 +LOOP_DETECTED: Final = 508 +NOT_EXTENDED: Final = 510 +NETWORK_AUTHENTICATION_REQUIRED: Final = 511 responses: dict[int, str] @@ -146,6 +166,7 @@ class HTTPResponse(io.BufferedIOBase, BinaryIO): # type: ignore[misc] # incomp def begin(self) -> None: ... class HTTPConnection: + blocksize: int auto_open: int # undocumented debuglevel: int default_port: int # undocumented @@ -153,7 +174,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 +188,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 +201,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 56097f163afd3..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 diff --git a/mypy/typeshed/stdlib/http/cookies.pyi b/mypy/typeshed/stdlib/http/cookies.pyi index c4af5256b5d8b..4df12e3125d4b 100644 --- a/mypy/typeshed/stdlib/http/cookies.pyi +++ b/mypy/typeshed/stdlib/http/cookies.pyi @@ -1,11 +1,8 @@ -import sys from collections.abc import Iterable, Mapping +from types import GenericAlias from typing import Any, Generic, TypeVar, overload from typing_extensions import TypeAlias -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["CookieError", "BaseCookie", "SimpleCookie"] _DataType: TypeAlias = str | Mapping[str, str | Morsel[Any]] @@ -44,8 +41,7 @@ class Morsel(dict[str, Any], Generic[_T]): def OutputString(self, attrs: list[str] | None = None) -> str: ... def __eq__(self, morsel: object) -> bool: ... def __setitem__(self, K: str, V: Any) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class BaseCookie(dict[str, Morsel[_T]], Generic[_T]): def __init__(self, input: _DataType | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi index 07cde553c1df6..2c1a374331bcc 100644 --- a/mypy/typeshed/stdlib/http/server.pyi +++ b/mypy/typeshed/stdlib/http/server.pyi @@ -3,11 +3,25 @@ import email.message import io import socketserver import sys -from _typeshed import StrPath, SupportsRead, SupportsWrite -from collections.abc import Mapping, Sequence -from typing import Any, AnyStr, BinaryIO, ClassVar +from _ssl import _PasswordType +from _typeshed import ReadableBuffer, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite +from collections.abc import Callable, Iterable, Mapping, Sequence +from ssl import Purpose, SSLContext +from typing import Any, AnyStr, BinaryIO, ClassVar, Protocol, type_check_only +from typing_extensions import Self, deprecated -__all__ = ["HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler"] +if sys.version_info >= (3, 14): + __all__ = [ + "HTTPServer", + "ThreadingHTTPServer", + "HTTPSServer", + "ThreadingHTTPSServer", + "BaseHTTPRequestHandler", + "SimpleHTTPRequestHandler", + "CGIHTTPRequestHandler", + ] +else: + __all__ = ["HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler"] class HTTPServer(socketserver.TCPServer): server_name: str @@ -15,6 +29,39 @@ class HTTPServer(socketserver.TCPServer): class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): ... +if sys.version_info >= (3, 14): + @type_check_only + class _SSLModule(Protocol): + @staticmethod + def create_default_context( + purpose: Purpose = ..., + *, + cafile: StrOrBytesPath | None = None, + capath: StrOrBytesPath | None = None, + cadata: str | ReadableBuffer | None = None, + ) -> SSLContext: ... + + class HTTPSServer(HTTPServer): + ssl: _SSLModule + certfile: StrOrBytesPath + keyfile: StrOrBytesPath | None + password: _PasswordType | None + alpn_protocols: Iterable[str] + def __init__( + self, + server_address: socketserver._AfInetAddress, + RequestHandlerClass: Callable[[Any, _socket._RetAddress, Self], socketserver.BaseRequestHandler], + bind_and_activate: bool = True, + *, + certfile: StrOrBytesPath, + keyfile: StrOrBytesPath | None = None, + password: _PasswordType | None = None, + alpn_protocols: Iterable[str] | None = None, + ) -> None: ... + def server_activate(self) -> None: ... + + class ThreadingHTTPSServer(socketserver.ThreadingMixIn, HTTPSServer): ... + class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): client_address: tuple[str, int] close_connection: bool @@ -61,7 +108,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): client_address: _socket._RetAddress, server: socketserver.BaseServer, *, - directory: str | None = None, + directory: StrPath | None = None, ) -> None: ... def do_GET(self) -> None: ... def do_HEAD(self) -> None: ... @@ -73,11 +120,23 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def executable(path: StrPath) -> bool: ... # undocumented -class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): - cgi_directories: list[str] - have_fork: bool # undocumented - def do_POST(self) -> None: ... - def is_cgi(self) -> bool: ... # undocumented - def is_executable(self, path: StrPath) -> bool: ... # undocumented - def is_python(self, path: StrPath) -> bool: ... # undocumented - def run_cgi(self) -> None: ... # undocumented +if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): + cgi_directories: list[str] + have_fork: bool # undocumented + def do_POST(self) -> None: ... + def is_cgi(self) -> bool: ... # undocumented + def is_executable(self, path: StrPath) -> bool: ... # undocumented + def is_python(self, path: StrPath) -> bool: ... # undocumented + def run_cgi(self) -> None: ... # undocumented + +else: + class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): + cgi_directories: list[str] + have_fork: bool # undocumented + def do_POST(self) -> None: ... + def is_cgi(self) -> bool: ... # undocumented + def is_executable(self, path: StrPath) -> bool: ... # undocumented + def is_python(self, path: StrPath) -> bool: ... # undocumented + def run_cgi(self) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/imaplib.pyi b/mypy/typeshed/stdlib/imaplib.pyi index 6a4d8b2e720a0..39fd466529bb2 100644 --- a/mypy/typeshed/stdlib/imaplib.pyi +++ b/mypy/typeshed/stdlib/imaplib.pyi @@ -1,16 +1,16 @@ import subprocess import sys import time -from _typeshed import ReadableBuffer, SizedBuffer +from _typeshed import ReadableBuffer, SizedBuffer, Unused from builtins import list as _list # conflicts with a method named "list" -from collections.abc import Callable +from collections.abc import Callable, Generator from datetime import datetime from re import Pattern from socket import socket as _socket from ssl import SSLContext, SSLSocket from types import TracebackType from typing import IO, Any, Literal, SupportsAbs, SupportsInt -from typing_extensions import Self, TypeAlias +from typing_extensions import Self, TypeAlias, deprecated __all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple", "Int2AP", "ParseFlags", "Time2Internaldate", "IMAP4_SSL"] @@ -26,6 +26,7 @@ class IMAP4: class error(Exception): ... class abort(error): ... class readonly(abort): ... + utf8_enabled: bool mustquote: Pattern[str] debug: int state: str @@ -40,18 +41,19 @@ class IMAP4: welcome: bytes capabilities: tuple[str, ...] PROTOCOL_VERSION: str - if sys.version_info >= (3, 9): - def __init__(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ... - def open(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ... + def __init__(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ... + def open(self, host: str = "", port: int = 143, timeout: float | None = None) -> None: ... + if sys.version_info >= (3, 14): + @property + @deprecated("IMAP4.file is unsupported, can cause errors, and may be removed.") + def file(self) -> IO[str] | IO[bytes]: ... else: - def __init__(self, host: str = "", port: int = 143) -> None: ... - def open(self, host: str = "", port: int = 143) -> None: ... + file: IO[str] | IO[bytes] def __getattr__(self, attr: str) -> Any: ... host: str port: int sock: _socket - file: IO[str] | IO[bytes] def read(self, size: int) -> bytes: ... def readline(self) -> bytes: ... def send(self, data: ReadableBuffer) -> None: ... @@ -77,6 +79,9 @@ class IMAP4: def getannotation(self, mailbox: str, entry: str, attribute: str) -> _CommandResults: ... def getquota(self, root: str) -> _CommandResults: ... def getquotaroot(self, mailbox: str) -> _CommandResults: ... + if sys.version_info >= (3, 14): + def idle(self, duration: float | None = None) -> Idler: ... + def list(self, directory: str = '""', pattern: str = "*") -> tuple[str, _AnyResponseData]: ... def login(self, user: str, password: str) -> tuple[Literal["OK"], _list[bytes]]: ... def login_cram_md5(self, user: str, password: str) -> _CommandResults: ... @@ -101,12 +106,19 @@ class IMAP4: def thread(self, threading_algorithm: str, charset: str, *search_criteria: str) -> _CommandResults: ... def uid(self, command: str, *args: str) -> _CommandResults: ... def unsubscribe(self, mailbox: str) -> _CommandResults: ... - if sys.version_info >= (3, 9): - def unselect(self) -> _CommandResults: ... - + def unselect(self) -> _CommandResults: ... def xatom(self, name: str, *args: str) -> _CommandResults: ... def print_log(self) -> None: ... +if sys.version_info >= (3, 14): + class Idler: + def __init__(self, imap: IMAP4, duration: float | None = None) -> None: ... + def __enter__(self) -> Self: ... + def __exit__(self, exc_type: object, exc_val: Unused, exc_tb: Unused) -> Literal[False]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[str, float | None]: ... + def burst(self, interval: float = 0.1) -> Generator[tuple[str, float | None]]: ... + class IMAP4_SSL(IMAP4): if sys.version_info < (3, 12): keyfile: str @@ -115,16 +127,6 @@ class IMAP4_SSL(IMAP4): def __init__( self, host: str = "", port: int = 993, *, ssl_context: SSLContext | None = None, timeout: float | None = None ) -> None: ... - elif sys.version_info >= (3, 9): - def __init__( - self, - host: str = "", - port: int = 993, - keyfile: str | None = None, - certfile: str | None = None, - ssl_context: SSLContext | None = None, - timeout: float | None = None, - ) -> None: ... else: def __init__( self, @@ -133,27 +135,32 @@ class IMAP4_SSL(IMAP4): keyfile: str | None = None, certfile: str | None = None, ssl_context: SSLContext | None = None, + timeout: float | None = None, ) -> None: ... sslobj: SSLSocket - file: IO[Any] - if sys.version_info >= (3, 9): - def open(self, host: str = "", port: int | None = 993, timeout: float | None = None) -> None: ... + if sys.version_info >= (3, 14): + @property + @deprecated("IMAP4_SSL.file is unsupported, can cause errors, and may be removed.") + def file(self) -> IO[Any]: ... else: - def open(self, host: str = "", port: int | None = 993) -> None: ... + file: IO[Any] + def open(self, host: str = "", port: int | None = 993, timeout: float | None = None) -> None: ... def ssl(self) -> SSLSocket: ... class IMAP4_stream(IMAP4): command: str def __init__(self, command: str) -> None: ... - file: IO[Any] + if sys.version_info >= (3, 14): + @property + @deprecated("IMAP4_stream.file is unsupported, can cause errors, and may be removed.") + def file(self) -> IO[Any]: ... + else: + file: IO[Any] process: subprocess.Popen[bytes] writefile: IO[Any] readfile: IO[Any] - if sys.version_info >= (3, 9): - def open(self, host: str | None = None, port: int | None = None, timeout: float | None = None) -> None: ... - else: - def open(self, host: str | None = None, port: int | None = None) -> None: ... + def open(self, host: str | None = None, port: int | None = None, timeout: float | None = None) -> None: ... class _Authenticator: mech: Callable[[bytes], bytes | bytearray | memoryview | str | None] diff --git a/mypy/typeshed/stdlib/imghdr.pyi b/mypy/typeshed/stdlib/imghdr.pyi index 6e1b858b8f320..e45ca3eb5bdbc 100644 --- a/mypy/typeshed/stdlib/imghdr.pyi +++ b/mypy/typeshed/stdlib/imghdr.pyi @@ -1,9 +1,10 @@ from _typeshed import StrPath from collections.abc import Callable -from typing import Any, BinaryIO, Protocol, overload +from typing import Any, BinaryIO, Protocol, overload, type_check_only __all__ = ["what"] +@type_check_only class _ReadableBinary(Protocol): def tell(self) -> int: ... def read(self, size: int, /) -> bytes: ... diff --git a/mypy/typeshed/stdlib/imp.pyi b/mypy/typeshed/stdlib/imp.pyi index ee5a0cd7bc726..b5b4223aa58e9 100644 --- a/mypy/typeshed/stdlib/imp.pyi +++ b/mypy/typeshed/stdlib/imp.pyi @@ -13,18 +13,18 @@ from _imp import ( from _typeshed import StrPath from os import PathLike from types import TracebackType -from typing import IO, Any, Protocol +from typing import IO, Any, Final, Protocol, type_check_only -SEARCH_ERROR: int -PY_SOURCE: int -PY_COMPILED: int -C_EXTENSION: int -PY_RESOURCE: int -PKG_DIRECTORY: int -C_BUILTIN: int -PY_FROZEN: int -PY_CODERESOURCE: int -IMP_HOOK: int +SEARCH_ERROR: Final = 0 +PY_SOURCE: Final = 1 +PY_COMPILED: Final = 2 +C_EXTENSION: Final = 3 +PY_RESOURCE: Final = 4 +PKG_DIRECTORY: Final = 5 +C_BUILTIN: Final = 6 +PY_FROZEN: Final = 7 +PY_CODERESOURCE: Final = 8 +IMP_HOOK: Final = 9 def new_module(name: str) -> types.ModuleType: ... def get_magic() -> bytes: ... @@ -39,6 +39,7 @@ class NullImporter: # Technically, a text file has to support a slightly different set of operations than a binary file, # but we ignore that here. +@type_check_only class _FileLike(Protocol): closed: bool mode: str diff --git a/mypy/typeshed/stdlib/importlib/__init__.pyi b/mypy/typeshed/stdlib/importlib/__init__.pyi index 8506efc01171f..d60f90adee19c 100644 --- a/mypy/typeshed/stdlib/importlib/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/__init__.pyi @@ -1,23 +1,16 @@ import sys -from collections.abc import Mapping, Sequence +from importlib._bootstrap import __import__ as __import__ from importlib.abc import Loader from types import ModuleType +from typing_extensions import deprecated __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: ... if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `importlib.util.find_spec()` instead.") def find_loader(name: str, path: str | None = None) -> Loader | None: ... def invalidate_caches() -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/_abc.pyi b/mypy/typeshed/stdlib/importlib/_abc.pyi index 1a21b9a72cd85..90ab340219172 100644 --- a/mypy/typeshed/stdlib/importlib/_abc.pyi +++ b/mypy/typeshed/stdlib/importlib/_abc.pyi @@ -2,11 +2,16 @@ import sys import types from abc import ABCMeta from importlib.machinery import ModuleSpec +from typing_extensions import deprecated if sys.version_info >= (3, 10): class Loader(metaclass=ABCMeta): def load_module(self, fullname: str) -> types.ModuleType: ... if sys.version_info < (3, 12): + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "The module spec is now used by the import machinery to generate a module repr." + ) def module_repr(self, module: types.ModuleType) -> str: ... def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... 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 4a0a70d0930da..ef7761f7119b9 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -4,9 +4,11 @@ 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 +from typing_extensions import deprecated if sys.version_info >= (3, 11): __all__ = [ @@ -35,8 +37,10 @@ else: def exec_module(self, module: types.ModuleType) -> None: ... if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use `MetaPathFinder` or `PathEntryFinder` instead.") class Finder(metaclass=ABCMeta): ... +@deprecated("Deprecated since Python 3.7. Use `importlib.resources.abc.TraversableResources` instead.") class ResourceLoader(Loader): @abstractmethod def get_data(self, path: str) -> bytes: ... @@ -49,14 +53,15 @@ class InspectLoader(Loader): def exec_module(self, module: types.ModuleType) -> None: ... @staticmethod def source_to_code( - data: ReadableBuffer | str | _ast.Module | _ast.Expression | _ast.Interactive, path: ReadableBuffer | StrPath = "" + data: ReadableBuffer | str | _ast.Module | _ast.Expression | _ast.Interactive, path: bytes | StrPath = "" ) -> types.CodeType: ... 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 + @deprecated("Deprecated since Python 3.3. Use `importlib.resources.abc.SourceLoader.path_stats` instead.") def path_mtime(self, path: str) -> float: ... def set_data(self, path: str, data: bytes) -> None: ... def get_source(self, fullname: str) -> str | None: ... @@ -67,6 +72,7 @@ if sys.version_info >= (3, 10): # Please keep in sync with _typeshed.importlib.MetaPathFinderProtocol class MetaPathFinder(metaclass=ABCMeta): if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `MetaPathFinder.find_spec()` instead.") def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ... def invalidate_caches(self) -> None: ... @@ -77,7 +83,9 @@ if sys.version_info >= (3, 10): class PathEntryFinder(metaclass=ABCMeta): if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `PathEntryFinder.find_spec()` instead.") def find_module(self, fullname: str) -> Loader | None: ... + @deprecated("Deprecated since Python 3.4; removed in Python 3.12. Use `find_spec()` instead.") def find_loader(self, fullname: str) -> tuple[Loader | None, Sequence[str]]: ... def invalidate_caches(self) -> None: ... @@ -101,30 +109,30 @@ 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: ... 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: ... + def get_filename(self, fullname: str | None = None) -> str: ... + def load_module(self, fullname: str | None = None) -> types.ModuleType: ... -class ResourceReader(metaclass=ABCMeta): - @abstractmethod - def open_resource(self, resource: str) -> IO[bytes]: ... - @abstractmethod - def resource_path(self, resource: str) -> str: ... - if sys.version_info >= (3, 10): +if sys.version_info < (3, 11): + class ResourceReader(metaclass=ABCMeta): @abstractmethod - def is_resource(self, path: str) -> bool: ... - else: + def open_resource(self, resource: str) -> IO[bytes]: ... @abstractmethod - def is_resource(self, name: str) -> bool: ... + def resource_path(self, resource: str) -> str: ... + if sys.version_info >= (3, 10): + @abstractmethod + def is_resource(self, path: str) -> bool: ... + else: + @abstractmethod + def is_resource(self, name: str) -> bool: ... - @abstractmethod - def contents(self) -> Iterator[str]: ... + @abstractmethod + def contents(self) -> Iterator[str]: ... -if sys.version_info >= (3, 9): @runtime_checkable class Traversable(Protocol): @abstractmethod @@ -170,3 +178,10 @@ if sys.version_info >= (3, 9): def resource_path(self, resource: Any) -> str: ... def is_resource(self, path: str) -> bool: ... def contents(self) -> Iterator[str]: ... + +elif sys.version_info < (3, 14): + from importlib.resources.abc import ( + ResourceReader as ResourceReader, + Traversable as Traversable, + TraversableResources as TraversableResources, + ) diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index 586c2b80ab7bd..767046b70a3d1 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -1,179 +1,43 @@ -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 +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, +) -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] +if sys.version_info >= (3, 11): + from importlib._bootstrap_external import NamespaceLoader as NamespaceLoader +if sys.version_info >= (3, 14): + from importlib._bootstrap_external import AppleFrameworkLoader as AppleFrameworkLoader 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: ... - -if sys.version_info >= (3, 11): - import importlib.readers - - 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: ... +if sys.version_info >= (3, 14): + __all__ = [ + "AppleFrameworkLoader", + "BYTECODE_SUFFIXES", + "BuiltinImporter", + "DEBUG_BYTECODE_SUFFIXES", + "EXTENSION_SUFFIXES", + "ExtensionFileLoader", + "FileFinder", + "FrozenImporter", + "ModuleSpec", + "NamespaceLoader", + "OPTIMIZED_BYTECODE_SUFFIXES", + "PathFinder", + "SOURCE_SUFFIXES", + "SourceFileLoader", + "SourcelessFileLoader", + "WindowsRegistryFinder", + "all_suffixes", + ] diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 5e26f89872772..9286e92331c82 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -11,7 +11,7 @@ from os import PathLike from pathlib import Path from re import Pattern from typing import Any, ClassVar, Generic, NamedTuple, TypeVar, overload -from typing_extensions import Self, TypeAlias +from typing_extensions import Self, TypeAlias, deprecated, disjoint_base _T = TypeVar("_T") _KT = TypeVar("_KT") @@ -59,24 +59,21 @@ else: value: str group: str -class EntryPoint(_EntryPointBase): - pattern: ClassVar[Pattern[str]] - if sys.version_info >= (3, 11): +if sys.version_info >= (3, 11): + class EntryPoint(_EntryPointBase): + pattern: ClassVar[Pattern[str]] name: str value: str group: str def __init__(self, name: str, value: str, group: str) -> None: ... - - def load(self) -> Any: ... # Callable[[], Any] or an importable module - @property - def extras(self) -> list[str]: ... - if sys.version_info >= (3, 9): + def load(self) -> Any: ... # Callable[[], Any] or an importable module + @property + def extras(self) -> list[str]: ... @property def module(self) -> str: ... @property def attr(self) -> str: ... - if sys.version_info >= (3, 10): dist: ClassVar[Distribution | None] def matches( self, @@ -88,16 +85,43 @@ class EntryPoint(_EntryPointBase): attr: str = ..., extras: list[str] = ..., ) -> bool: ... # undocumented - - def __hash__(self) -> int: ... - def __eq__(self, other: object) -> bool: ... - if sys.version_info >= (3, 11): + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... def __lt__(self, other: object) -> bool: ... - if sys.version_info < (3, 12): + if sys.version_info < (3, 12): + def __iter__(self) -> Iterator[Any]: ... # result of iter((str, Self)), really + +else: + @disjoint_base + class EntryPoint(_EntryPointBase): + pattern: ClassVar[Pattern[str]] + + def load(self) -> Any: ... # Callable[[], Any] or an importable module + @property + def extras(self) -> list[str]: ... + @property + def module(self) -> str: ... + @property + def attr(self) -> str: ... + if sys.version_info >= (3, 10): + dist: ClassVar[Distribution | None] + def matches( + self, + *, + name: str = ..., + value: str = ..., + group: str = ..., + module: str = ..., + attr: str = ..., + extras: list[str] = ..., + ) -> bool: ... # undocumented + + def __hash__(self) -> int: ... def __iter__(self) -> Iterator[Any]: ... # result of iter((str, Self)), really if sys.version_info >= (3, 12): class EntryPoints(tuple[EntryPoint, ...]): + __slots__ = () def __getitem__(self, name: str) -> EntryPoint: ... # type: ignore[override] def select( self, @@ -115,10 +139,12 @@ if sys.version_info >= (3, 12): def groups(self) -> set[str]: ... elif sys.version_info >= (3, 10): - class DeprecatedList(list[_T]): ... + class DeprecatedList(list[_T]): + __slots__ = () class EntryPoints(DeprecatedList[EntryPoint]): # use as list is deprecated since 3.10 # int argument is deprecated since 3.10 + __slots__ = () def __getitem__(self, name: int | str) -> EntryPoint: ... # type: ignore[override] def select( self, @@ -139,7 +165,9 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12): class Deprecated(Generic[_KT, _VT]): def __getitem__(self, name: _KT) -> _VT: ... @overload - def get(self, name: _KT) -> _VT | None: ... + def get(self, name: _KT, default: None = None) -> _VT | None: ... + @overload + def get(self, name: _KT, default: _VT) -> _VT: ... @overload def get(self, name: _KT, default: _T) -> _VT | _T: ... def __iter__(self) -> Iterator[_KT]: ... @@ -147,6 +175,7 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12): def keys(self) -> dict_keys[_KT, _VT]: ... def values(self) -> dict_values[_KT, _VT]: ... + @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `select` instead.") class SelectableGroups(Deprecated[str, EntryPoints], dict[str, EntryPoints]): # use as dict is deprecated since 3.10 @classmethod def load(cls, eps: Iterable[EntryPoint]) -> Self: ... @@ -228,7 +257,7 @@ class Distribution(_distribution_parent): def name(self) -> str: ... if sys.version_info >= (3, 13): @property - def origin(self) -> types.SimpleNamespace: ... + def origin(self) -> types.SimpleNamespace | None: ... class DistributionFinder(MetaPathFinder): class Context: diff --git a/mypy/typeshed/stdlib/importlib/readers.pyi b/mypy/typeshed/stdlib/importlib/readers.pyi index 41d7af966d58c..4a6c73921535a 100644 --- a/mypy/typeshed/stdlib/importlib/readers.pyi +++ b/mypy/typeshed/stdlib/importlib/readers.pyi @@ -5,16 +5,20 @@ import pathlib import sys import zipfile -from _typeshed import Incomplete, StrPath +from _typeshed import StrPath from collections.abc import Iterable, Iterator from io import BufferedReader from typing import Literal, NoReturn, TypeVar from typing_extensions import Never +if sys.version_info >= (3, 10): + from importlib._bootstrap_external import FileLoader + from zipimport import zipimporter + if sys.version_info >= (3, 11): - import importlib.resources.abc as abc + from importlib.resources import abc else: - import importlib.abc as abc + from importlib import abc if sys.version_info >= (3, 10): if sys.version_info >= (3, 11): @@ -27,14 +31,14 @@ if sys.version_info >= (3, 10): class FileReader(abc.TraversableResources): path: pathlib.Path - def __init__(self, loader) -> None: ... + def __init__(self, loader: FileLoader) -> None: ... def resource_path(self, resource: StrPath) -> str: ... def files(self) -> pathlib.Path: ... class ZipReader(abc.TraversableResources): prefix: str - archive: Incomplete - def __init__(self, loader, module: str) -> None: ... + archive: str + def __init__(self, loader: zipimporter, module: str) -> None: ... def open_resource(self, resource: str) -> BufferedReader: ... def is_resource(self, path: StrPath) -> bool: ... def files(self) -> zipfile.Path: ... @@ -63,6 +67,6 @@ if sys.version_info >= (3, 10): class NamespaceReader(abc.TraversableResources): path: MultiplexedPath - def __init__(self, namespace_path) -> None: ... + def __init__(self, namespace_path: Iterable[str]) -> None: ... def resource_path(self, resource: str) -> str: ... def files(self) -> MultiplexedPath: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi index f82df8c591fad..28adc37da4a42 100644 --- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi @@ -4,21 +4,31 @@ from collections.abc import Iterator from contextlib import AbstractContextManager from pathlib import Path from types import ModuleType -from typing import Any, BinaryIO, TextIO -from typing_extensions import TypeAlias +from typing import Any, BinaryIO, Literal, TextIO +from typing_extensions import TypeAlias, deprecated if sys.version_info >= (3, 11): - from importlib.resources._common import Package as Package + from importlib.resources.abc import Traversable else: - Package: TypeAlias = str | ModuleType - -if sys.version_info >= (3, 9): from importlib.abc import Traversable -__all__ = ["Package", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] +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): - __all__ += ["as_file", "files"] +__all__ = [ + "Package", + "as_file", + "contents", + "files", + "is_resource", + "open_binary", + "open_text", + "path", + "read_binary", + "read_text", +] if sys.version_info >= (3, 10): __all__ += ["ResourceReader"] @@ -31,11 +41,12 @@ if sys.version_info < (3, 11): elif sys.version_info < (3, 13): Resource: TypeAlias = str -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 12): from importlib.resources._common import Anchor as Anchor __all__ += ["Anchor"] +if sys.version_info >= (3, 13): from importlib.resources._functional import ( contents as contents, is_resource as is_resource, @@ -51,20 +62,25 @@ else: 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 path(package: Package, resource: Resource) -> AbstractContextManager[Path, Literal[False]]: ... def is_resource(package: Package, name: str) -> bool: ... - def contents(package: Package) -> Iterator[str]: ... + if sys.version_info >= (3, 11): + @deprecated("Deprecated since Python 3.11. Use `files(anchor).iterdir()`.") + def contents(package: Package) -> Iterator[str]: ... + else: + 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]: ... +else: + def as_file(path: Traversable) -> AbstractContextManager[Path, Literal[False]]: ... if sys.version_info >= (3, 11): from importlib.resources._common import files as files - -elif sys.version_info >= (3, 9): +else: def files(package: Package) -> Traversable: ... -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 11): + from importlib.resources.abc import ResourceReader as ResourceReader +elif sys.version_info >= (3, 10): from importlib.abc import ResourceReader as ResourceReader diff --git a/mypy/typeshed/stdlib/importlib/resources/_common.pyi b/mypy/typeshed/stdlib/importlib/resources/_common.pyi index f04f70f25e230..11a93ca82d8df 100644 --- a/mypy/typeshed/stdlib/importlib/resources/_common.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/_common.pyi @@ -5,9 +5,9 @@ if sys.version_info >= (3, 11): import types from collections.abc import Callable from contextlib import AbstractContextManager - from importlib.abc import ResourceReader, Traversable + from importlib.resources.abc import ResourceReader, Traversable from pathlib import Path - from typing import overload + from typing import Literal, overload from typing_extensions import TypeAlias, deprecated Package: TypeAlias = str | types.ModuleType @@ -16,12 +16,12 @@ if sys.version_info >= (3, 11): Anchor: TypeAlias = Package def package_to_anchor( - func: Callable[[Anchor | None], Traversable] + 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'") + @deprecated("Deprecated since Python 3.12; will be removed in Python 3.15. Use `anchor` parameter instead.") def files(package: Anchor | None = None) -> Traversable: ... else: @@ -39,4 +39,4 @@ if sys.version_info >= (3, 11): def get_package(package: Package) -> types.ModuleType: ... def from_package(package: types.ModuleType) -> Traversable: ... - def as_file(path: Traversable) -> AbstractContextManager[Path]: ... + def as_file(path: Traversable) -> AbstractContextManager[Path, Literal[False]]: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/_functional.pyi b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi index 97e46bdf0a536..71e01bcd3d5ec 100644 --- a/mypy/typeshed/stdlib/importlib/resources/_functional.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi @@ -8,8 +8,8 @@ if sys.version_info >= (3, 13): from importlib.resources._common import Anchor from io import TextIOWrapper from pathlib import Path - from typing import BinaryIO, overload - from typing_extensions import Unpack + from typing import BinaryIO, Literal, overload + from typing_extensions import Unpack, deprecated def open_binary(anchor: Anchor, *path_names: StrPath) -> BinaryIO: ... @overload @@ -25,6 +25,7 @@ if sys.version_info >= (3, 13): ) -> 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 path(anchor: Anchor, *path_names: StrPath) -> AbstractContextManager[Path, Literal[False]]: ... def is_resource(anchor: Anchor, *path_names: StrPath) -> bool: ... + @deprecated("Deprecated since Python 3.11. Use `files(anchor).iterdir()`.") 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..80d92a608604e 100644 --- a/mypy/typeshed/stdlib/importlib/resources/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/abc.pyi @@ -1,12 +1,55 @@ import sys +from abc import ABCMeta, abstractmethod +from collections.abc import Iterator +from io import BufferedReader +from typing import IO, Any, Literal, Protocol, overload, runtime_checkable if sys.version_info >= (3, 11): - # These are all actually defined in this file on 3.11+, - # and re-exported from importlib.abc, - # but it's much less code duplication for typeshed if we pretend that they're still defined - # in importlib.abc on 3.11+, and re-exported from this file - from importlib.abc import ( - ResourceReader as ResourceReader, - Traversable as Traversable, - TraversableResources as TraversableResources, - ) + class ResourceReader(metaclass=ABCMeta): + @abstractmethod + def open_resource(self, resource: str) -> IO[bytes]: ... + @abstractmethod + def resource_path(self, resource: str) -> str: ... + @abstractmethod + def is_resource(self, path: str) -> bool: ... + @abstractmethod + def contents(self) -> Iterator[str]: ... + + @runtime_checkable + class Traversable(Protocol): + @abstractmethod + def is_dir(self) -> bool: ... + @abstractmethod + def is_file(self) -> bool: ... + @abstractmethod + def iterdir(self) -> Iterator[Traversable]: ... + @abstractmethod + def joinpath(self, *descendants: str) -> Traversable: ... + + # The documentation and runtime protocol allows *args, **kwargs arguments, + # but this would mean that all implementers would have to support them, + # which is not the case. + @overload + @abstractmethod + 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]: ... + @property + @abstractmethod + def name(self) -> str: ... + def __truediv__(self, child: str, /) -> Traversable: ... + @abstractmethod + def read_bytes(self) -> bytes: ... + @abstractmethod + def read_text(self, encoding: str | None = None) -> str: ... + + class TraversableResources(ResourceReader): + @abstractmethod + def files(self) -> Traversable: ... + def open_resource(self, resource: str) -> BufferedReader: ... + def resource_path(self, resource: Any) -> str: ... + def is_resource(self, path: str) -> bool: ... + def contents(self) -> Iterator[str]: ... + + __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi index 2492c76d5c6ce..577d3a667eca8 100644 --- a/mypy/typeshed/stdlib/importlib/util.pyi +++ b/mypy/typeshed/stdlib/importlib/util.pyi @@ -1,44 +1,75 @@ -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 typing_extensions import ParamSpec +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 importlib.abc import Loader +from types import TracebackType +from typing import Literal +from typing_extensions import ParamSpec, Self, deprecated _P = ParamSpec("_P") if sys.version_info < (3, 12): + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "`__name__`, `__package__` and `__loader__` are now set automatically." + ) def module_for_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "`__name__`, `__package__` and `__loader__` are now set automatically." + ) def set_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... + @deprecated( + "Deprecated since Python 3.4; removed in Python 3.12. " + "`__name__`, `__package__` and `__loader__` are now set automatically." + ) 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: ... +class LazyLoader(Loader): + def __init__(self, loader: Loader) -> None: ... @classmethod - def factory(cls, loader: importlib.abc.Loader) -> Callable[..., LazyLoader]: ... + def factory(cls, loader: Loader) -> Callable[..., LazyLoader]: ... def exec_module(self, module: types.ModuleType) -> None: ... def source_hash(source_bytes: ReadableBuffer) -> bytes: ... + +if sys.version_info >= (3, 12): + class _incompatible_extension_module_restrictions: + def __init__(self, *, disable_check: bool) -> None: ... + disable_check: bool + old: Literal[-1, 0, 1] # exists only while entered + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + @property + def override(self) -> Literal[-1, 1]: ... # undocumented + +if sys.version_info >= (3, 14): + __all__ = [ + "LazyLoader", + "Loader", + "MAGIC_NUMBER", + "cache_from_source", + "decode_source", + "find_spec", + "module_from_spec", + "resolve_name", + "source_from_cache", + "source_hash", + "spec_from_file_location", + "spec_from_loader", + ] diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 1eb9fc502e125..55ae61617af7e 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -2,7 +2,7 @@ import dis import enum import sys import types -from _typeshed import StrPath +from _typeshed import AnnotationForm, StrPath from collections import OrderedDict from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Generator, Mapping, Sequence, Set as AbstractSet from types import ( @@ -25,8 +25,11 @@ from types import ( TracebackType, WrapperDescriptorType, ) -from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload -from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs +from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload, type_check_only +from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs, deprecated, disjoint_base + +if sys.version_info >= (3, 14): + from annotationlib import Format if sys.version_info >= (3, 11): __all__ = [ @@ -139,12 +142,14 @@ if sys.version_info >= (3, 11): "getasyncgenstate", "BufferFlags", ] + if sys.version_info >= (3, 14): + __all__ += ["CO_HAS_DOCSTRING", "CO_METHOD", "ispackage"] _P = ParamSpec("_P") _T = TypeVar("_T") _F = TypeVar("_F", bound=Callable[..., Any]) -_T_cont = TypeVar("_T_cont", contravariant=True) -_V_cont = TypeVar("_V_cont", contravariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) +_V_contra = TypeVar("_V_contra", contravariant=True) # # Types and members @@ -172,6 +177,9 @@ CO_COROUTINE: Final = 128 CO_ITERABLE_COROUTINE: Final = 256 CO_ASYNC_GENERATOR: Final = 512 TPFLAGS_IS_ABSTRACT: Final = 1048576 +if sys.version_info >= (3, 14): + CO_HAS_DOCSTRING: Final = 67108864 + CO_METHOD: Final = 134217728 modulesbyfile: dict[str, Any] @@ -199,6 +207,11 @@ def getmodulename(path: StrPath) -> str | None: ... def ismodule(object: object) -> TypeIs[ModuleType]: ... def isclass(object: object) -> TypeIs[type[Any]]: ... def ismethod(object: object) -> TypeIs[MethodType]: ... + +if sys.version_info >= (3, 14): + # Not TypeIs because it does not return True for all modules + def ispackage(object: object) -> TypeGuard[ModuleType]: ... + def isfunction(object: object) -> TypeIs[FunctionType]: ... if sys.version_info >= (3, 12): @@ -227,12 +240,13 @@ def isasyncgenfunction(obj: Callable[..., AsyncGenerator[Any, Any]]) -> bool: .. def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... @overload def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... +@type_check_only +class _SupportsSet(Protocol[_T_contra, _V_contra]): + def __set__(self, instance: _T_contra, value: _V_contra, /) -> None: ... -class _SupportsSet(Protocol[_T_cont, _V_cont]): - def __set__(self, instance: _T_cont, value: _V_cont, /) -> None: ... - -class _SupportsDelete(Protocol[_T_cont]): - def __delete__(self, instance: _T_cont, /) -> None: ... +@type_check_only +class _SupportsDelete(Protocol[_T_contra]): + def __delete__(self, instance: _T_contra, /) -> None: ... def isasyncgen(object: object) -> TypeIs[AsyncGeneratorType[Any, Any]]: ... def istraceback(object: object) -> TypeIs[TracebackType]: ... @@ -294,7 +308,18 @@ _IntrospectableCallable: TypeAlias = Callable[..., Any] # # Introspecting callables with the Signature object # -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 14): + def signature( + obj: _IntrospectableCallable, + *, + follow_wrapped: bool = True, + globals: Mapping[str, Any] | None = None, + locals: Mapping[str, Any] | None = None, + eval_str: bool = False, + annotation_format: Format = Format.VALUE, # noqa: Y011 + ) -> Signature: ... + +elif sys.version_info >= (3, 10): def signature( obj: _IntrospectableCallable, *, @@ -311,6 +336,7 @@ class _void: ... class _empty: ... class Signature: + __slots__ = ("_return_annotation", "_parameters") def __init__( self, parameters: Sequence[Parameter] | None = None, *, return_annotation: Any = ..., __validate_parameters__: bool = True ) -> None: ... @@ -323,7 +349,19 @@ class Signature: def bind_partial(self, *args: Any, **kwargs: Any) -> BoundArguments: ... def replace(self, *, parameters: Sequence[Parameter] | type[_void] | None = ..., return_annotation: Any = ...) -> Self: ... __replace__ = replace - if sys.version_info >= (3, 10): + if sys.version_info >= (3, 14): + @classmethod + def from_callable( + cls, + obj: _IntrospectableCallable, + *, + follow_wrapped: bool = True, + globals: Mapping[str, Any] | None = None, + locals: Mapping[str, Any] | None = None, + eval_str: bool = False, + annotation_format: Format = Format.VALUE, # noqa: Y011 + ) -> Self: ... + elif sys.version_info >= (3, 10): @classmethod def from_callable( cls, @@ -337,20 +375,24 @@ class Signature: else: @classmethod def from_callable(cls, obj: _IntrospectableCallable, *, follow_wrapped: bool = True) -> Self: ... - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 14): + def format(self, *, max_width: int | None = None, quote_annotation_strings: bool = True) -> str: ... + elif sys.version_info >= (3, 13): def format(self, *, max_width: int | None = None) -> str: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 14): + from annotationlib import get_annotations as get_annotations +elif sys.version_info >= (3, 10): def get_annotations( - obj: Callable[..., object] | type[Any] | ModuleType, + obj: Callable[..., object] | type[object] | ModuleType, # any callable, class, or module *, - globals: Mapping[str, Any] | None = None, - locals: Mapping[str, Any] | None = None, + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key eval_str: bool = False, - ) -> dict[str, Any]: ... + ) -> dict[str, AnnotationForm]: ... # values are type expressions # The name is the same as the enum's name in CPython class _ParameterKind(enum.IntEnum): @@ -370,11 +412,12 @@ if sys.version_info >= (3, 12): AGEN_CLOSED: Final = "AGEN_CLOSED" def getasyncgenstate( - agen: AsyncGenerator[Any, Any] + agen: AsyncGenerator[Any, Any], ) -> Literal["AGEN_CREATED", "AGEN_RUNNING", "AGEN_SUSPENDED", "AGEN_CLOSED"]: ... def getasyncgenlocals(agen: AsyncGeneratorType[Any, Any]) -> dict[str, Any]: ... class Parameter: + __slots__ = ("_name", "_kind", "_default", "_annotation") def __init__(self, name: str, kind: _ParameterKind, *, default: Any = ..., annotation: Any = ...) -> None: ... empty = _empty @@ -406,6 +449,7 @@ class Parameter: def __hash__(self) -> int: ... class BoundArguments: + __slots__ = ("arguments", "_signature", "__weakref__") arguments: OrderedDict[str, Any] @property def args(self) -> tuple[Any, ...]: ... @@ -416,16 +460,16 @@ class BoundArguments: def __init__(self, signature: Signature, arguments: OrderedDict[str, Any]) -> None: ... def apply_defaults(self) -> None: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] # # Classes and functions # -# TODO: The actual return type should be list[_ClassTreeItem] but mypy doesn't -# seem to be supporting this at the moment: -# _ClassTreeItem = list[_ClassTreeItem] | Tuple[type, Tuple[type, ...]] -def getclasstree(classes: list[type], unique: bool = False) -> list[Any]: ... -def walktree(classes: list[type], children: Mapping[type[Any], list[type]], parent: type[Any] | None) -> list[Any]: ... +_ClassTreeItem: TypeAlias = list[tuple[type, ...]] | list[_ClassTreeItem] + +def getclasstree(classes: list[type], unique: bool = False) -> _ClassTreeItem: ... +def walktree(classes: list[type], children: Mapping[type[Any], list[type]], parent: type[Any] | None) -> _ClassTreeItem: ... class Arguments(NamedTuple): args: list[str] @@ -435,12 +479,14 @@ class Arguments(NamedTuple): def getargs(co: CodeType) -> Arguments: ... if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.0; removed in Python 3.11.") class ArgSpec(NamedTuple): args: list[str] varargs: str | None keywords: str | None defaults: tuple[Any, ...] + @deprecated("Deprecated since Python 3.0; removed in Python 3.11. Use `inspect.signature()` instead.") def getargspec(func: object) -> ArgSpec: ... class FullArgSpec(NamedTuple): @@ -461,10 +507,19 @@ class ArgInfo(NamedTuple): locals: dict[str, Any] def getargvalues(frame: FrameType) -> ArgInfo: ... -def formatannotation(annotation: object, base_module: str | None = None) -> str: ... + +if sys.version_info >= (3, 14): + def formatannotation(annotation: object, base_module: str | None = None, *, quote_annotation_strings: bool = True) -> str: ... + +else: + def formatannotation(annotation: object, base_module: str | None = None) -> str: ... + def formatannotationrelativeto(object: object) -> Callable[[object], str]: ... if sys.version_info < (3, 11): + @deprecated( + "Deprecated since Python 3.5; removed in Python 3.11. Use `inspect.signature()` and the `Signature` class instead." + ) def formatargspec( args: list[str], varargs: str | None = None, @@ -515,19 +570,6 @@ if sys.version_info >= (3, 11): code_context: list[str] | None index: int | None # type: ignore[assignment] - class Traceback(_Traceback): - positions: dis.Positions | None - def __new__( - cls, - filename: str, - lineno: int, - function: str, - code_context: list[str] | None, - index: int | None, - *, - positions: dis.Positions | None = None, - ) -> Self: ... - class _FrameInfo(NamedTuple): frame: FrameType filename: str @@ -536,19 +578,63 @@ if sys.version_info >= (3, 11): code_context: list[str] | None index: int | None # type: ignore[assignment] - class FrameInfo(_FrameInfo): - positions: dis.Positions | None - def __new__( - cls, - frame: FrameType, - filename: str, - lineno: int, - function: str, - code_context: list[str] | None, - index: int | None, - *, - positions: dis.Positions | None = None, - ) -> Self: ... + if sys.version_info >= (3, 12): + class Traceback(_Traceback): + positions: dis.Positions | None + def __new__( + cls, + filename: str, + lineno: int, + function: str, + code_context: list[str] | None, + index: int | None, + *, + positions: dis.Positions | None = None, + ) -> Self: ... + + class FrameInfo(_FrameInfo): + positions: dis.Positions | None + def __new__( + cls, + frame: FrameType, + filename: str, + lineno: int, + function: str, + code_context: list[str] | None, + index: int | None, + *, + positions: dis.Positions | None = None, + ) -> Self: ... + + else: + @disjoint_base + class Traceback(_Traceback): + positions: dis.Positions | None + def __new__( + cls, + filename: str, + lineno: int, + function: str, + code_context: list[str] | None, + index: int | None, + *, + positions: dis.Positions | None = None, + ) -> Self: ... + + @disjoint_base + class FrameInfo(_FrameInfo): + positions: dis.Positions | None + def __new__( + cls, + frame: FrameType, + filename: str, + lineno: int, + function: str, + code_context: list[str] | None, + index: int | None, + *, + positions: dis.Positions | None = None, + ) -> Self: ... else: class Traceback(NamedTuple): @@ -590,7 +676,7 @@ GEN_SUSPENDED: Final = "GEN_SUSPENDED" GEN_CLOSED: Final = "GEN_CLOSED" def getgeneratorstate( - generator: Generator[Any, Any, Any] + generator: Generator[Any, Any, Any], ) -> Literal["GEN_CREATED", "GEN_RUNNING", "GEN_SUSPENDED", "GEN_CLOSED"]: ... CORO_CREATED: Final = "CORO_CREATED" @@ -599,7 +685,7 @@ CORO_SUSPENDED: Final = "CORO_SUSPENDED" CORO_CLOSED: Final = "CORO_CLOSED" def getcoroutinestate( - coroutine: Coroutine[Any, Any, Any] + coroutine: Coroutine[Any, Any, Any], ) -> Literal["CORO_CREATED", "CORO_RUNNING", "CORO_SUSPENDED", "CORO_CLOSED"]: ... def getgeneratorlocals(generator: Generator[Any, Any, Any]) -> dict[str, Any]: ... def getcoroutinelocals(coroutine: Coroutine[Any, Any, Any]) -> dict[str, Any]: ... @@ -616,8 +702,7 @@ class Attribute(NamedTuple): def classify_class_attrs(cls: type) -> list[Attribute]: ... -if sys.version_info >= (3, 9): - class ClassFoundException(Exception): ... +class ClassFoundException(Exception): ... if sys.version_info >= (3, 12): class BufferFlags(enum.IntFlag): diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 7607608696dd0..d301d700e9d0f 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, Final, Generic, 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, Protocol, TypeVar __all__ = [ "BlockingIOError", @@ -31,209 +44,32 @@ __all__ = [ "SEEK_END", ] +if sys.version_info >= (3, 14): + __all__ += ["Reader", "Writer"] + if sys.version_info >= (3, 11): - __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] + from _io import text_encoding as text_encoding -_T = TypeVar("_T") + __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] -DEFAULT_BUFFER_SIZE: Final = 8192 +_T_co = TypeVar("_T_co", covariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) SEEK_SET: Final = 0 SEEK_CUR: Final = 1 SEEK_END: Final = 2 -open = builtins.open - -def open_code(path: str) -> IO[bytes]: ... - -BlockingIOError = builtins.BlockingIOError - 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): - 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 - raw: RawIOBase - 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 - raw: RawIOBase - 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) -> 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, 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 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) -> _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: ... - -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): ... + +if sys.version_info >= (3, 14): + class Reader(Protocol[_T_co]): + __slots__ = () + def read(self, size: int = ..., /) -> _T_co: ... + + class Writer(Protocol[_T_contra]): + __slots__ = () + def write(self, data: _T_contra, /) -> int: ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index f51ea87dcfcfe..e2f3defa2deac 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Iterable, Iterator -from typing import Any, Final, Generic, Literal, SupportsInt, TypeVar, overload +from typing import Any, Final, Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias # Undocumented length constants @@ -18,28 +18,28 @@ def ip_network( address: _RawIPAddress | _RawNetworkPart | tuple[_RawIPAddress] | tuple[_RawIPAddress, int], strict: bool = True ) -> IPv4Network | IPv6Network: ... def ip_interface( - address: _RawIPAddress | _RawNetworkPart | tuple[_RawIPAddress] | tuple[_RawIPAddress, int] + address: _RawIPAddress | _RawNetworkPart | tuple[_RawIPAddress] | tuple[_RawIPAddress, int], ) -> IPv4Interface | IPv6Interface: ... class _IPAddressBase: + __slots__ = () @property def compressed(self) -> str: ... @property def exploded(self) -> str: ... @property def reverse_pointer(self) -> str: ... - @property - def version(self) -> int: ... + if sys.version_info < (3, 14): + @property + def version(self) -> int: ... -class _BaseAddress(_IPAddressBase, SupportsInt): - def __init__(self, address: object) -> None: ... +class _BaseAddress(_IPAddressBase): + __slots__ = () def __add__(self, other: int) -> Self: ... def __hash__(self) -> int: ... def __int__(self) -> int: ... def __sub__(self, other: int) -> Self: ... - if sys.version_info >= (3, 9): - def __format__(self, fmt: str) -> str: ... - + def __format__(self, fmt: str) -> str: ... def __eq__(self, other: object) -> bool: ... def __lt__(self, other: Self) -> bool: ... if sys.version_info >= (3, 11): @@ -51,29 +51,9 @@ 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 - def __init__(self, address: object, strict: bool = ...) -> None: ... def __contains__(self, other: Any) -> bool: ... def __getitem__(self, n: int) -> _A: ... def __iter__(self) -> Iterator[_A]: ... @@ -93,7 +73,7 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): @property def broadcast_address(self) -> _A: ... def compare_networks(self, other: Self) -> int: ... - def hosts(self) -> Iterator[_A]: ... + def hosts(self) -> Iterator[_A] | list[_A]: ... @property def is_global(self) -> bool: ... @property @@ -109,8 +89,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,43 +106,89 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): @property def hostmask(self) -> _A: ... -class _BaseInterface(_BaseAddress, Generic[_A, _N]): - hostmask: _A - netmask: _A - network: _N +class _BaseV4: + __slots__ = () + if sys.version_info >= (3, 14): + version: Final = 4 + max_prefixlen: Final = 32 + else: + @property + def version(self) -> Literal[4]: ... + @property + def max_prefixlen(self) -> Literal[32]: ... + +class IPv4Address(_BaseV4, _BaseAddress): + __slots__ = ("_ip", "__weakref__") + def __init__(self, address: object) -> None: ... @property - def ip(self) -> _A: ... + def is_global(self) -> bool: ... @property - def with_hostmask(self) -> str: ... + def is_link_local(self) -> bool: ... @property - def with_netmask(self) -> str: ... + def is_loopback(self) -> bool: ... @property - def with_prefixlen(self) -> str: ... - -class _BaseV4: + def is_multicast(self) -> bool: ... @property - def version(self) -> Literal[4]: ... + def is_private(self) -> bool: ... @property - def max_prefixlen(self) -> Literal[32]: ... - -class IPv4Address(_BaseV4, _BaseAddress): + 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 IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): + def __init__(self, address: object, strict: bool = True) -> None: ... -class IPv4Interface(IPv4Address, _BaseInterface[IPv4Address, IPv4Network]): +class IPv4Interface(IPv4Address): + netmask: IPv4Address + network: IPv4Network def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... - -class _BaseV6: @property - def version(self) -> Literal[6]: ... + def hostmask(self) -> IPv4Address: ... + @property + def ip(self) -> IPv4Address: ... @property - def max_prefixlen(self) -> Literal[128]: ... + def with_hostmask(self) -> str: ... + @property + def with_netmask(self) -> str: ... + @property + def with_prefixlen(self) -> str: ... + +class _BaseV6: + __slots__ = () + if sys.version_info >= (3, 14): + version: Final = 6 + max_prefixlen: Final = 128 + else: + @property + def version(self) -> Literal[6]: ... + @property + def max_prefixlen(self) -> Literal[128]: ... class IPv6Address(_BaseV6, _BaseAddress): + __slots__ = ("_ip", "_scope_id", "__weakref__") + def __init__(self, address: object) -> None: ... + @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 @@ -173,20 +197,31 @@ class IPv6Address(_BaseV6, _BaseAddress): def sixtofour(self) -> IPv4Address | None: ... @property def teredo(self) -> tuple[IPv4Address, IPv4Address] | None: ... - if sys.version_info >= (3, 9): - @property - def scope_id(self) -> str | None: ... - + @property + def scope_id(self) -> str | None: ... def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... class IPv6Network(_BaseV6, _BaseNetwork[IPv6Address]): + def __init__(self, address: object, strict: bool = True) -> None: ... @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 1635b6a0a0729..73745fe92d9eb 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -1,10 +1,9 @@ import sys +from _typeshed import MaybeNone from collections.abc import Callable, Iterable, Iterator +from types import GenericAlias from typing import Any, Generic, Literal, SupportsComplex, SupportsFloat, SupportsIndex, SupportsInt, TypeVar, overload -from typing_extensions import Self, TypeAlias - -if sys.version_info >= (3, 9): - from types import GenericAlias +from typing_extensions import Self, TypeAlias, disjoint_base _T = TypeVar("_T") _S = TypeVar("_S") @@ -28,6 +27,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 +@disjoint_base class count(Iterator[_N]): @overload def __new__(cls) -> count[int]: ... @@ -38,53 +38,60 @@ class count(Iterator[_N]): def __next__(self) -> _N: ... def __iter__(self) -> Self: ... +@disjoint_base class cycle(Iterator[_T]): - def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __new__(cls, iterable: Iterable[_T], /) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... +@disjoint_base class repeat(Iterator[_T]): @overload - def __init__(self, object: _T) -> None: ... + def __new__(cls, object: _T) -> Self: ... @overload - def __init__(self, object: _T, times: int) -> None: ... + def __new__(cls, object: _T, times: int) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... def __length_hint__(self) -> int: ... +@disjoint_base class accumulate(Iterator[_T]): @overload - def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + def __new__(cls, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> Self: ... @overload - def __init__(self, iterable: Iterable[_S], func: Callable[[_T, _S], _T], *, initial: _T | None = ...) -> None: ... + def __new__(cls, iterable: Iterable[_S], func: Callable[[_T, _S], _T], *, initial: _T | None = ...) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... +@disjoint_base class chain(Iterator[_T]): - def __init__(self, *iterables: Iterable[_T]) -> None: ... + def __new__(cls, *iterables: Iterable[_T]) -> Self: ... def __next__(self) -> _T: ... def __iter__(self) -> Self: ... @classmethod # We use type[Any] and not type[_S] to not lose the type inference from __iterable def from_iterable(cls: type[Any], iterable: Iterable[Iterable[_S]], /) -> chain[_S]: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... +@disjoint_base class compress(Iterator[_T]): - def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... + def __new__(cls, data: Iterable[_T], selectors: Iterable[Any]) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... +@disjoint_base class dropwhile(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... +@disjoint_base class filterfalse(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... + def __new__(cls, function: _Predicate[_T] | None, iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... +@disjoint_base 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]: ... @@ -93,26 +100,29 @@ class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... +@disjoint_base class islice(Iterator[_T]): @overload - def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... + def __new__(cls, iterable: Iterable[_T], stop: int | None, /) -> Self: ... @overload - def __init__(self, iterable: Iterable[_T], start: int | None, stop: int | None, step: int | None = ..., /) -> None: ... + def __new__(cls, iterable: Iterable[_T], start: int | None, stop: int | None, step: int | None = ..., /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... +@disjoint_base 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: ... +@disjoint_base class takewhile(Iterator[_T]): - def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __new__(cls, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> _T: ... def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... - +@disjoint_base class zip_longest(Iterator[_T_co]): # one iterable (fillvalue doesn't matter) @overload @@ -122,7 +132,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 +141,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 +150,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 +159,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 +184,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, @@ -191,6 +201,7 @@ class zip_longest(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... +@disjoint_base class product(Iterator[_T_co]): @overload def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... @@ -276,6 +287,7 @@ class product(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... +@disjoint_base class permutations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... @@ -290,6 +302,7 @@ class permutations(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... +@disjoint_base class combinations(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... @@ -304,6 +317,7 @@ class combinations(Iterator[_T_co]): def __iter__(self) -> Self: ... def __next__(self) -> _T_co: ... +@disjoint_base class combinations_with_replacement(Iterator[_T_co]): @overload def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... @@ -319,12 +333,14 @@ class combinations_with_replacement(Iterator[_T_co]): def __next__(self) -> _T_co: ... if sys.version_info >= (3, 10): + @disjoint_base 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): + @disjoint_base 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: ... diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index aa4a3bdf61d4f..83b78666d4a70 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -2,11 +2,11 @@ from collections.abc import Callable, Iterator from re import Pattern from typing import Any, Final -ESCAPE: Final[Pattern[str]] -ESCAPE_ASCII: Final[Pattern[str]] -HAS_UTF8: Final[Pattern[bytes]] -ESCAPE_DCT: Final[dict[str, str]] -INFINITY: Final[float] +ESCAPE: Final[Pattern[str]] # undocumented +ESCAPE_ASCII: Final[Pattern[str]] # undocumented +HAS_UTF8: Final[Pattern[bytes]] # undocumented +ESCAPE_DCT: Final[dict[str, str]] # undocumented +INFINITY: Final[float] # undocumented 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..68b42e92d295e --- /dev/null +++ b/mypy/typeshed/stdlib/json/scanner.pyi @@ -0,0 +1,7 @@ +from _json import make_scanner as make_scanner +from re import Pattern +from typing import Final + +__all__ = ["make_scanner"] + +NUMBER_RE: Final[Pattern[str]] # undocumented diff --git a/mypy/typeshed/stdlib/keyword.pyi b/mypy/typeshed/stdlib/keyword.pyi index 960dfd2fa155a..6b8bdad6beb6a 100644 --- a/mypy/typeshed/stdlib/keyword.pyi +++ b/mypy/typeshed/stdlib/keyword.pyi @@ -1,11 +1,7 @@ -import sys from collections.abc import Sequence from typing import Final -if sys.version_info >= (3, 9): - __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"] -else: - __all__ = ["iskeyword", "kwlist"] +__all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"] def iskeyword(s: str, /) -> bool: ... @@ -13,9 +9,8 @@ def iskeyword(s: str, /) -> bool: ... # type it as a sequence kwlist: Final[Sequence[str]] -if sys.version_info >= (3, 9): - def issoftkeyword(s: str, /) -> bool: ... +def issoftkeyword(s: str, /) -> bool: ... - # a list at runtime, but you're not meant to mutate it; - # type it as a sequence - softkwlist: Final[Sequence[str]] +# a list at runtime, but you're not meant to mutate it; +# type it as a sequence +softkwlist: Final[Sequence[str]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi index bfaa9970c996c..7f4f7f4e8656e 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_tuple_params.pyi @@ -1,4 +1,3 @@ -from _typeshed import Incomplete from typing import ClassVar, Literal from .. import fixer_base @@ -13,5 +12,5 @@ class FixTupleParams(fixer_base.BaseFix): def simplify_args(node): ... def find_params(node): ... -def map_to_index(param_list, prefix=..., d: Incomplete | None = ...): ... +def map_to_index(param_list, prefix=[], d=None): ... def tuple_name(param_list): ... diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi index 6d9f776c61aec..5776d100d1da0 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi @@ -1,6 +1,6 @@ from _typeshed import Incomplete, StrPath from collections.abc import Iterable, Iterator -from typing import IO, NoReturn, overload +from typing import IO, ClassVar, NoReturn, overload from . import grammar from .tokenize import _TokenInfo @@ -46,5 +46,6 @@ class DFAState: def addarc(self, next: DFAState, label: str) -> None: ... def unifystate(self, old: DFAState, new: DFAState) -> None: ... def __eq__(self, other: DFAState) -> bool: ... # type: ignore[override] + __hash__: ClassVar[None] # type: ignore[assignment] def generate_grammar(filename: StrPath = "Grammar.txt") -> PgenGrammar: ... diff --git a/mypy/typeshed/stdlib/lib2to3/pytree.pyi b/mypy/typeshed/stdlib/lib2to3/pytree.pyi index 138333bd58af1..51bdbc75e1421 100644 --- a/mypy/typeshed/stdlib/lib2to3/pytree.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pytree.pyi @@ -1,7 +1,7 @@ from _typeshed import Incomplete, SupportsGetItem, SupportsLenAndGetItem, Unused from abc import abstractmethod from collections.abc import Iterable, Iterator, MutableSequence -from typing import Final +from typing import ClassVar, Final from typing_extensions import Self, TypeAlias from .fixer_base import BaseFix @@ -24,6 +24,7 @@ class Base: was_changed: bool was_checked: bool def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] @abstractmethod def _eq(self, other: Base) -> bool: ... @abstractmethod diff --git a/mypy/typeshed/stdlib/linecache.pyi b/mypy/typeshed/stdlib/linecache.pyi index 2e050e13b621b..5379a21e7d123 100644 --- a/mypy/typeshed/stdlib/linecache.pyi +++ b/mypy/typeshed/stdlib/linecache.pyi @@ -1,12 +1,8 @@ -import sys from collections.abc import Callable from typing import Any from typing_extensions import TypeAlias -if sys.version_info >= (3, 9): - __all__ = ["getline", "clearcache", "checkcache", "lazycache"] -else: - __all__ = ["getline", "clearcache", "checkcache"] +__all__ = ["getline", "clearcache", "checkcache", "lazycache"] _ModuleGlobals: TypeAlias = dict[str, Any] _ModuleMetadata: TypeAlias = tuple[int, float | None, list[str], str] diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi index 58de654495723..80c39a532dc86 100644 --- a/mypy/typeshed/stdlib/locale.pyi +++ b/mypy/typeshed/stdlib/locale.pyi @@ -18,6 +18,7 @@ from builtins import str as _str from collections.abc import Callable, Iterable from decimal import Decimal from typing import Any +from typing_extensions import deprecated if sys.version_info >= (3, 11): from _locale import getencoding as getencoding @@ -137,9 +138,14 @@ def getpreferredencoding(do_setlocale: bool = True) -> _str: ... def normalize(localename: _str) -> _str: ... if sys.version_info < (3, 13): - def resetlocale(category: int = ...) -> None: ... + if sys.version_info >= (3, 11): + @deprecated("Deprecated since Python 3.11; removed in Python 3.13. Use `locale.setlocale(locale.LC_ALL, '')` instead.") + def resetlocale(category: int = ...) -> None: ... + else: + def resetlocale(category: int = ...) -> None: ... if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.7; removed in Python 3.12. Use `locale.format_string()` instead.") def format( percent: _str, value: float | Decimal, grouping: bool = False, monetary: bool = False, *additional: Any ) -> _str: ... @@ -147,6 +153,10 @@ if sys.version_info < (3, 12): def format_string(f: _str, val: Any, grouping: bool = False, monetary: bool = False) -> _str: ... def currency(val: float | Decimal, symbol: bool = True, grouping: bool = False, international: bool = False) -> _str: ... def delocalize(string: _str) -> _str: ... + +if sys.version_info >= (3, 10): + def localize(string: _str, grouping: bool = False, monetary: bool = False) -> _str: ... + def atof(string: _str, func: Callable[[_str], float] = ...) -> float: ... def atoi(string: _str) -> int: ... def str(val: float) -> _str: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 9a4827a8f626f..8248f82ea87ac 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -6,13 +6,10 @@ from io import TextIOWrapper from re import Pattern from string import Template from time import struct_time -from types import FrameType, TracebackType -from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload +from types import FrameType, GenericAlias, TracebackType +from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only from typing_extensions import Self, TypeAlias, deprecated -if sys.version_info >= (3, 11): - from types import GenericAlias - __all__ = [ "BASIC_FORMAT", "BufferingFormatter", @@ -70,11 +67,13 @@ _Level: TypeAlias = int | str _FormatStyle: TypeAlias = Literal["%", "{", "$"] if sys.version_info >= (3, 12): + @type_check_only class _SupportsFilter(Protocol): def filter(self, record: LogRecord, /) -> bool | LogRecord: ... _FilterType: TypeAlias = Filter | Callable[[LogRecord], bool | LogRecord] | _SupportsFilter else: + @type_check_only class _SupportsFilter(Protocol): def filter(self, record: LogRecord, /) -> bool: ... @@ -156,7 +155,7 @@ class Logger(Filterer): stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... - @deprecated("Deprecated; use warning() instead.") + @deprecated("Deprecated since Python 3.3. Use `Logger.warning()` instead.") def warn( self, msg: object, @@ -273,10 +272,7 @@ class Formatter: datefmt: str | None # undocumented _style: PercentStyle # undocumented default_time_format: str - if sys.version_info >= (3, 9): - default_msec_format: str | None - else: - default_msec_format: str + default_msec_format: str | None if sys.version_info >= (3, 10): def __init__( @@ -379,6 +375,9 @@ class LoggerAdapter(Generic[_L]): else: extra: Mapping[str, object] + if sys.version_info >= (3, 13): + merge_extra: bool + def process(self, msg: Any, kwargs: MutableMapping[str, Any]) -> tuple[Any, MutableMapping[str, Any]]: ... def debug( self, @@ -410,7 +409,7 @@ class LoggerAdapter(Generic[_L]): extra: Mapping[str, object] | None = None, **kwargs: object, ) -> None: ... - @deprecated("Deprecated; use warning() instead.") + @deprecated("Deprecated since Python 3.3. Use `LoggerAdapter.warning()` instead.") def warn( self, msg: object, @@ -520,7 +519,7 @@ def warning( stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... -@deprecated("Deprecated; use warning() instead.") +@deprecated("Deprecated since Python 3.3. Use `warning()` instead.") def warn( msg: object, *args: object, @@ -577,37 +576,20 @@ if sys.version_info >= (3, 11): def getLevelNamesMapping() -> dict[str, int]: ... def makeLogRecord(dict: Mapping[str, object]) -> LogRecord: ... - -if sys.version_info >= (3, 9): - def basicConfig( - *, - filename: StrPath | None = ..., - filemode: str = ..., - format: str = ..., - datefmt: str | None = ..., - style: _FormatStyle = ..., - level: _Level | None = ..., - stream: SupportsWrite[str] | None = ..., - handlers: Iterable[Handler] | None = ..., - force: bool | None = ..., - encoding: str | None = ..., - errors: str | None = ..., - ) -> None: ... - -else: - def basicConfig( - *, - filename: StrPath | None = ..., - filemode: str = ..., - format: str = ..., - datefmt: str | None = ..., - style: _FormatStyle = ..., - level: _Level | None = ..., - stream: SupportsWrite[str] | None = ..., - handlers: Iterable[Handler] | None = ..., - force: bool = ..., - ) -> None: ... - +def basicConfig( + *, + filename: StrPath | None = ..., + filemode: str = ..., + format: str = ..., + datefmt: str | None = ..., + style: _FormatStyle = ..., + level: _Level | None = ..., + stream: SupportsWrite[str] | None = ..., + handlers: Iterable[Handler] | None = ..., + force: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., +) -> None: ... def shutdown(handlerList: Sequence[Any] = ...) -> None: ... # handlerList is undocumented def setLoggerClass(klass: type[Logger]) -> None: ... def captureWarnings(capture: bool) -> None: ... @@ -633,14 +615,10 @@ class FileHandler(StreamHandler[TextIOWrapper]): mode: str # undocumented encoding: str | None # undocumented delay: bool # undocumented - if sys.version_info >= (3, 9): - errors: str | None # undocumented - def __init__( - self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None - ) -> None: ... - else: - def __init__(self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False) -> None: ... - + errors: str | None # undocumented + def __init__( + self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None + ) -> None: ... def _open(self) -> TextIOWrapper: ... # undocumented class NullHandler(Handler): ... @@ -681,4 +659,4 @@ class StringTemplateStyle(PercentStyle): # undocumented _STYLES: Final[dict[str, tuple[PercentStyle, str]]] -BASIC_FORMAT: Final[str] +BASIC_FORMAT: Final = "%(levelname)s:%(name)s:%(message)s" diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index 83fe7461cb5c5..72412ddc2cea5 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -1,30 +1,33 @@ 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, Final, Literal, SupportsIndex, TypedDict, overload -from typing_extensions import Required, TypeAlias +from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload, type_check_only +from typing_extensions import Required, TypeAlias, disjoint_base from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level -DEFAULT_LOGGING_CONFIG_PORT: int +DEFAULT_LOGGING_CONFIG_PORT: Final = 9030 RESET_ERROR: Final[int] # undocumented IDENTIFIER: Final[Pattern[str]] # undocumented if sys.version_info >= (3, 11): + @type_check_only class _RootLoggerConfiguration(TypedDict, total=False): level: _Level filters: Sequence[str | _FilterType] handlers: Sequence[str] else: + @type_check_only class _RootLoggerConfiguration(TypedDict, total=False): level: _Level filters: Sequence[str] handlers: Sequence[str] +@type_check_only class _LoggerConfiguration(_RootLoggerConfiguration, TypedDict, total=False): propagate: bool @@ -32,6 +35,7 @@ _FormatterConfigurationTypedDict = TypedDict( "_FormatterConfigurationTypedDict", {"class": str, "format": str, "datefmt": str, "style": _FormatStyle}, total=False ) +@type_check_only class _FilterConfigurationTypedDict(TypedDict): name: str @@ -43,6 +47,7 @@ _FilterConfiguration: TypeAlias = _FilterConfigurationTypedDict | dict[str, Any] # Handler config can have additional keys even when not providing a custom factory so we just use `dict`. _HandlerConfiguration: TypeAlias = dict[str, Any] +@type_check_only class _DictConfigArgs(TypedDict, total=False): version: Required[Literal[1]] formatters: dict[str, _FormatterConfiguration] @@ -63,7 +68,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 +76,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: ... @@ -95,13 +100,22 @@ class ConvertingList(list[Any], ConvertingMixin): # undocumented def __getitem__(self, key: slice) -> Any: ... def pop(self, idx: SupportsIndex = -1) -> Any: ... -class ConvertingTuple(tuple[Any, ...], ConvertingMixin): # undocumented - @overload - def __getitem__(self, key: SupportsIndex) -> Any: ... - @overload - def __getitem__(self, key: slice) -> Any: ... +if sys.version_info >= (3, 12): + class ConvertingTuple(tuple[Any, ...], ConvertingMixin): # undocumented + @overload + def __getitem__(self, key: SupportsIndex) -> Any: ... + @overload + def __getitem__(self, key: slice) -> Any: ... -class BaseConfigurator: # undocumented +else: + @disjoint_base + class ConvertingTuple(tuple[Any, ...], ConvertingMixin): # undocumented + @overload + def __getitem__(self, key: SupportsIndex) -> Any: ... + @overload + def __getitem__(self, key: slice) -> Any: ... + +class BaseConfigurator: CONVERT_PATTERN: Pattern[str] WORD_PATTERN: Pattern[str] DOT_PATTERN: Pattern[str] @@ -110,13 +124,15 @@ class BaseConfigurator: # undocumented value_converters: dict[str, str] importer: Callable[..., Any] + config: dict[str, Any] # undocumented + def __init__(self, config: _DictConfigArgs | dict[str, Any]) -> None: ... def resolve(self, s: str) -> Any: ... def ext_convert(self, value: str) -> Any: ... 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 91f9fe57e46f3..535f1c6851831 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -8,68 +8,50 @@ from logging import FileHandler, Handler, LogRecord from re import Pattern from socket import SocketKind, socket from threading import Thread -from typing import Any, ClassVar, Final, Protocol, TypeVar +from types import TracebackType +from typing import Any, ClassVar, Final, Protocol, TypeVar, type_check_only +from typing_extensions import Self _T = TypeVar("_T") -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] +DEFAULT_TCP_LOGGING_PORT: Final = 9020 +DEFAULT_UDP_LOGGING_PORT: Final = 9021 +DEFAULT_HTTP_LOGGING_PORT: Final = 9022 +DEFAULT_SOAP_LOGGING_PORT: Final = 9023 +SYSLOG_UDP_PORT: Final = 514 +SYSLOG_TCP_PORT: Final = 514 class WatchedFileHandler(FileHandler): dev: int # undocumented ino: int # undocumented - if sys.version_info >= (3, 9): - def __init__( - self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None - ) -> None: ... - else: - def __init__(self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False) -> None: ... - + def __init__( + self, filename: StrPath, mode: str = "a", encoding: str | None = None, delay: bool = False, errors: str | None = None + ) -> None: ... def _statstream(self) -> None: ... # undocumented def reopenIfNeeded(self) -> None: ... class BaseRotatingHandler(FileHandler): namer: Callable[[str], str] | None rotator: Callable[[str, str], None] | None - if sys.version_info >= (3, 9): - def __init__( - self, filename: StrPath, mode: str, encoding: str | None = None, delay: bool = False, errors: str | None = None - ) -> None: ... - else: - def __init__(self, filename: StrPath, mode: str, encoding: str | None = None, delay: bool = False) -> None: ... - + def __init__( + self, filename: StrPath, mode: str, encoding: str | None = None, delay: bool = False, errors: str | None = None + ) -> None: ... def rotation_filename(self, default_name: str) -> str: ... def rotate(self, source: str, dest: str) -> None: ... class RotatingFileHandler(BaseRotatingHandler): maxBytes: int # undocumented backupCount: int # undocumented - if sys.version_info >= (3, 9): - def __init__( - self, - filename: StrPath, - mode: str = "a", - maxBytes: int = 0, - backupCount: int = 0, - encoding: str | None = None, - delay: bool = False, - errors: str | None = None, - ) -> None: ... - else: - def __init__( - self, - filename: StrPath, - mode: str = "a", - maxBytes: int = 0, - backupCount: int = 0, - encoding: str | None = None, - delay: bool = False, - ) -> None: ... - + def __init__( + self, + filename: StrPath, + mode: str = "a", + maxBytes: int = 0, + backupCount: int = 0, + encoding: str | None = None, + delay: bool = False, + errors: str | None = None, + ) -> None: ... def doRollover(self) -> None: ... def shouldRollover(self, record: LogRecord) -> int: ... # undocumented @@ -83,32 +65,18 @@ class TimedRotatingFileHandler(BaseRotatingHandler): dayOfWeek: int # undocumented rolloverAt: int # undocumented extMatch: Pattern[str] # undocumented - if sys.version_info >= (3, 9): - def __init__( - self, - filename: StrPath, - when: str = "h", - interval: int = 1, - backupCount: int = 0, - encoding: str | None = None, - delay: bool = False, - utc: bool = False, - atTime: datetime.time | None = None, - errors: str | None = None, - ) -> None: ... - else: - def __init__( - self, - filename: StrPath, - when: str = "h", - interval: int = 1, - backupCount: int = 0, - encoding: str | None = None, - delay: bool = False, - utc: bool = False, - atTime: datetime.time | None = None, - ) -> None: ... - + def __init__( + self, + filename: StrPath, + when: str = "h", + interval: int = 1, + backupCount: int = 0, + encoding: str | None = None, + delay: bool = False, + utc: bool = False, + atTime: datetime.time | None = None, + errors: str | None = None, + ) -> None: ... def doRollover(self) -> None: ... def shouldRollover(self, record: LogRecord) -> int: ... # undocumented def computeRollover(self, currentTime: int) -> int: ... # undocumented @@ -155,13 +123,10 @@ class SysLogHandler(Handler): LOG_CRON: int LOG_AUTHPRIV: int LOG_FTP: int - - if sys.version_info >= (3, 9): - LOG_NTP: int - LOG_SECURITY: int - LOG_CONSOLE: int - LOG_SOLCRON: int - + LOG_NTP: int + LOG_SECURITY: int + LOG_CONSOLE: int + LOG_SOLCRON: int LOG_LOCAL0: int LOG_LOCAL1: int LOG_LOCAL2: int @@ -179,9 +144,19 @@ class SysLogHandler(Handler): priority_names: ClassVar[dict[str, int]] # undocumented facility_names: ClassVar[dict[str, int]] # undocumented priority_map: ClassVar[dict[str, str]] # undocumented - def __init__( - self, address: tuple[str, int] | str = ("localhost", 514), facility: str | int = 1, socktype: SocketKind | None = None - ) -> None: ... + if sys.version_info >= (3, 14): + timeout: float | None + def __init__( + self, + address: tuple[str, int] | str = ("localhost", 514), + facility: str | int = 1, + socktype: SocketKind | None = None, + timeout: float | None = None, + ) -> None: ... + else: + def __init__( + self, address: tuple[str, int] | str = ("localhost", 514), facility: str | int = 1, socktype: SocketKind | None = None + ) -> None: ... if sys.version_info >= (3, 11): def createSocket(self) -> None: ... @@ -191,7 +166,7 @@ class SysLogHandler(Handler): class NTEventLogHandler(Handler): def __init__(self, appname: str, dllname: str | None = None, logtype: str = "Application") -> None: ... def getEventCategory(self, record: LogRecord) -> int: ... - # TODO correct return value? + # TODO: correct return value? def getEventType(self, record: LogRecord) -> int: ... def getMessageID(self, record: LogRecord) -> int: ... @@ -248,9 +223,9 @@ class HTTPHandler(Handler): context: ssl.SSLContext | None = None, ) -> None: ... def mapLogRecord(self, record: LogRecord) -> dict[str, Any]: ... - if sys.version_info >= (3, 9): - def getConnection(self, host: str, secure: bool) -> http.client.HTTPConnection: ... # undocumented + def getConnection(self, host: str, secure: bool) -> http.client.HTTPConnection: ... # undocumented +@type_check_only class _QueueLike(Protocol[_T]): def get(self) -> _T: ... def put_nowait(self, item: _T, /) -> None: ... @@ -260,6 +235,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 @@ -273,3 +250,9 @@ class QueueListener: def stop(self) -> None: ... def enqueue_sentinel(self) -> None: ... def handle(self, record: LogRecord) -> None: ... + + if sys.version_info >= (3, 14): + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None + ) -> None: ... diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index 2df2b9a8bd6a4..b7ef607b75cbf 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,9 +1,49 @@ -from _compression import BaseStream +import sys +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, Final, Literal, TextIO, final, overload +from io import TextIOWrapper +from typing import IO, Literal, overload from typing_extensions import Self, TypeAlias +if sys.version_info >= (3, 14): + from compression._common._streams import BaseStream +else: + from _compression import BaseStream + __all__ = [ "CHECK_NONE", "CHECK_CRC32", @@ -48,62 +88,6 @@ _OpenTextWritingMode: TypeAlias = Literal["wt", "xt", "at"] _PathOrFile: TypeAlias = StrOrBytesPath | IO[bytes] -_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 - -# 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, @@ -161,7 +145,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: StrOrBytesPath, @@ -174,7 +158,7 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> TextIO: ... +) -> TextIOWrapper: ... @overload def open( filename: _PathOrFile, @@ -187,11 +171,10 @@ def open( encoding: str | None = None, errors: str | None = None, newline: str | None = None, -) -> LZMAFile | TextIO: ... +) -> LZMAFile | TextIOWrapper: ... def compress( data: ReadableBuffer, format: int = 1, check: int = -1, preset: int | None = None, filters: _FilterChain | None = None ) -> bytes: ... 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 a98a00a42853e..89bd998b4dfeb 100644 --- a/mypy/typeshed/stdlib/mailbox.pyi +++ b/mypy/typeshed/stdlib/mailbox.pyi @@ -4,13 +4,11 @@ import sys from _typeshed import StrPath, SupportsNoArgReadline, SupportsRead from abc import ABCMeta, abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Literal, Protocol, TypeVar, overload +from email._policybase import _MessageT +from types import GenericAlias, TracebackType +from typing import IO, Any, AnyStr, Generic, Literal, Protocol, TypeVar, overload, type_check_only from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = [ "Mailbox", "Maildir", @@ -32,15 +30,17 @@ __all__ = [ ] _T = TypeVar("_T") -_MessageT = TypeVar("_MessageT", bound=Message) +@type_check_only class _SupportsReadAndReadline(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ... _MessageData: TypeAlias = email.message.Message | bytes | str | io.StringIO | _SupportsReadAndReadline +@type_check_only class _HasIteritems(Protocol): def iteritems(self) -> Iterator[tuple[str, _MessageData]]: ... +@type_check_only class _HasItems(Protocol): def items(self) -> Iterator[tuple[str, _MessageData]]: ... @@ -101,8 +101,7 @@ class Mailbox(Generic[_MessageT]): def unlock(self) -> None: ... @abstractmethod def close(self) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class Maildir(Mailbox[MaildirMessage]): colon: str @@ -251,8 +250,7 @@ class _ProxyFile(Generic[AnyStr]): def flush(self) -> None: ... @property def closed(self) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class _PartialFile(_ProxyFile[AnyStr]): def __init__(self, f: IO[AnyStr], start: int | None = None, stop: int | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/marshal.pyi b/mypy/typeshed/stdlib/marshal.pyi index 6ab202637ddaa..46c421e4ce307 100644 --- a/mypy/typeshed/stdlib/marshal.pyi +++ b/mypy/typeshed/stdlib/marshal.pyi @@ -2,10 +2,10 @@ import builtins import sys import types from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite -from typing import Any +from typing import Any, Final from typing_extensions import TypeAlias -version: int +version: Final[int] _Marshallable: TypeAlias = ( # handled in w_object() in marshal.c @@ -28,14 +28,22 @@ _Marshallable: TypeAlias = ( | ReadableBuffer ) -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 14): + def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 5, /, *, allow_code: bool = True) -> None: ... + def dumps(value: _Marshallable, version: int = 5, /, *, allow_code: bool = True) -> bytes: ... + +elif sys.version_info >= (3, 13): def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /, *, allow_code: bool = True) -> None: ... - def load(file: SupportsRead[bytes], /, *, allow_code: bool = True) -> Any: ... def dumps(value: _Marshallable, version: int = 4, /, *, allow_code: bool = True) -> bytes: ... - def loads(bytes: ReadableBuffer, /, *, allow_code: bool = True) -> Any: ... else: def dump(value: _Marshallable, file: SupportsWrite[bytes], version: int = 4, /) -> None: ... - def load(file: SupportsRead[bytes], /) -> Any: ... def dumps(value: _Marshallable, version: int = 4, /) -> bytes: ... + +if sys.version_info >= (3, 13): + def load(file: SupportsRead[bytes], /, *, allow_code: bool = True) -> Any: ... + def loads(bytes: ReadableBuffer, /, *, allow_code: bool = True) -> Any: ... + +else: + def load(file: SupportsRead[bytes], /) -> Any: ... def loads(bytes: ReadableBuffer, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index 2bb61e0669b4d..1903d488f7bb3 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -1,6 +1,7 @@ import sys +from _typeshed import SupportsMul, SupportsRMul from collections.abc import Iterable -from typing import Protocol, SupportsFloat, SupportsIndex, TypeVar, overload +from typing import Any, Final, Literal, Protocol, SupportsFloat, SupportsIndex, TypeVar, overload, type_check_only from typing_extensions import TypeAlias _T = TypeVar("_T") @@ -8,11 +9,11 @@ _T_co = TypeVar("_T_co", covariant=True) _SupportsFloatOrIndex: TypeAlias = SupportsFloat | SupportsIndex -e: float -pi: float -inf: float -nan: float -tau: float +e: Final[float] +pi: Final[float] +inf: Final[float] +nan: Final[float] +tau: Final[float] def acos(x: _SupportsFloatOrIndex, /) -> float: ... def acosh(x: _SupportsFloatOrIndex, /) -> float: ... @@ -25,6 +26,7 @@ def atanh(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 11): def cbrt(x: _SupportsFloatOrIndex, /) -> float: ... +@type_check_only class _SupportsCeil(Protocol[_T_co]): def __ceil__(self) -> _T_co: ... @@ -48,7 +50,7 @@ if sys.version_info >= (3, 11): def expm1(x: _SupportsFloatOrIndex, /) -> float: ... def fabs(x: _SupportsFloatOrIndex, /) -> float: ... def factorial(x: SupportsIndex, /) -> int: ... - +@type_check_only class _SupportsFloor(Protocol[_T_co]): def __floor__(self) -> _T_co: ... @@ -60,13 +62,7 @@ def fmod(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... def frexp(x: _SupportsFloatOrIndex, /) -> tuple[float, int]: ... def fsum(seq: Iterable[_SupportsFloatOrIndex], /) -> float: ... def gamma(x: _SupportsFloatOrIndex, /) -> float: ... - -if sys.version_info >= (3, 9): - def gcd(*integers: SupportsIndex) -> int: ... - -else: - def gcd(x: SupportsIndex, y: SupportsIndex, /) -> int: ... - +def gcd(*integers: SupportsIndex) -> int: ... def hypot(*coordinates: _SupportsFloatOrIndex) -> float: ... def isclose( a: _SupportsFloatOrIndex, @@ -79,10 +75,7 @@ def isinf(x: _SupportsFloatOrIndex, /) -> bool: ... def isfinite(x: _SupportsFloatOrIndex, /) -> bool: ... def isnan(x: _SupportsFloatOrIndex, /) -> bool: ... def isqrt(n: SupportsIndex, /) -> int: ... - -if sys.version_info >= (3, 9): - def lcm(*integers: SupportsIndex) -> int: ... - +def lcm(*integers: SupportsIndex) -> int: ... def ldexp(x: _SupportsFloatOrIndex, i: int, /) -> float: ... def lgamma(x: _SupportsFloatOrIndex, /) -> float: ... def log(x: _SupportsFloatOrIndex, base: _SupportsFloatOrIndex = ...) -> float: ... @@ -94,15 +87,35 @@ def modf(x: _SupportsFloatOrIndex, /) -> tuple[float, float]: ... if sys.version_info >= (3, 12): def nextafter(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /, *, steps: SupportsIndex | None = None) -> float: ... -elif sys.version_info >= (3, 9): +else: def nextafter(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... def perm(n: SupportsIndex, k: SupportsIndex | None = None, /) -> int: ... def pow(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... + +_PositiveInteger: TypeAlias = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] +_NegativeInteger: TypeAlias = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20] +_LiteralInteger = _PositiveInteger | _NegativeInteger | Literal[0] # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed + +_MultiplicableT1 = TypeVar("_MultiplicableT1", bound=SupportsMul[Any, Any]) +_MultiplicableT2 = TypeVar("_MultiplicableT2", bound=SupportsMul[Any, Any]) + +@type_check_only +class _SupportsProdWithNoDefaultGiven(SupportsMul[Any, Any], SupportsRMul[int, Any], Protocol): ... + +_SupportsProdNoDefaultT = TypeVar("_SupportsProdNoDefaultT", bound=_SupportsProdWithNoDefaultGiven) + +# This stub is based on the type stub for `builtins.sum`. +# Like `builtins.sum`, it cannot be precisely represented in a type stub +# without introducing many false positives. +# For more details on its limitations and false positives, see #13572. +# Instead, just like `builtins.sum`, we explicitly handle several useful cases. +@overload +def prod(iterable: Iterable[bool | _LiteralInteger], /, *, start: int = 1) -> int: ... # type: ignore[overload-overlap] @overload -def prod(iterable: Iterable[SupportsIndex], /, *, start: SupportsIndex = 1) -> int: ... # type: ignore[overload-overlap] +def prod(iterable: Iterable[_SupportsProdNoDefaultT], /) -> _SupportsProdNoDefaultT | Literal[1]: ... @overload -def prod(iterable: Iterable[_SupportsFloatOrIndex], /, *, start: _SupportsFloatOrIndex = 1) -> float: ... +def prod(iterable: Iterable[_MultiplicableT1], /, *, start: _MultiplicableT2) -> _MultiplicableT1 | _MultiplicableT2: ... def radians(x: _SupportsFloatOrIndex, /) -> float: ... def remainder(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, /) -> float: ... def sin(x: _SupportsFloatOrIndex, /) -> float: ... @@ -116,13 +129,12 @@ def tan(x: _SupportsFloatOrIndex, /) -> float: ... def tanh(x: _SupportsFloatOrIndex, /) -> float: ... # Is different from `_typeshed.SupportsTrunc`, which is not generic +@type_check_only class _SupportsTrunc(Protocol[_T_co]): def __trunc__(self) -> _T_co: ... def trunc(x: _SupportsTrunc[_T], /) -> _T: ... - -if sys.version_info >= (3, 9): - def ulp(x: _SupportsFloatOrIndex, /) -> float: ... +def ulp(x: _SupportsFloatOrIndex, /) -> float: ... if sys.version_info >= (3, 13): def fma(x: _SupportsFloatOrIndex, y: _SupportsFloatOrIndex, z: _SupportsFloatOrIndex, /) -> float: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index a0c150d6e7e84..8a5baba629141 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -1,61 +1,80 @@ +import os import sys from _typeshed import ReadableBuffer, Unused -from collections.abc import Iterable, Iterator, Sized +from collections.abc import Iterator from typing import Final, Literal, NoReturn, overload -from typing_extensions import Self +from typing_extensions import Self, disjoint_base -ACCESS_DEFAULT: int -ACCESS_READ: int -ACCESS_WRITE: int -ACCESS_COPY: int +ACCESS_DEFAULT: Final = 0 +ACCESS_READ: Final = 1 +ACCESS_WRITE: Final = 2 +ACCESS_COPY: Final = 3 -ALLOCATIONGRANULARITY: int +ALLOCATIONGRANULARITY: Final[int] if sys.platform == "linux": - MAP_DENYWRITE: int - MAP_EXECUTABLE: int + MAP_DENYWRITE: Final[int] + MAP_EXECUTABLE: Final[int] if sys.version_info >= (3, 10): - MAP_POPULATE: int + MAP_POPULATE: Final[int] if sys.version_info >= (3, 11) and sys.platform != "win32" and sys.platform != "darwin": - MAP_STACK: int + MAP_STACK: Final[int] if sys.platform != "win32": - MAP_ANON: int - MAP_ANONYMOUS: int - MAP_PRIVATE: int - MAP_SHARED: int - PROT_EXEC: int - PROT_READ: int - PROT_WRITE: int + MAP_ANON: Final[int] + MAP_ANONYMOUS: Final[int] + MAP_PRIVATE: Final[int] + MAP_SHARED: Final[int] + PROT_EXEC: Final[int] + PROT_READ: Final[int] + PROT_WRITE: Final[int] -PAGESIZE: int +PAGESIZE: Final[int] -class mmap(Iterable[int], Sized): +@disjoint_base +class mmap: if sys.platform == "win32": - def __init__(self, fileno: int, length: int, tagname: str | None = ..., access: int = ..., offset: int = ...) -> None: ... + def __new__(self, fileno: int, length: int, tagname: str | None = None, access: int = 0, offset: int = 0) -> Self: ... else: - def __init__( - self, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... - ) -> None: ... + if sys.version_info >= (3, 13): + def __new__( + cls, + fileno: int, + length: int, + flags: int = ..., + prot: int = ..., + access: int = 0, + offset: int = 0, + *, + trackfd: bool = True, + ) -> Self: ... + else: + def __new__( + cls, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = 0, offset: int = 0 + ) -> Self: ... def close(self) -> None: ... - def flush(self, offset: int = ..., size: int = ...) -> None: ... + def flush(self, offset: int = 0, size: int = ...) -> None: ... def move(self, dest: int, src: int, count: int) -> None: ... def read_byte(self) -> int: ... def readline(self) -> bytes: ... def resize(self, newsize: int) -> None: ... - def seek(self, pos: int, whence: int = ...) -> None: ... + if sys.platform != "win32": + def seek(self, pos: int, whence: Literal[0, 1, 2, 3, 4] = os.SEEK_SET) -> None: ... + else: + def seek(self, pos: int, whence: Literal[0, 1, 2] = os.SEEK_SET) -> None: ... + def size(self) -> int: ... def tell(self) -> int: ... def write_byte(self, byte: int) -> None: ... def __len__(self) -> int: ... closed: bool if sys.platform != "win32": - def madvise(self, option: int, start: int = ..., length: int = ...) -> None: ... + def madvise(self, option: int, start: int = 0, length: int = ...) -> None: ... def find(self, sub: ReadableBuffer, start: int = ..., stop: int = ...) -> int: ... def rfind(self, sub: ReadableBuffer, start: int = ..., stop: int = ...) -> int: ... - def read(self, n: int | None = ...) -> bytes: ... + def read(self, n: int | None = None) -> bytes: ... def write(self, bytes: ReadableBuffer) -> int: ... @overload def __getitem__(self, key: int, /) -> int: ... @@ -80,42 +99,42 @@ class mmap(Iterable[int], Sized): def seekable(self) -> Literal[True]: ... if sys.platform != "win32": - MADV_NORMAL: int - MADV_RANDOM: int - MADV_SEQUENTIAL: int - MADV_WILLNEED: int - MADV_DONTNEED: int - MADV_FREE: int + MADV_NORMAL: Final[int] + MADV_RANDOM: Final[int] + MADV_SEQUENTIAL: Final[int] + MADV_WILLNEED: Final[int] + MADV_DONTNEED: Final[int] + MADV_FREE: Final[int] if sys.platform == "linux": - MADV_REMOVE: int - MADV_DONTFORK: int - MADV_DOFORK: int - MADV_HWPOISON: int - MADV_MERGEABLE: int - MADV_UNMERGEABLE: int + MADV_REMOVE: Final[int] + MADV_DONTFORK: Final[int] + MADV_DOFORK: Final[int] + MADV_HWPOISON: Final[int] + MADV_MERGEABLE: Final[int] + MADV_UNMERGEABLE: Final[int] # Seems like this constant is not defined in glibc. # See https://github.com/python/typeshed/pull/5360 for details - # MADV_SOFT_OFFLINE: int - MADV_HUGEPAGE: int - MADV_NOHUGEPAGE: int - MADV_DONTDUMP: int - MADV_DODUMP: int + # MADV_SOFT_OFFLINE: Final[int] + MADV_HUGEPAGE: Final[int] + MADV_NOHUGEPAGE: Final[int] + MADV_DONTDUMP: Final[int] + MADV_DODUMP: Final[int] # This Values are defined for FreeBSD but type checkers do not support conditions for these if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32": - MADV_NOSYNC: int - MADV_AUTOSYNC: int - MADV_NOCORE: int - MADV_CORE: int - MADV_PROTECT: int + MADV_NOSYNC: Final[int] + MADV_AUTOSYNC: Final[int] + MADV_NOCORE: Final[int] + MADV_CORE: Final[int] + MADV_PROTECT: Final[int] if sys.version_info >= (3, 10) and sys.platform == "darwin": - MADV_FREE_REUSABLE: int - MADV_FREE_REUSE: int + MADV_FREE_REUSABLE: Final[int] + MADV_FREE_REUSE: Final[int] if sys.version_info >= (3, 13) and sys.platform != "win32": - MAP_32BIT: Final = 32768 + MAP_32BIT: Final[int] if sys.version_info >= (3, 13) and sys.platform == "darwin": MAP_NORESERVE: Final = 64 diff --git a/mypy/typeshed/stdlib/modulefinder.pyi b/mypy/typeshed/stdlib/modulefinder.pyi index 2cf948ba898a8..6db665a18e691 100644 --- a/mypy/typeshed/stdlib/modulefinder.pyi +++ b/mypy/typeshed/stdlib/modulefinder.pyi @@ -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/msilib/__init__.pyi b/mypy/typeshed/stdlib/msilib/__init__.pyi index 3e43cbc44f520..622f585f5beea 100644 --- a/mypy/typeshed/stdlib/msilib/__init__.pyi +++ b/mypy/typeshed/stdlib/msilib/__init__.pyi @@ -1,26 +1,26 @@ import sys from collections.abc import Container, Iterable, Sequence from types import ModuleType -from typing import Any, Literal +from typing import Any, Final if sys.platform == "win32": from _msi import * from _msi import _Database - AMD64: bool - Win64: bool + AMD64: Final[bool] + Win64: Final[bool] - datasizemask: Literal[0x00FF] - type_valid: Literal[0x0100] - type_localizable: Literal[0x0200] - typemask: Literal[0x0C00] - type_long: Literal[0x0000] - type_short: Literal[0x0400] - type_string: Literal[0x0C00] - type_binary: Literal[0x0800] - type_nullable: Literal[0x1000] - type_key: Literal[0x2000] - knownbits: Literal[0x3FFF] + datasizemask: Final = 0x00FF + type_valid: Final = 0x0100 + type_localizable: Final = 0x0200 + typemask: Final = 0x0C00 + type_long: Final = 0x0000 + type_short: Final = 0x0400 + type_string: Final = 0x0C00 + type_binary: Final = 0x0800 + type_nullable: Final = 0x1000 + type_key: Final = 0x2000 + knownbits: Final = 0x3FFF class Table: name: str diff --git a/mypy/typeshed/stdlib/msilib/schema.pyi b/mypy/typeshed/stdlib/msilib/schema.pyi index 4ad9a1783fcd0..3bbdc41a1e8ec 100644 --- a/mypy/typeshed/stdlib/msilib/schema.pyi +++ b/mypy/typeshed/stdlib/msilib/schema.pyi @@ -1,4 +1,5 @@ import sys +from typing import Final if sys.platform == "win32": from . import Table @@ -89,6 +90,6 @@ if sys.platform == "win32": Upgrade: Table Verb: Table - tables: list[Table] + tables: Final[list[Table]] _Validation_records: list[tuple[str, str, str, int | None, int | None, str | None, int | None, str | None, str | None, str]] diff --git a/mypy/typeshed/stdlib/msilib/sequence.pyi b/mypy/typeshed/stdlib/msilib/sequence.pyi index b8af09f46e65f..a9f5c24717bd3 100644 --- a/mypy/typeshed/stdlib/msilib/sequence.pyi +++ b/mypy/typeshed/stdlib/msilib/sequence.pyi @@ -1,13 +1,14 @@ import sys +from typing import Final from typing_extensions import TypeAlias if sys.platform == "win32": _SequenceType: TypeAlias = list[tuple[str, str | None, int]] - AdminExecuteSequence: _SequenceType - AdminUISequence: _SequenceType - AdvtExecuteSequence: _SequenceType - InstallExecuteSequence: _SequenceType - InstallUISequence: _SequenceType + AdminExecuteSequence: Final[_SequenceType] + AdminUISequence: Final[_SequenceType] + AdvtExecuteSequence: Final[_SequenceType] + InstallExecuteSequence: Final[_SequenceType] + InstallUISequence: Final[_SequenceType] - tables: list[str] + tables: Final[list[str]] diff --git a/mypy/typeshed/stdlib/msilib/text.pyi b/mypy/typeshed/stdlib/msilib/text.pyi index 441c843ca6cfe..da3c5fd0fb7a1 100644 --- a/mypy/typeshed/stdlib/msilib/text.pyi +++ b/mypy/typeshed/stdlib/msilib/text.pyi @@ -1,7 +1,8 @@ import sys +from typing import Final if sys.platform == "win32": - ActionText: list[tuple[str, str, str | None]] - UIText: list[tuple[str, str | None]] + ActionText: Final[list[tuple[str, str, str | None]]] + UIText: Final[list[tuple[str, str | None]]] dirname: str - tables: list[str] + tables: Final[list[str]] diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi index 403a5d9335227..5feca8eab5c1c 100644 --- a/mypy/typeshed/stdlib/msvcrt.pyi +++ b/mypy/typeshed/stdlib/msvcrt.pyi @@ -9,10 +9,10 @@ if sys.platform == "win32": LK_NBLCK: Final = 2 LK_RLCK: Final = 3 LK_NBRLCK: Final = 4 - SEM_FAILCRITICALERRORS: int - SEM_NOALIGNMENTFAULTEXCEPT: int - SEM_NOGPFAULTERRORBOX: int - SEM_NOOPENFILEERRORBOX: int + SEM_FAILCRITICALERRORS: Final = 0x0001 + SEM_NOALIGNMENTFAULTEXCEPT: Final = 0x0004 + SEM_NOGPFAULTERRORBOX: Final = 0x0002 + SEM_NOOPENFILEERRORBOX: Final = 0x8000 def locking(fd: int, mode: int, nbytes: int, /) -> None: ... def setmode(fd: int, mode: int, /) -> int: ... def open_osfhandle(handle: int, flags: int, /) -> int: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 7045a81b85be3..cd4fa102c0f3e 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_contra = TypeVar("_SendT_contra", contravariant=True, default=Any) +_RecvT_co = TypeVar("_RecvT_co", covariant=True, default=Any) + +class _ConnectionBase(Generic[_SendT_contra, _RecvT_co]): 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_contra) -> 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_co: ... 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_contra, _RecvT_co]): ... if sys.platform == "win32": - class PipeConnection(_ConnectionBase): ... + class PipeConnection(_ConnectionBase[_SendT_contra, _RecvT_co]): ... 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_contra, _RecvT_co] | socket.socket | int], timeout: float | None = None +) -> list[Connection[_SendT_contra, _RecvT_co] | 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 31b9828563554..c4af295d23161 100644 --- a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi @@ -1,3 +1,4 @@ +import sys from _typeshed import FileDescriptorLike, Unused from collections.abc import Sequence from struct import Struct @@ -14,13 +15,26 @@ class ForkServer: def connect_to_new_process(self, fds: Sequence[int]) -> tuple[int, int]: ... def ensure_running(self) -> None: ... -def main( - listener_fd: int | None, - alive_r: FileDescriptorLike, - preload: Sequence[str], - main_path: str | None = None, - sys_path: Unused = None, -) -> None: ... +if sys.version_info >= (3, 14): + def main( + listener_fd: int | None, + alive_r: FileDescriptorLike, + preload: Sequence[str], + main_path: str | None = None, + sys_path: list[str] | None = None, + *, + authkey_r: int | None = None, + ) -> None: ... + +else: + def main( + listener_fd: int | None, + alive_r: FileDescriptorLike, + preload: Sequence[str], + main_path: str | None = None, + sys_path: Unused = None, + ) -> None: ... + def read_signed(fd: int) -> Any: ... def write_signed(fd: int, n: int) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/heap.pyi b/mypy/typeshed/stdlib/multiprocessing/heap.pyi index b5e2ced5e8ee5..38191a099f1ec 100644 --- a/mypy/typeshed/stdlib/multiprocessing/heap.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/heap.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import Incomplete from collections.abc import Callable from mmap import mmap -from typing import Protocol +from typing import Protocol, type_check_only from typing_extensions import TypeAlias __all__ = ["BufferWrapper"] @@ -20,6 +20,7 @@ class Arena: _Block: TypeAlias = tuple[Arena, int, int] if sys.platform != "win32": + @type_check_only class _SupportsDetach(Protocol): def detach(self) -> int: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 5d5b9cdcb9135..5efe69a973777 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -2,23 +2,33 @@ import queue import sys import threading from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT -from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence -from types import TracebackType +from collections.abc import ( + Callable, + Iterable, + Iterator, + Mapping, + MutableMapping, + MutableSequence, + MutableSet, + Sequence, + Set as AbstractSet, +) +from types import GenericAlias, TracebackType from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload from typing_extensions import Self, TypeAlias -from .connection import Connection +from . import pool +from .connection import Connection, _Address 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"] -if sys.version_info >= (3, 9): - from types import GenericAlias - _T = TypeVar("_T") _KT = TypeVar("_KT") _VT = TypeVar("_VT") +_S = TypeVar("_S") class Namespace: def __init__(self, **kwds: Any) -> None: ... @@ -28,15 +38,16 @@ class Namespace: _Namespace: TypeAlias = Namespace class Token: + __slots__ = ("typeid", "address", "id") typeid: str | bytes | None - address: tuple[str | bytes, int] + address: _Address | None id: str | bytes | int | None - def __init__(self, typeid: bytes | str | None, address: tuple[str | bytes, int], id: str | bytes | int | None) -> None: ... + def __init__(self, typeid: bytes | str | None, address: _Address | None, id: str | bytes | int | None) -> None: ... def __getstate__(self) -> tuple[str | bytes | None, tuple[str | bytes, int], str | bytes | int | None]: ... def __setstate__(self, state: tuple[str | bytes | None, tuple[str | bytes, int], str | bytes | int | None]) -> None: ... class BaseProxy: - _address_to_local: dict[Any, Any] + _address_to_local: dict[_Address, Any] _mutex: Any def __init__( self, @@ -57,34 +68,105 @@ class ValueProxy(BaseProxy, Generic[_T]): def get(self) -> _T: ... def set(self, value: _T) -> None: ... value: _T - if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +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] + +if sys.version_info >= (3, 14): + class _BaseSetProxy(BaseProxy, MutableSet[_T]): + __builtins__: ClassVar[dict[str, Any]] + # Copied from builtins.set + def add(self, element: _T, /) -> None: ... + def copy(self) -> set[_T]: ... + def clear(self) -> None: ... + def difference(self, *s: Iterable[Any]) -> set[_T]: ... + def difference_update(self, *s: Iterable[Any]) -> None: ... + def discard(self, element: _T, /) -> None: ... + def intersection(self, *s: Iterable[Any]) -> set[_T]: ... + def intersection_update(self, *s: Iterable[Any]) -> None: ... + def isdisjoint(self, s: Iterable[Any], /) -> bool: ... + def issubset(self, s: Iterable[Any], /) -> bool: ... + def issuperset(self, s: Iterable[Any], /) -> bool: ... + def pop(self) -> _T: ... + def remove(self, element: _T, /) -> None: ... + def symmetric_difference(self, s: Iterable[_T], /) -> set[_T]: ... + def symmetric_difference_update(self, s: Iterable[_T], /) -> None: ... + def union(self, *s: Iterable[_S]) -> set[_T | _S]: ... + def update(self, *s: Iterable[_T]) -> None: ... + def __len__(self) -> int: ... + def __contains__(self, o: object, /) -> bool: ... + def __iter__(self) -> Iterator[_T]: ... + def __and__(self, value: AbstractSet[object], /) -> set[_T]: ... + def __iand__(self, value: AbstractSet[object], /) -> Self: ... + def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... + def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] + def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ... + def __isub__(self, value: AbstractSet[object], /) -> Self: ... + def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... + def __ixor__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] + def __le__(self, value: AbstractSet[object], /) -> bool: ... + def __lt__(self, value: AbstractSet[object], /) -> bool: ... + def __ge__(self, value: AbstractSet[object], /) -> bool: ... + def __gt__(self, value: AbstractSet[object], /) -> bool: ... + def __eq__(self, value: object, /) -> bool: ... + def __rand__(self, value: AbstractSet[object], /) -> set[_T]: ... + def __ror__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[misc] + def __rsub__(self, value: AbstractSet[_T], /) -> set[_T]: ... + def __rxor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[misc] 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: ... + class SetProxy(_BaseSetProxy[_T]): ... class BaseListProxy(BaseProxy, MutableSequence[_T]): __builtins__: ClassVar[dict[str, Any]] @@ -101,6 +183,7 @@ class BaseListProxy(BaseProxy, MutableSequence[_T]): def __setitem__(self, s: slice, o: Iterable[_T], /) -> None: ... def __mul__(self, n: SupportsIndex, /) -> list[_T]: ... def __rmul__(self, n: SupportsIndex, /) -> list[_T]: ... + def __imul__(self, value: SupportsIndex, /) -> Self: ... def __reversed__(self) -> Iterator[_T]: ... def append(self, object: _T, /) -> None: ... def extend(self, iterable: Iterable[_T], /) -> None: ... @@ -122,20 +205,50 @@ class ListProxy(BaseListProxy[_T]): if sys.version_info >= (3, 13): def __class_getitem__(cls, args: Any, /) -> Any: ... +# Send is (kind, result) +# Receive is (id, methodname, args, kwds) +_ServerConnection: TypeAlias = Connection[tuple[str, Any], tuple[str, str, Iterable[Any], Mapping[str, Any]]] + # Returned by BaseManager.get_server() class Server: - address: Any + address: _Address | None + id_to_obj: dict[str, tuple[Any, set[str], dict[str, str]]] + fallback_mapping: dict[str, Callable[[_ServerConnection, str, Any], Any]] + public: list[str] + # Registry values are (callable, exposed, method_to_typeid, proxytype) def __init__( - self, registry: dict[str, tuple[Callable[..., Any], Any, Any, Any]], address: Any, authkey: bytes, serializer: str + self, + registry: dict[str, tuple[Callable[..., Any], Iterable[str], dict[str, str], Any]], + address: _Address | None, + authkey: bytes, + serializer: str, ) -> None: ... def serve_forever(self) -> None: ... - def accept_connection(self, c: Connection, name: str) -> None: ... + def accepter(self) -> None: ... + if sys.version_info >= (3, 10): + def handle_request(self, conn: _ServerConnection) -> None: ... + else: + def handle_request(self, c: _ServerConnection) -> None: ... + + def serve_client(self, conn: _ServerConnection) -> None: ... + def fallback_getvalue(self, conn: _ServerConnection, ident: str, obj: _T) -> _T: ... + def fallback_str(self, conn: _ServerConnection, ident: str, obj: Any) -> str: ... + def fallback_repr(self, conn: _ServerConnection, ident: str, obj: Any) -> str: ... + def dummy(self, c: _ServerConnection) -> None: ... + def debug_info(self, c: _ServerConnection) -> str: ... + def number_of_objects(self, c: _ServerConnection) -> int: ... + def shutdown(self, c: _ServerConnection) -> None: ... + def create(self, c: _ServerConnection, typeid: str, /, *args: Any, **kwds: Any) -> tuple[str, tuple[str, ...]]: ... + def get_methods(self, c: _ServerConnection, token: Token) -> set[str]: ... + def accept_connection(self, c: _ServerConnection, name: str) -> None: ... + def incref(self, c: _ServerConnection, ident: str) -> None: ... + def decref(self, c: _ServerConnection, ident: str) -> None: ... class BaseManager: if sys.version_info >= (3, 11): def __init__( self, - address: Any | None = None, + address: _Address | None = None, authkey: bytes | None = None, serializer: str = "pickle", ctx: BaseContext | None = None, @@ -145,7 +258,7 @@ class BaseManager: else: def __init__( self, - address: Any | None = None, + address: _Address | None = None, authkey: bytes | None = None, serializer: str = "pickle", ctx: BaseContext | None = None, @@ -154,10 +267,10 @@ 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: ... + def address(self) -> _Address | None: ... @classmethod def register( cls, @@ -174,14 +287,26 @@ class BaseManager: ) -> None: ... class SyncManager(BaseManager): - def BoundedSemaphore(self, value: Any = ...) -> threading.BoundedSemaphore: ... - def Condition(self, lock: Any = ...) -> threading.Condition: ... + def Barrier( + self, parties: int, action: Callable[[], None] | None = None, timeout: float | None = None + ) -> threading.Barrier: ... + def BoundedSemaphore(self, value: int = 1) -> threading.BoundedSemaphore: ... + def Condition(self, lock: threading.Lock | threading._RLock | None = None) -> threading.Condition: ... def Event(self) -> threading.Event: ... def Lock(self) -> threading.Lock: ... def Namespace(self) -> _Namespace: ... + def Pool( + self, + processes: int | None = None, + initializer: Callable[..., object] | None = None, + initargs: Iterable[Any] = (), + maxtasksperchild: int | None = None, + context: Any | None = None, + ) -> pool.Pool: ... def Queue(self, maxsize: int = ...) -> queue.Queue[Any]: ... + def JoinableQueue(self, maxsize: int = ...) -> queue.Queue[Any]: ... def RLock(self) -> threading.RLock: ... - def Semaphore(self, value: Any = ...) -> threading.Semaphore: ... + def Semaphore(self, value: int = 1) -> threading.Semaphore: ... def Array(self, typecode: Any, sequence: Sequence[_T]) -> Sequence[_T]: ... def Value(self, typecode: Any, value: _T) -> ValueProxy[_T]: ... # Overloads are copied from builtins.dict.__init__ @@ -205,9 +330,18 @@ class SyncManager(BaseManager): def list(self, sequence: Sequence[_T], /) -> ListProxy[_T]: ... @overload def list(self) -> ListProxy[Any]: ... + if sys.version_info >= (3, 14): + @overload + def set(self, iterable: Iterable[_T], /) -> SetProxy[_T]: ... + @overload + def set(self) -> SetProxy[Any]: ... class RemoteError(Exception): ... -class SharedMemoryServer(Server): ... + +class SharedMemoryServer(Server): + def track_segment(self, c: _ServerConnection, segment_name: str) -> None: ... + def release_segment(self, c: _ServerConnection, segment_name: str) -> None: ... + def list_segments(self, c: _ServerConnection) -> list[str]: ... class SharedMemoryManager(BaseManager): def get_server(self) -> SharedMemoryServer: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 950ed1d8c56b6..f276372d09039 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,12 +1,9 @@ -import sys from collections.abc import Callable, Iterable, Iterator, Mapping -from types import TracebackType +from multiprocessing.context import DefaultContext, Process +from types import GenericAlias, TracebackType from typing import Any, Final, Generic, TypeVar from typing_extensions import Self -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["Pool", "ThreadPool"] _S = TypeVar("_S") @@ -20,8 +17,7 @@ class ApplyResult(Generic[_T]): def wait(self, timeout: float | None = None) -> None: ... def ready(self) -> bool: ... def successful(self) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # alias created during issue #17805 AsyncResult = ApplyResult @@ -53,6 +49,8 @@ class Pool: maxtasksperchild: int | None = None, context: Any | None = None, ) -> None: ... + @staticmethod + def Process(ctx: DefaultContext, *args: Any, **kwds: Any) -> Process: ... def apply(self, func: Callable[..., _T], args: Iterable[Any] = (), kwds: Mapping[str, Any] = {}) -> _T: ... def apply_async( self, diff --git a/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi b/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi index 4fcbfd99a8d0d..5e53b055cc797 100644 --- a/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/popen_fork.pyi @@ -18,6 +18,9 @@ if sys.platform != "win32": def duplicate_for_child(self, fd: int) -> int: ... def poll(self, flag: int = 1) -> int | None: ... def wait(self, timeout: float | None = None) -> int | None: ... + if sys.version_info >= (3, 14): + def interrupt(self) -> None: ... + def terminate(self) -> None: ... def kill(self) -> None: ... def close(self) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/queues.pyi b/mypy/typeshed/stdlib/multiprocessing/queues.pyi index 581a46ea0bc85..a6b00d744c421 100644 --- a/mypy/typeshed/stdlib/multiprocessing/queues.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/queues.pyi @@ -1,9 +1,7 @@ import sys +from types import GenericAlias from typing import Any, Generic, TypeVar -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["Queue", "SimpleQueue", "JoinableQueue"] _T = TypeVar("_T") @@ -31,11 +29,8 @@ class JoinableQueue(Queue[_T]): class SimpleQueue(Generic[_T]): def __init__(self, *, ctx: Any = ...) -> None: ... - if sys.version_info >= (3, 9): - def close(self) -> None: ... - + def close(self) -> None: ... def empty(self) -> bool: ... def get(self) -> _T: ... def put(self, obj: _T) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index a31987bcc3cbe..490ae195c20e2 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -1,12 +1,12 @@ import pickle import sys +from _pickle import _ReducedType from _typeshed import HasFileno, SupportsWrite, Unused from abc import ABCMeta from builtins import type as Type # alias to avoid name clash from collections.abc import Callable from copyreg import _DispatchTableType from multiprocessing import connection -from pickle import _ReducedType from socket import socket from typing import Any, Final @@ -35,15 +35,16 @@ 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: - ACKNOWLEDGE: Final[bool] + if sys.version_info < (3, 14): + 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/resource_tracker.pyi b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi index 61da7fdf1cebe..cb2f27a628614 100644 --- a/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/resource_tracker.pyi @@ -1,3 +1,4 @@ +import sys from _typeshed import FileDescriptorOrPath from collections.abc import Sized @@ -8,6 +9,8 @@ class ResourceTracker: def ensure_running(self) -> None: ... def register(self, name: Sized, rtype: str) -> None: ... def unregister(self, name: Sized, rtype: str) -> None: ... + if sys.version_info >= (3, 12): + def __del__(self) -> None: ... _resource_tracker: ResourceTracker ensure_running = _resource_tracker.ensure_running diff --git a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi index b63cedf858676..f75a372a69a2d 100644 --- a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi @@ -1,11 +1,9 @@ import sys from collections.abc import Iterable +from types import GenericAlias from typing import Any, Generic, TypeVar, overload from typing_extensions import Self -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["SharedMemory", "ShareableList"] _SLT = TypeVar("_SLT", int, float, bool, str, bytes, None) @@ -17,7 +15,7 @@ class SharedMemory: def __init__(self, name: str | None = None, create: bool = False, size: int = 0) -> None: ... @property - def buf(self) -> memoryview: ... + def buf(self) -> memoryview | None: ... @property def name(self) -> str: ... @property @@ -40,5 +38,4 @@ class ShareableList(Generic[_SLT]): def format(self) -> str: ... def count(self, value: _SLT) -> int: ... def index(self, value: _SLT) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 2b0498abc2c61..e2ec15f05ea23 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -1,10 +1,11 @@ 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 -from typing import Any, Generic, Literal, Protocol, TypeVar, overload +from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only __all__ = ["RawValue", "RawArray", "Value", "Array", "copy", "synchronized"] @@ -80,7 +81,7 @@ def synchronized( ) -> SynchronizedArray[_T]: ... @overload def synchronized(obj: _CT, lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedBase[_CT]: ... - +@type_check_only class _AcquireFunc(Protocol): def __call__(self, block: bool = ..., timeout: float | None = ..., /) -> bool: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi index 43ce2f07d9962..4a97532228974 100644 --- a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi @@ -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..a0d97baa0633b 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: ... @@ -49,6 +54,7 @@ class RLock(SemLock): class Semaphore(SemLock): def __init__(self, value: int = 1, *, ctx: BaseContext) -> None: ... + def get_value(self) -> int: ... class BoundedSemaphore(Semaphore): def __init__(self, value: int = 1, *, ctx: BaseContext) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi index d5b6384afd5ed..3583194c77e29 100644 --- a/mypy/typeshed/stdlib/multiprocessing/util.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi @@ -1,3 +1,4 @@ +import sys import threading from _typeshed import ConvertibleToInt, Incomplete, Unused from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence @@ -22,14 +23,19 @@ __all__ = [ "SUBWARNING", ] +if sys.version_info >= (3, 14): + __all__ += ["warn"] + _T = TypeVar("_T") _R_co = TypeVar("_R_co", default=Any, covariant=True) -NOTSET: Final[int] -SUBDEBUG: Final[int] -DEBUG: Final[int] -INFO: Final[int] -SUBWARNING: Final[int] +NOTSET: Final = 0 +SUBDEBUG: Final = 5 +DEBUG: Final = 10 +INFO: Final = 20 +SUBWARNING: Final = 25 +if sys.version_info >= (3, 14): + WARNING: Final = 30 LOGGER_NAME: Final[str] DEFAULT_LOGGING_FORMAT: Final[str] @@ -37,12 +43,16 @@ DEFAULT_LOGGING_FORMAT: Final[str] def sub_debug(msg: object, *args: object) -> None: ... def debug(msg: object, *args: object) -> None: ... def info(msg: object, *args: object) -> None: ... + +if sys.version_info >= (3, 14): + def warn(msg: object, *args: object) -> None: ... + def sub_warning(msg: object, *args: object) -> None: ... def get_logger() -> Logger: ... def log_to_stderr(level: _LoggingLevel | None = None) -> Logger: ... def is_abstract_socket_namespace(address: str | bytes | None) -> bool: ... -abstract_sockets_supported: bool +abstract_sockets_supported: Final[bool] def get_temp_dir() -> str: ... def register_after_fork(obj: _T, func: Callable[[_T], object]) -> None: ... diff --git a/mypy/typeshed/stdlib/nntplib.pyi b/mypy/typeshed/stdlib/nntplib.pyi index 85dfbff1cb50e..1fb1e79f69a1f 100644 --- a/mypy/typeshed/stdlib/nntplib.pyi +++ b/mypy/typeshed/stdlib/nntplib.pyi @@ -1,7 +1,6 @@ import datetime import socket import ssl -import sys from _typeshed import Unused from builtins import list as _list # conflicts with a method named "list" from collections.abc import Iterable @@ -98,10 +97,6 @@ class NNTP: def over( self, message_spec: None | str | _list[Any] | tuple[Any, ...], *, file: _File = None ) -> tuple[str, _list[tuple[int, dict[str, str]]]]: ... - if sys.version_info < (3, 9): - def xgtitle(self, group: str, *, file: _File = None) -> tuple[str, _list[tuple[str, str]]]: ... - def xpath(self, id: Any) -> tuple[str, str]: ... - def date(self) -> tuple[str, datetime.datetime]: ... def post(self, data: bytes | Iterable[bytes]) -> str: ... def ihave(self, message_id: Any, data: bytes | Iterable[bytes]) -> str: ... diff --git a/mypy/typeshed/stdlib/nt.pyi b/mypy/typeshed/stdlib/nt.pyi index e1d57d09a9bd5..0c87444d18f44 100644 --- a/mypy/typeshed/stdlib/nt.pyi +++ b/mypy/typeshed/stdlib/nt.pyi @@ -89,14 +89,14 @@ if sys.platform == "win32": umask as umask, uname_result as uname_result, unlink as unlink, + unsetenv as unsetenv, urandom as urandom, utime as utime, waitpid as waitpid, + waitstatus_to_exitcode as waitstatus_to_exitcode, write as write, ) - if sys.version_info >= (3, 9): - from os import unsetenv as unsetenv, waitstatus_to_exitcode as waitstatus_to_exitcode if sys.version_info >= (3, 11): from os import EX_OK as EX_OK if sys.version_info >= (3, 12): @@ -110,4 +110,7 @@ if sys.platform == "win32": if sys.version_info >= (3, 13): from os import fchmod as fchmod, lchmod as lchmod + if sys.version_info >= (3, 14): + from os import readinto as readinto + environ: dict[str, str] diff --git a/mypy/typeshed/stdlib/ntpath.pyi b/mypy/typeshed/stdlib/ntpath.pyi index ebe305ef708c2..074df075b9727 100644 --- a/mypy/typeshed/stdlib/ntpath.pyi +++ b/mypy/typeshed/stdlib/ntpath.pyi @@ -1,6 +1,8 @@ import sys from _typeshed import BytesPath, StrOrBytesPath, StrPath from genericpath import ( + ALLOW_MISSING as ALLOW_MISSING, + _AllowMissingType, commonprefix as commonprefix, exists as exists, getatime as getatime, @@ -89,6 +91,7 @@ __all__ = [ "sameopenfile", "samestat", "commonpath", + "ALLOW_MISSING", ] if sys.version_info >= (3, 12): __all__ += ["isjunction", "splitroot"] @@ -108,16 +111,10 @@ def join(path: StrPath, /, *paths: StrPath) -> str: ... def join(path: BytesPath, /, *paths: BytesPath) -> bytes: ... if sys.platform == "win32": - if sys.version_info >= (3, 10): - @overload - def realpath(path: PathLike[AnyStr], *, strict: bool = False) -> AnyStr: ... - @overload - def realpath(path: AnyStr, *, strict: bool = False) -> AnyStr: ... - else: - @overload - def realpath(path: PathLike[AnyStr]) -> AnyStr: ... - @overload - def realpath(path: AnyStr) -> AnyStr: ... + @overload + def realpath(path: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ... + @overload + def realpath(path: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... else: realpath = abspath diff --git a/mypy/typeshed/stdlib/nturl2path.pyi b/mypy/typeshed/stdlib/nturl2path.pyi index b8ad8d6821554..014af8a0fd2ed 100644 --- a/mypy/typeshed/stdlib/nturl2path.pyi +++ b/mypy/typeshed/stdlib/nturl2path.pyi @@ -1,2 +1,12 @@ -def url2pathname(url: str) -> str: ... -def pathname2url(p: str) -> str: ... +import sys +from typing_extensions import deprecated + +if sys.version_info >= (3, 14): + @deprecated("The `nturl2path` module is deprecated since Python 3.14.") + def url2pathname(url: str) -> str: ... + @deprecated("The `nturl2path` module is deprecated since Python 3.14.") + def pathname2url(p: str) -> str: ... + +else: + def url2pathname(url: str) -> str: ... + def pathname2url(p: str) -> str: ... diff --git a/mypy/typeshed/stdlib/numbers.pyi b/mypy/typeshed/stdlib/numbers.pyi index e129de2cdc67a..64fb16581e952 100644 --- a/mypy/typeshed/stdlib/numbers.pyi +++ b/mypy/typeshed/stdlib/numbers.pyi @@ -7,9 +7,8 @@ # (since type checkers don't see `complex` as a subtype of `numbers.Complex`, # nor `float` as a subtype of `numbers.Real`, etc.) -from _typeshed import Incomplete from abc import ABCMeta, abstractmethod -from typing import Literal, Protocol, overload +from typing import ClassVar, Literal, Protocol, overload, type_check_only __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] @@ -23,6 +22,7 @@ __all__ = ["Number", "Complex", "Real", "Rational", "Integral"] # NOTE: We can't include `__complex__` here, # as we want `int` to be seen as a subtype of `_ComplexLike`, # and `int.__complex__` does not exist :( +@type_check_only class _ComplexLike(Protocol): def __neg__(self) -> _ComplexLike: ... def __pos__(self) -> _ComplexLike: ... @@ -30,6 +30,7 @@ class _ComplexLike(Protocol): # _RealLike is a structural-typing approximation # of the `Real` ABC, which is not (and cannot be) a protocol +@type_check_only class _RealLike(_ComplexLike, Protocol): def __trunc__(self) -> _IntegralLike: ... def __floor__(self) -> _IntegralLike: ... @@ -42,6 +43,7 @@ class _RealLike(_ComplexLike, Protocol): # _IntegralLike is a structural-typing approximation # of the `Integral` ABC, which is not (and cannot be) a protocol +@type_check_only class _IntegralLike(_RealLike, Protocol): def __invert__(self) -> _IntegralLike: ... def __int__(self) -> int: ... @@ -59,12 +61,14 @@ class _IntegralLike(_RealLike, Protocol): ################# class Number(metaclass=ABCMeta): + __slots__ = () @abstractmethod def __hash__(self) -> int: ... # See comment at the top of the file # for why some of these return types are purposefully vague class Complex(Number, _ComplexLike): + __slots__ = () @abstractmethod def __complex__(self) -> complex: ... def __bool__(self) -> bool: ... @@ -102,10 +106,12 @@ class Complex(Number, _ComplexLike): def conjugate(self) -> _ComplexLike: ... @abstractmethod def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] # See comment at the top of the file # for why some of these return types are purposefully vague class Real(Complex, _RealLike): + __slots__ = () @abstractmethod def __float__(self) -> float: ... @abstractmethod @@ -150,6 +156,7 @@ class Real(Complex, _RealLike): # See comment at the top of the file # for why some of these return types are purposefully vague class Rational(Real): + __slots__ = () @property @abstractmethod def numerator(self) -> _IntegralLike: ... @@ -161,11 +168,12 @@ class Rational(Real): # See comment at the top of the file # for why some of these return types are purposefully vague class Integral(Rational, _IntegralLike): + __slots__ = () @abstractmethod def __int__(self) -> int: ... def __index__(self) -> int: ... @abstractmethod - def __pow__(self, exponent, modulus: Incomplete | None = None) -> _IntegralLike: ... + def __pow__(self, exponent, modulus=None) -> _IntegralLike: ... @abstractmethod def __lshift__(self, other) -> _IntegralLike: ... @abstractmethod diff --git a/mypy/typeshed/stdlib/opcode.pyi b/mypy/typeshed/stdlib/opcode.pyi index f9f76962f8765..ed0e96ef1cb9c 100644 --- a/mypy/typeshed/stdlib/opcode.pyi +++ b/mypy/typeshed/stdlib/opcode.pyi @@ -1,5 +1,5 @@ import sys -from typing import Literal +from typing import Final, Literal __all__ = [ "cmp_op", @@ -23,41 +23,25 @@ else: if sys.version_info >= (3, 13): __all__ += ["hasjump"] -if sys.version_info >= (3, 9): - cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]] -else: - cmp_op: tuple[ - Literal["<"], - Literal["<="], - Literal["=="], - Literal["!="], - Literal[">"], - Literal[">="], - Literal["in"], - Literal["not in"], - Literal["is"], - Literal["is not"], - Literal["exception match"], - Literal["BAD"], - ] -hasconst: list[int] -hasname: list[int] -hasjrel: list[int] -hasjabs: list[int] -haslocal: list[int] -hascompare: list[int] -hasfree: list[int] +cmp_op: tuple[Literal["<"], Literal["<="], Literal["=="], Literal["!="], Literal[">"], Literal[">="]] +hasconst: Final[list[int]] +hasname: Final[list[int]] +hasjrel: Final[list[int]] +hasjabs: Final[list[int]] +haslocal: Final[list[int]] +hascompare: Final[list[int]] +hasfree: Final[list[int]] if sys.version_info >= (3, 12): - hasarg: list[int] - hasexc: list[int] + hasarg: Final[list[int]] + hasexc: Final[list[int]] else: - hasnargs: list[int] + hasnargs: Final[list[int]] if sys.version_info >= (3, 13): - hasjump: list[int] -opname: list[str] + hasjump: Final[list[int]] +opname: Final[list[str]] -opmap: dict[str, int] -HAVE_ARGUMENT: int -EXTENDED_ARG: int +opmap: Final[dict[str, int]] +HAVE_ARGUMENT: Final = 43 +EXTENDED_ARG: Final = 69 def stack_effect(opcode: int, oparg: int | None = None, /, *, jump: bool | None = None) -> int: ... diff --git a/mypy/typeshed/stdlib/operator.pyi b/mypy/typeshed/stdlib/operator.pyi index 1a817f00f3c12..2f919514b0b8b 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 Self, TypeVarTuple, Unpack + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_Ts = TypeVarTuple("_Ts") __all__ = [ "abs", @@ -59,9 +120,13 @@ __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 @@ -111,3 +176,42 @@ __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, ...] + # + # If we can't infer a literal key from __new__ (ie: `itemgetter[Literal[0]]` for `itemgetter(0)`), + # then we can't annotate __call__'s return type or it'll break on tuples + # + # These issues are best demonstrated by the `itertools.check_itertools_recipes.unique_justseen` test. + def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ... + +@final +class methodcaller: + def __new__(cls, name: str, /, *args: Any, **kwargs: Any) -> Self: ... + def __call__(self, obj: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index b513bb6470605..c522917992800 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,7 +1,9 @@ -from _typeshed import Incomplete +import builtins +from _typeshed import MaybeNone, SupportsWrite from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import IO, Any, AnyStr, Literal, NoReturn, overload +from typing import Any, ClassVar, Final, Literal, NoReturn, overload +from typing_extensions import Self __all__ = [ "Option", @@ -22,13 +24,13 @@ __all__ = [ "BadOptionError", "check_choice", ] +NO_DEFAULT: Final = ("NO", "DEFAULT") +SUPPRESS_HELP: Final = "SUPPRESSHELP" +SUPPRESS_USAGE: Final = "SUPPRESSUSAGE" -NO_DEFAULT: tuple[str, ...] -SUPPRESS_HELP: str -SUPPRESS_USAGE: str - -def check_builtin(option: Option, opt, value: str): ... -def check_choice(option: Option, opt, value: str) -> str: ... +# Can return complex, float, or int depending on the option's type +def check_builtin(option: Option, opt: str, value: str) -> complex: ... +def check_choice(option: Option, opt: str, value: str) -> str: ... class OptParseError(Exception): msg: str @@ -56,15 +58,17 @@ 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 option_strings: dict[Option, str] parser: OptionParser - short_first: Incomplete + short_first: bool | Literal[0, 1] width: int - def __init__(self, indent_increment: int, max_help_position: int, width: int | None, short_first: int) -> None: ... + def __init__( + self, indent_increment: int, max_help_position: int, width: int | None, short_first: bool | Literal[0, 1] + ) -> None: ... def dedent(self) -> None: ... def expand_default(self, option: Option) -> str: ... def format_description(self, description: str | None) -> str: ... @@ -83,14 +87,22 @@ class HelpFormatter: class IndentedHelpFormatter(HelpFormatter): def __init__( - self, indent_increment: int = 2, max_help_position: int = 24, width: int | None = None, short_first: int = 1 + self, + indent_increment: int = 2, + max_help_position: int = 24, + width: int | None = None, + short_first: bool | Literal[0, 1] = 1, ) -> None: ... def format_heading(self, heading: str) -> str: ... def format_usage(self, usage: str) -> str: ... class TitledHelpFormatter(HelpFormatter): def __init__( - self, indent_increment: int = 0, max_help_position: int = 24, width: int | None = None, short_first: int = 0 + self, + indent_increment: int = 0, + max_help_position: int = 24, + width: int | None = None, + short_first: bool | Literal[0, 1] = 0, ) -> None: ... def format_heading(self, heading: str) -> str: ... def format_usage(self, usage: str) -> str: ... @@ -99,25 +111,46 @@ class Option: ACTIONS: tuple[str, ...] ALWAYS_TYPED_ACTIONS: tuple[str, ...] ATTRS: list[str] - CHECK_METHODS: list[Callable[..., Incomplete]] | None + CHECK_METHODS: list[Callable[[Self], object]] | None CONST_ACTIONS: tuple[str, ...] STORE_ACTIONS: tuple[str, ...] TYPED_ACTIONS: tuple[str, ...] TYPES: tuple[str, ...] - TYPE_CHECKER: dict[str, Callable[[Option, str, Incomplete], Any]] + TYPE_CHECKER: dict[str, Callable[[Option, str, str], object]] _long_opts: list[str] _short_opts: list[str] action: str + type: str | None dest: str | None - default: Incomplete + default: Any # default can be "any" type nargs: int - type: Incomplete - callback: Callable[..., Incomplete] | None - callback_args: tuple[Incomplete, ...] | None - callback_kwargs: dict[str, Incomplete] | None + const: Any | None # const can be "any" type + choices: list[str] | tuple[str, ...] | None + # Callback args and kwargs cannot be expressed in Python's type system. + # Revisit if ParamSpec is ever changed to work with packed args/kwargs. + callback: Callable[..., object] | None + callback_args: tuple[Any, ...] | None + callback_kwargs: dict[str, Any] | None help: str | None metavar: str | None - def __init__(self, *opts: str | None, **attrs) -> None: ... + def __init__( + self, + *opts: str | None, + # The following keywords are handled by the _set_attrs method. All default to + # `None` except for `default`, which defaults to `NO_DEFAULT`. + action: str | None = None, + type: str | builtins.type | None = None, + dest: str | None = None, + default: Any = ..., # = NO_DEFAULT + nargs: int | None = None, + const: Any | None = None, + choices: list[str] | tuple[str, ...] | None = None, + callback: Callable[..., object] | None = None, + callback_args: tuple[Any, ...] | None = None, + callback_kwargs: dict[str, Any] | None = None, + help: str | None = None, + metavar: str | None = None, + ) -> None: ... def _check_action(self) -> None: ... def _check_callback(self) -> None: ... def _check_choice(self) -> None: ... @@ -126,13 +159,14 @@ class Option: def _check_nargs(self) -> None: ... def _check_opt_strings(self, opts: Iterable[str | None]) -> list[str]: ... def _check_type(self) -> None: ... - def _set_attrs(self, attrs: dict[str, Incomplete]) -> None: ... + def _set_attrs(self, attrs: dict[str, Any]) -> None: ... # accepted attrs depend on the ATTRS attribute def _set_opt_strings(self, opts: Iterable[str]) -> None: ... - def check_value(self, opt: str, value): ... - def convert_value(self, opt: str, value): ... + def check_value(self, opt: str, value: str) -> Any: ... # return type cannot be known statically + def convert_value(self, opt: str, value: str | tuple[str, ...] | None) -> Any: ... # return type cannot be known statically def get_opt_string(self) -> str: ... - def process(self, opt, value, values, parser: OptionParser) -> int: ... - def take_action(self, action: str, dest: str, opt, value, values, parser: OptionParser) -> int: ... + def process(self, opt: str, value: str | tuple[str, ...] | None, values: Values, parser: OptionParser) -> int: ... + # value of take_action can be "any" type + def take_action(self, action: str, dest: str, opt: str, value: Any, values: Values, parser: OptionParser) -> int: ... def takes_value(self) -> bool: ... make_option = Option @@ -141,7 +175,7 @@ class OptionContainer: _long_opt: dict[str, Option] _short_opt: dict[str, Option] conflict_handler: str - defaults: dict[str, Incomplete] + defaults: dict[str, Any] # default values can be "any" type description: str | None option_class: type[Option] def __init__( @@ -153,7 +187,25 @@ class OptionContainer: @overload def add_option(self, opt: Option, /) -> Option: ... @overload - def add_option(self, arg: str, /, *args: str | None, **kwargs) -> Option: ... + def add_option( + self, + opt_str: str, + /, + *opts: str | None, + action: str | None = None, + type: str | builtins.type | None = None, + dest: str | None = None, + default: Any = ..., # = NO_DEFAULT + nargs: int | None = None, + const: Any | None = None, + choices: list[str] | tuple[str, ...] | None = None, + callback: Callable[..., object] | None = None, + callback_args: tuple[Any, ...] | None = None, + callback_kwargs: dict[str, Any] | None = None, + help: str | None = None, + metavar: str | None = None, + **kwargs, # Allow arbitrary keyword arguments for user defined option_class + ) -> Option: ... def add_options(self, option_list: Iterable[Option]) -> None: ... def destroy(self) -> None: ... def format_option_help(self, formatter: HelpFormatter) -> str: ... @@ -175,15 +227,19 @@ class OptionGroup(OptionContainer): def set_title(self, title: str) -> None: ... class Values: - def __init__(self, defaults: Mapping[str, Incomplete] | None = None) -> None: ... - def _update(self, dict: Mapping[str, Incomplete], mode) -> None: ... - def _update_careful(self, dict: Mapping[str, Incomplete]) -> None: ... - def _update_loose(self, dict: Mapping[str, Incomplete]) -> None: ... - def ensure_value(self, attr: str, value): ... - def read_file(self, filename: str, mode: str = "careful") -> None: ... - def read_module(self, modname: str, mode: str = "careful") -> None: ... - def __getattr__(self, name: str): ... - def __setattr__(self, name: str, value, /) -> None: ... + def __init__(self, defaults: Mapping[str, object] | None = None) -> None: ... + def _update(self, dict: Mapping[str, object], mode: Literal["careful", "loose"]) -> None: ... + def _update_careful(self, dict: Mapping[str, object]) -> None: ... + def _update_loose(self, dict: Mapping[str, object]) -> None: ... + def ensure_value(self, attr: str, value: object) -> Any: ... # return type cannot be known statically + def read_file(self, filename: str, mode: Literal["careful", "loose"] = "careful") -> None: ... + def read_module(self, modname: str, mode: Literal["careful", "loose"] = "careful") -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] + # __getattr__ doesn't exist, but anything passed as a default to __init__ + # is set on the instance. + def __getattr__(self, name: str) -> Any: ... + # TODO: mypy infers -> object for __getattr__ if __setattr__ has `value: object` + def __setattr__(self, name: str, value: Any, /) -> None: ... def __eq__(self, other: object) -> bool: ... class OptionParser(OptionContainer): @@ -217,17 +273,17 @@ class OptionParser(OptionContainer): def _add_version_option(self) -> None: ... def _create_option_list(self) -> None: ... def _get_all_options(self) -> list[Option]: ... - def _get_args(self, args: Iterable[Incomplete]) -> list[Incomplete]: ... + def _get_args(self, args: list[str] | None) -> list[str]: ... def _init_parsing_state(self) -> None: ... def _match_long_opt(self, opt: str) -> str: ... - def _populate_option_list(self, option_list: Iterable[Option], add_help: bool = True) -> None: ... - def _process_args(self, largs: list[Incomplete], rargs: list[Incomplete], values: Values) -> None: ... - def _process_long_opt(self, rargs: list[Incomplete], values) -> None: ... - def _process_short_opts(self, rargs: list[Incomplete], values) -> None: ... + def _populate_option_list(self, option_list: Iterable[Option] | None, add_help: bool = True) -> None: ... + def _process_args(self, largs: list[str], rargs: list[str], values: Values) -> None: ... + def _process_long_opt(self, rargs: list[str], values: Values) -> None: ... + def _process_short_opts(self, rargs: list[str], values: Values) -> None: ... @overload def add_option_group(self, opt_group: OptionGroup, /) -> OptionGroup: ... @overload - def add_option_group(self, *args, **kwargs) -> OptionGroup: ... + def add_option_group(self, title: str, /, description: str | None = None) -> OptionGroup: ... 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: ... @@ -242,14 +298,11 @@ class OptionParser(OptionContainer): def get_prog_name(self) -> str: ... def get_usage(self) -> str: ... def get_version(self) -> str: ... - @overload - def parse_args(self, args: None = None, values: Values | None = None) -> tuple[Values, list[str]]: ... - @overload - def parse_args(self, args: Sequence[AnyStr], values: Values | None = None) -> tuple[Values, list[AnyStr]]: ... - def print_usage(self, file: IO[str] | None = None) -> None: ... - def print_help(self, file: IO[str] | None = None) -> None: ... - def print_version(self, file: IO[str] | None = None) -> None: ... - def set_default(self, dest, value) -> None: ... - def set_defaults(self, **kwargs) -> None: ... - def set_process_default_values(self, process) -> None: ... - def set_usage(self, usage: str) -> None: ... + def parse_args(self, args: list[str] | None = None, values: Values | None = None) -> tuple[Values, list[str]]: ... + def print_usage(self, file: SupportsWrite[str] | None = None) -> None: ... + def print_help(self, file: SupportsWrite[str] | None = None) -> None: ... + def print_version(self, file: SupportsWrite[str] | None = None) -> None: ... + def set_default(self, dest: str, value: Any) -> None: ... # default value can be "any" type + def set_defaults(self, **kwargs: Any) -> None: ... # default values can be "any" type + def set_process_default_values(self, process: bool) -> None: ... + def set_usage(self, usage: str | None) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index d7bb4883a0f2d..bb0a57153948a 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 GenericAlias, TracebackType from typing import ( IO, Any, @@ -39,13 +39,467 @@ from typing import ( final, overload, runtime_checkable, + type_check_only, ) -from typing_extensions import Self, TypeAlias, Unpack, deprecated +from typing_extensions import LiteralString, Self, TypeAlias, Unpack, deprecated from . import path as _path -if sys.version_info >= (3, 9): - from types import GenericAlias +# Re-export common definitions from os.path to reduce duplication +from .path import ( + altsep as altsep, + curdir as curdir, + defpath as defpath, + devnull as devnull, + extsep as extsep, + pardir as pardir, + pathsep as pathsep, + sep as sep, +) + +__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", + "unsetenv", + "urandom", + "utime", + "waitpid", + "waitstatus_to_exitcode", + "walk", + "write", +] +if sys.version_info >= (3, 14): + __all__ += ["readinto"] +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", + "P_PIDFD", + "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", + "pidfd_open", + "removexattr", + "setxattr", + ] +if sys.platform == "linux" and sys.version_info >= (3, 14): + __all__ += ["SCHED_DEADLINE", "SCHED_NORMAL"] +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_NEWTIME", + "CLONE_NEWUSER", + "CLONE_NEWUTS", + "CLONE_SIGHAND", + "CLONE_SYSVSEM", + "CLONE_THREAD", + "CLONE_VM", + "setns", + "unshare", + "PIDFD_NONBLOCK", + ] +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 == "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_KILLED", + "CLD_STOPPED", + "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 != "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"] # This unnecessary alias is to work around various errors path = _path @@ -67,22 +521,22 @@ supports_follow_symlinks: set[Callable[..., Any]] if sys.platform != "win32": # Unix only - PRIO_PROCESS: int - PRIO_PGRP: int - PRIO_USER: int + PRIO_PROCESS: Final[int] + PRIO_PGRP: Final[int] + PRIO_USER: Final[int] - F_LOCK: int - F_TLOCK: int - F_ULOCK: int - F_TEST: int + F_LOCK: Final[int] + F_TLOCK: Final[int] + F_ULOCK: Final[int] + F_TEST: Final[int] if sys.platform != "darwin": - POSIX_FADV_NORMAL: int - POSIX_FADV_SEQUENTIAL: int - POSIX_FADV_RANDOM: int - POSIX_FADV_NOREUSE: int - POSIX_FADV_WILLNEED: int - POSIX_FADV_DONTNEED: int + POSIX_FADV_NORMAL: Final[int] + POSIX_FADV_SEQUENTIAL: Final[int] + POSIX_FADV_RANDOM: Final[int] + POSIX_FADV_NOREUSE: Final[int] + POSIX_FADV_WILLNEED: Final[int] + POSIX_FADV_DONTNEED: Final[int] if sys.platform != "linux" and sys.platform != "darwin": # In the os-module docs, these are marked as being available @@ -92,145 +546,153 @@ if sys.platform != "win32": # so the sys-module docs recommend doing `if sys.platform.startswith('freebsd')` # to detect FreeBSD builds. Unfortunately that would be too dynamic # for type checkers, however. - SF_NODISKIO: int - SF_MNOWAIT: int - SF_SYNC: int + SF_NODISKIO: Final[int] + SF_MNOWAIT: Final[int] + SF_SYNC: Final[int] if sys.version_info >= (3, 11): - SF_NOCACHE: int + SF_NOCACHE: Final[int] if sys.platform == "linux": - XATTR_SIZE_MAX: int - XATTR_CREATE: int - XATTR_REPLACE: int - - P_PID: int - P_PGID: int - P_ALL: int - - if sys.platform == "linux" and sys.version_info >= (3, 9): - P_PIDFD: int - - WEXITED: int - WSTOPPED: int - WNOWAIT: int - - CLD_EXITED: int - CLD_DUMPED: int - CLD_TRAPPED: int - CLD_CONTINUED: int - - if sys.version_info >= (3, 9): - 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 + XATTR_SIZE_MAX: Final[int] + XATTR_CREATE: Final[int] + XATTR_REPLACE: Final[int] + + P_PID: Final[int] + P_PGID: Final[int] + P_ALL: Final[int] + + if sys.platform == "linux": + P_PIDFD: Final[int] + + WEXITED: Final[int] + WSTOPPED: Final[int] + WNOWAIT: Final[int] + + CLD_EXITED: Final[int] + CLD_DUMPED: Final[int] + CLD_TRAPPED: Final[int] + CLD_CONTINUED: Final[int] + CLD_KILLED: Final[int] + CLD_STOPPED: Final[int] + + SCHED_OTHER: Final[int] + SCHED_FIFO: Final[int] + SCHED_RR: Final[int] + if sys.platform != "darwin" and sys.platform != "linux": + SCHED_SPORADIC: Final[int] + +if sys.platform == "linux": + SCHED_BATCH: Final[int] + SCHED_IDLE: Final[int] + SCHED_RESET_ON_FORK: Final[int] + +if sys.version_info >= (3, 14) and sys.platform == "linux": + SCHED_DEADLINE: Final[int] + SCHED_NORMAL: Final[int] if sys.platform != "win32": - RTLD_LAZY: int - RTLD_NOW: int - RTLD_GLOBAL: int - RTLD_LOCAL: int - RTLD_NODELETE: int - RTLD_NOLOAD: int + RTLD_LAZY: Final[int] + RTLD_NOW: Final[int] + RTLD_GLOBAL: Final[int] + RTLD_LOCAL: Final[int] + RTLD_NODELETE: Final[int] + RTLD_NOLOAD: Final[int] if sys.platform == "linux": - RTLD_DEEPBIND: int - GRND_NONBLOCK: int - GRND_RANDOM: int + RTLD_DEEPBIND: Final[int] + GRND_NONBLOCK: Final[int] + GRND_RANDOM: Final[int] if sys.platform == "darwin" and sys.version_info >= (3, 12): - PRIO_DARWIN_BG: int - PRIO_DARWIN_NONUI: int - PRIO_DARWIN_PROCESS: int - PRIO_DARWIN_THREAD: int - -SEEK_SET: int -SEEK_CUR: int -SEEK_END: int + PRIO_DARWIN_BG: Final[int] + PRIO_DARWIN_NONUI: Final[int] + PRIO_DARWIN_PROCESS: Final[int] + PRIO_DARWIN_THREAD: Final[int] + +SEEK_SET: Final = 0 +SEEK_CUR: Final = 1 +SEEK_END: Final = 2 +if sys.platform != "win32": + SEEK_DATA: Final = 3 + SEEK_HOLE: Final = 4 + +O_RDONLY: Final[int] +O_WRONLY: Final[int] +O_RDWR: Final[int] +O_APPEND: Final[int] +O_CREAT: Final[int] +O_EXCL: Final[int] +O_TRUNC: Final[int] +if sys.platform == "win32": + O_BINARY: Final[int] + O_NOINHERIT: Final[int] + O_SHORT_LIVED: Final[int] + O_TEMPORARY: Final[int] + O_RANDOM: Final[int] + O_SEQUENTIAL: Final[int] + O_TEXT: Final[int] + if sys.platform != "win32": - SEEK_DATA: int # some flavors of Unix - SEEK_HOLE: int # some flavors of Unix - -O_RDONLY: int -O_WRONLY: int -O_RDWR: int -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? + O_DSYNC: Final[int] + O_SYNC: Final[int] + O_NDELAY: Final[int] + O_NONBLOCK: Final[int] + O_NOCTTY: Final[int] + O_CLOEXEC: Final[int] + O_ASYNC: Final[int] # Gnu extension if in C library + O_DIRECTORY: Final[int] # Gnu extension if in C library + O_NOFOLLOW: Final[int] # Gnu extension if in C library + O_ACCMODE: Final[int] # TODO: when does this exist? + +if sys.platform == "linux": + O_RSYNC: Final[int] + O_DIRECT: Final[int] # Gnu extension if in C library + O_NOATIME: Final[int] # Gnu extension if in C library + O_PATH: Final[int] # Gnu extension if in C library + O_TMPFILE: Final[int] # Gnu extension if in C library + O_LARGEFILE: Final[int] # Gnu extension if in C library + +if sys.platform != "linux" and sys.platform != "win32": + O_SHLOCK: Final[int] + O_EXLOCK: Final[int] + +if sys.platform == "darwin" and sys.version_info >= (3, 10): + O_EVTONLY: Final[int] + O_NOFOLLOW_ANY: Final[int] + O_SYMLINK: Final[int] + +if sys.platform != "win32" and sys.version_info >= (3, 10): + O_FSYNC: Final[int] + +if sys.platform != "linux" and sys.platform != "win32" and sys.version_info >= (3, 13): + O_EXEC: Final[int] + O_SEARCH: Final[int] if sys.platform != "win32" and sys.platform != "darwin": # posix, but apparently missing on macos - ST_APPEND: int - ST_MANDLOCK: int - ST_NOATIME: int - ST_NODEV: int - ST_NODIRATIME: int - ST_NOEXEC: int - ST_RELATIME: int - ST_SYNCHRONOUS: int - ST_WRITE: int + ST_APPEND: Final[int] + ST_MANDLOCK: Final[int] + ST_NOATIME: Final[int] + ST_NODEV: Final[int] + ST_NODIRATIME: Final[int] + ST_NOEXEC: Final[int] + ST_RELATIME: Final[int] + ST_SYNCHRONOUS: Final[int] + ST_WRITE: Final[int] if sys.platform != "win32": - NGROUPS_MAX: int - ST_NOSUID: int - ST_RDONLY: int + NGROUPS_MAX: Final[int] + ST_NOSUID: Final[int] + ST_RDONLY: Final[int] -curdir: str -pardir: str -sep: str -if sys.platform == "win32": - altsep: str -else: - altsep: str | None -extsep: str -pathsep: str -defpath: str -linesep: str -devnull: str -name: str - -F_OK: int -R_OK: int -W_OK: int -X_OK: int +linesep: Literal["\n", "\r\n"] +name: LiteralString + +F_OK: Final = 0 +R_OK: Final = 4 +W_OK: Final = 2 +X_OK: Final = 1 _EnvironCodeFunc: TypeAlias = Callable[[AnyStr], AnyStr] @@ -239,29 +701,14 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]): decodekey: _EnvironCodeFunc[AnyStr] encodevalue: _EnvironCodeFunc[AnyStr] decodevalue: _EnvironCodeFunc[AnyStr] - if sys.version_info >= (3, 9): - def __init__( - self, - data: MutableMapping[AnyStr, AnyStr], - encodekey: _EnvironCodeFunc[AnyStr], - decodekey: _EnvironCodeFunc[AnyStr], - encodevalue: _EnvironCodeFunc[AnyStr], - decodevalue: _EnvironCodeFunc[AnyStr], - ) -> None: ... - else: - putenv: Callable[[AnyStr, AnyStr], object] - unsetenv: Callable[[AnyStr, AnyStr], object] - def __init__( - self, - data: MutableMapping[AnyStr, AnyStr], - encodekey: _EnvironCodeFunc[AnyStr], - decodekey: _EnvironCodeFunc[AnyStr], - encodevalue: _EnvironCodeFunc[AnyStr], - decodevalue: _EnvironCodeFunc[AnyStr], - putenv: Callable[[AnyStr, AnyStr], object], - unsetenv: Callable[[AnyStr, AnyStr], object], - ) -> None: ... - + def __init__( + self, + data: MutableMapping[AnyStr, AnyStr], + encodekey: _EnvironCodeFunc[AnyStr], + decodekey: _EnvironCodeFunc[AnyStr], + encodevalue: _EnvironCodeFunc[AnyStr], + decodevalue: _EnvironCodeFunc[AnyStr], + ) -> None: ... def setdefault(self, key: AnyStr, value: AnyStr) -> AnyStr: ... def copy(self) -> dict[AnyStr, AnyStr]: ... def __delitem__(self, key: AnyStr) -> None: ... @@ -269,63 +716,65 @@ class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]): def __setitem__(self, key: AnyStr, value: AnyStr) -> None: ... def __iter__(self) -> Iterator[AnyStr]: ... def __len__(self) -> int: ... - if sys.version_info >= (3, 9): - def __or__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ... - def __ror__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ... - # We use @overload instead of a Union for reasons similar to those given for - # overloading MutableMapping.update in stdlib/typing.pyi - # The type: ignore is needed due to incompatible __or__/__ior__ signatures - @overload # type: ignore[misc] - def __ior__(self, other: Mapping[AnyStr, AnyStr]) -> Self: ... - @overload - def __ior__(self, other: Iterable[tuple[AnyStr, AnyStr]]) -> Self: ... + def __or__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ... + def __ror__(self, other: Mapping[_T1, _T2]) -> dict[AnyStr | _T1, AnyStr | _T2]: ... + # We use @overload instead of a Union for reasons similar to those given for + # overloading MutableMapping.update in stdlib/typing.pyi + # The type: ignore is needed due to incompatible __or__/__ior__ signatures + @overload # type: ignore[misc] + def __ior__(self, other: Mapping[AnyStr, AnyStr]) -> Self: ... + @overload + def __ior__(self, other: Iterable[tuple[AnyStr, AnyStr]]) -> Self: ... environ: _Environ[str] if sys.platform != "win32": environb: _Environ[bytes] +if sys.version_info >= (3, 14): + def reload_environ() -> None: ... + if sys.version_info >= (3, 11) or sys.platform != "win32": - EX_OK: int + EX_OK: Final[int] if sys.platform != "win32": confstr_names: dict[str, int] pathconf_names: dict[str, int] sysconf_names: dict[str, int] - EX_USAGE: int - EX_DATAERR: int - EX_NOINPUT: int - EX_NOUSER: int - EX_NOHOST: int - EX_UNAVAILABLE: int - EX_SOFTWARE: int - EX_OSERR: int - EX_OSFILE: int - EX_CANTCREAT: int - EX_IOERR: int - EX_TEMPFAIL: int - EX_PROTOCOL: int - EX_NOPERM: int - EX_CONFIG: int + EX_USAGE: Final[int] + EX_DATAERR: Final[int] + EX_NOINPUT: Final[int] + EX_NOUSER: Final[int] + EX_NOHOST: Final[int] + EX_UNAVAILABLE: Final[int] + EX_SOFTWARE: Final[int] + EX_OSERR: Final[int] + EX_OSFILE: Final[int] + EX_CANTCREAT: Final[int] + EX_IOERR: Final[int] + EX_TEMPFAIL: Final[int] + EX_PROTOCOL: Final[int] + EX_NOPERM: Final[int] + EX_CONFIG: Final[int] # Exists on some Unix platforms, e.g. Solaris. if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": - EX_NOTFOUND: int + EX_NOTFOUND: Final[int] -P_NOWAIT: int -P_NOWAITO: int -P_WAIT: int +P_NOWAIT: Final[int] +P_NOWAITO: Final[int] +P_WAIT: Final[int] if sys.platform == "win32": - P_DETACH: int - P_OVERLAY: int + P_DETACH: Final[int] + P_OVERLAY: Final[int] # wait()/waitpid() options if sys.platform != "win32": - WNOHANG: int # Unix only - WCONTINUED: int # some Unix systems - WUNTRACED: int # Unix only + WNOHANG: Final[int] # Unix only + WCONTINUED: Final[int] # some Unix systems + WUNTRACED: Final[int] # Unix only -TMP_MAX: int # Undocumented, but used by tempfile +TMP_MAX: Final[int] # Undocumented, but used by tempfile # ----- os classes (structures) ----- @final @@ -412,8 +861,12 @@ In the future, this property will contain the last metadata change time.""" # 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] + __slots__ = () @abstractmethod def __fspath__(self) -> AnyStr_co: ... @@ -438,8 +891,7 @@ class DirEntry(Generic[AnyStr]): def is_symlink(self) -> bool: ... def stat(self, *, follow_symlinks: bool = True) -> stat_result: ... def __fspath__(self) -> AnyStr: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 12): def is_junction(self) -> bool: ... @@ -562,9 +1014,7 @@ if sys.platform != "win32": else: def putenv(name: str, value: str, /) -> None: ... - - if sys.version_info >= (3, 9): - def unsetenv(name: str, /) -> None: ... + def unsetenv(name: str, /) -> None: ... _Opener: TypeAlias = Callable[[str, int], int] @@ -578,7 +1028,7 @@ def fdopen( newline: str | None = ..., closefd: bool = ..., opener: _Opener | None = ..., -) -> _TextIOWrapper: ... +) -> TextIOWrapper: ... @overload def fdopen( fd: int, @@ -691,11 +1141,11 @@ if sys.platform != "win32": def pwritev(fd: int, buffers: SupportsLenAndGetItem[ReadableBuffer], offset: int, flags: int = 0, /) -> int: ... if sys.platform != "darwin": if sys.version_info >= (3, 10): - RWF_APPEND: int # docs say available on 3.7+, stubtest says otherwise - RWF_DSYNC: int - RWF_SYNC: int - RWF_HIPRI: int - RWF_NOWAIT: int + RWF_APPEND: Final[int] # docs say available on 3.7+, stubtest says otherwise + RWF_DSYNC: Final[int] + RWF_SYNC: Final[int] + RWF_HIPRI: Final[int] + RWF_NOWAIT: Final[int] if sys.platform == "linux": def sendfile(out_fd: FileDescriptor, in_fd: FileDescriptor, offset: int | None, count: int) -> int: ... @@ -705,14 +1155,17 @@ if sys.platform != "win32": in_fd: FileDescriptor, offset: int, count: int, - headers: Sequence[ReadableBuffer] = ..., - trailers: Sequence[ReadableBuffer] = ..., + headers: Sequence[ReadableBuffer] = (), + trailers: Sequence[ReadableBuffer] = (), flags: int = 0, ) -> int: ... # FreeBSD and Mac OS X only def readv(fd: int, buffers: SupportsLenAndGetItem[WriteableBuffer], /) -> int: ... def writev(fd: int, buffers: SupportsLenAndGetItem[ReadableBuffer], /) -> int: ... +if sys.version_info >= (3, 14): + def readinto(fd: int, buffer: ReadableBuffer, /) -> int: ... + @final class terminal_size(structseq[int], tuple[int, int]): if sys.version_info >= (3, 10): @@ -748,7 +1201,7 @@ if sys.platform != "win32": def getcwd() -> str: ... def getcwdb() -> bytes: ... -def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, follow_symlinks: bool = ...) -> None: ... +def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, follow_symlinks: bool = True) -> None: ... if sys.platform != "win32" and sys.platform != "linux": def chflags(path: StrOrBytesPath, flags: int, follow_symlinks: bool = True) -> None: ... # some flavors of Unix @@ -793,9 +1246,13 @@ 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 +@type_check_only +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 +1374,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: ... @@ -1031,9 +1504,9 @@ else: setsigdef: Iterable[int] = ..., scheduler: tuple[Any, sched_param] | None = ..., ) -> int: ... - POSIX_SPAWN_OPEN: int - POSIX_SPAWN_CLOSE: int - POSIX_SPAWN_DUP2: int + POSIX_SPAWN_OPEN: Final = 0 + POSIX_SPAWN_CLOSE: Final = 1 + POSIX_SPAWN_DUP2: Final = 2 if sys.platform != "win32": @final @@ -1059,6 +1532,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: ... @@ -1089,31 +1570,33 @@ if sys.platform == "win32": def add_dll_directory(path: str) -> _AddedDllDirectory: ... if sys.platform == "linux": - MFD_CLOEXEC: int - MFD_ALLOW_SEALING: int - MFD_HUGETLB: int - MFD_HUGE_SHIFT: int - MFD_HUGE_MASK: int - MFD_HUGE_64KB: int - MFD_HUGE_512KB: int - MFD_HUGE_1MB: int - MFD_HUGE_2MB: int - MFD_HUGE_8MB: int - MFD_HUGE_16MB: int - MFD_HUGE_32MB: int - MFD_HUGE_256MB: int - MFD_HUGE_512MB: int - MFD_HUGE_1GB: int - MFD_HUGE_2GB: int - MFD_HUGE_16GB: int + MFD_CLOEXEC: Final[int] + MFD_ALLOW_SEALING: Final[int] + MFD_HUGETLB: Final[int] + MFD_HUGE_SHIFT: Final[int] + MFD_HUGE_MASK: Final[int] + MFD_HUGE_64KB: Final[int] + MFD_HUGE_512KB: Final[int] + MFD_HUGE_1MB: Final[int] + MFD_HUGE_2MB: Final[int] + MFD_HUGE_8MB: Final[int] + MFD_HUGE_16MB: Final[int] + MFD_HUGE_32MB: Final[int] + MFD_HUGE_256MB: Final[int] + MFD_HUGE_512MB: Final[int] + MFD_HUGE_1GB: Final[int] + MFD_HUGE_2GB: Final[int] + MFD_HUGE_16GB: Final[int] def memfd_create(name: str, flags: int = ...) -> int: ... def copy_file_range(src: int, dst: int, count: int, offset_src: int | None = ..., offset_dst: int | None = ...) -> int: ... -if sys.version_info >= (3, 9): - def waitstatus_to_exitcode(status: int) -> int: ... +def waitstatus_to_exitcode(status: int) -> int: ... - if sys.platform == "linux": - def pidfd_open(pid: int, flags: int = ...) -> int: ... +if sys.platform == "linux": + def pidfd_open(pid: int, flags: int = ...) -> int: ... + +if sys.version_info >= (3, 12) and sys.platform == "linux": + PIDFD_NONBLOCK: Final = 2048 if sys.version_info >= (3, 12) and sys.platform == "win32": def listdrives() -> list[str]: ... @@ -1121,12 +1604,12 @@ if sys.version_info >= (3, 12) and sys.platform == "win32": def listvolumes() -> list[str]: ... if sys.version_info >= (3, 10) and sys.platform == "linux": - EFD_CLOEXEC: int - EFD_NONBLOCK: int - EFD_SEMAPHORE: int - SPLICE_F_MORE: int - SPLICE_F_MOVE: int - SPLICE_F_NONBLOCK: int + EFD_CLOEXEC: Final[int] + EFD_NONBLOCK: Final[int] + EFD_SEMAPHORE: Final[int] + SPLICE_F_MORE: Final[int] + SPLICE_F_MOVE: Final[int] + SPLICE_F_NONBLOCK: Final[int] def eventfd(initval: int, flags: int = 524288) -> FileDescriptor: ... def eventfd_read(fd: FileDescriptor) -> int: ... def eventfd_write(fd: FileDescriptor, value: int) -> None: ... @@ -1140,20 +1623,20 @@ if sys.version_info >= (3, 10) and sys.platform == "linux": ) -> int: ... if sys.version_info >= (3, 12) and sys.platform == "linux": - CLONE_FILES: int - CLONE_FS: int - CLONE_NEWCGROUP: int # Linux 4.6+ - CLONE_NEWIPC: int # Linux 2.6.19+ - CLONE_NEWNET: int # Linux 2.6.24+ - CLONE_NEWNS: int - CLONE_NEWPID: int # Linux 3.8+ - CLONE_NEWTIME: int # Linux 5.6+ - CLONE_NEWUSER: int # Linux 3.8+ - CLONE_NEWUTS: int # Linux 2.6.19+ - CLONE_SIGHAND: int - CLONE_SYSVSEM: int # Linux 2.6.26+ - CLONE_THREAD: int - CLONE_VM: int + CLONE_FILES: Final[int] + CLONE_FS: Final[int] + CLONE_NEWCGROUP: Final[int] # Linux 4.6+ + CLONE_NEWIPC: Final[int] # Linux 2.6.19+ + CLONE_NEWNET: Final[int] # Linux 2.6.24+ + CLONE_NEWNS: Final[int] + CLONE_NEWPID: Final[int] # Linux 3.8+ + CLONE_NEWTIME: Final[int] # Linux 5.6+ + CLONE_NEWUSER: Final[int] # Linux 3.8+ + CLONE_NEWUTS: Final[int] # Linux 2.6.19+ + CLONE_SIGHAND: Final[int] + CLONE_SYSVSEM: Final[int] # Linux 2.6.26+ + CLONE_THREAD: Final[int] + CLONE_VM: Final[int] def unshare(flags: int) -> None: ... def setns(fd: FileDescriptorLike, nstype: int = 0) -> None: ... diff --git a/mypy/typeshed/stdlib/ossaudiodev.pyi b/mypy/typeshed/stdlib/ossaudiodev.pyi index b9ee3edab033e..f8230b4f02123 100644 --- a/mypy/typeshed/stdlib/ossaudiodev.pyi +++ b/mypy/typeshed/stdlib/ossaudiodev.pyi @@ -1,119 +1,120 @@ import sys -from typing import Any, Literal, overload +from typing import Any, Final, Literal, overload if sys.platform != "win32" and sys.platform != "darwin": - AFMT_AC3: int - AFMT_A_LAW: int - AFMT_IMA_ADPCM: int - AFMT_MPEG: int - AFMT_MU_LAW: int - AFMT_QUERY: int - AFMT_S16_BE: int - AFMT_S16_LE: int - AFMT_S16_NE: int - AFMT_S8: int - AFMT_U16_BE: int - AFMT_U16_LE: int - AFMT_U8: int - SNDCTL_COPR_HALT: int - SNDCTL_COPR_LOAD: int - SNDCTL_COPR_RCODE: int - SNDCTL_COPR_RCVMSG: int - SNDCTL_COPR_RDATA: int - SNDCTL_COPR_RESET: int - SNDCTL_COPR_RUN: int - SNDCTL_COPR_SENDMSG: int - SNDCTL_COPR_WCODE: int - SNDCTL_COPR_WDATA: int - SNDCTL_DSP_BIND_CHANNEL: int - SNDCTL_DSP_CHANNELS: int - SNDCTL_DSP_GETBLKSIZE: int - SNDCTL_DSP_GETCAPS: int - SNDCTL_DSP_GETCHANNELMASK: int - SNDCTL_DSP_GETFMTS: int - SNDCTL_DSP_GETIPTR: int - SNDCTL_DSP_GETISPACE: int - SNDCTL_DSP_GETODELAY: int - SNDCTL_DSP_GETOPTR: int - SNDCTL_DSP_GETOSPACE: int - SNDCTL_DSP_GETSPDIF: int - SNDCTL_DSP_GETTRIGGER: int - SNDCTL_DSP_MAPINBUF: int - SNDCTL_DSP_MAPOUTBUF: int - SNDCTL_DSP_NONBLOCK: int - SNDCTL_DSP_POST: int - SNDCTL_DSP_PROFILE: int - SNDCTL_DSP_RESET: int - SNDCTL_DSP_SAMPLESIZE: int - SNDCTL_DSP_SETDUPLEX: int - SNDCTL_DSP_SETFMT: int - SNDCTL_DSP_SETFRAGMENT: int - SNDCTL_DSP_SETSPDIF: int - SNDCTL_DSP_SETSYNCRO: int - SNDCTL_DSP_SETTRIGGER: int - SNDCTL_DSP_SPEED: int - SNDCTL_DSP_STEREO: int - SNDCTL_DSP_SUBDIVIDE: int - SNDCTL_DSP_SYNC: int - SNDCTL_FM_4OP_ENABLE: int - SNDCTL_FM_LOAD_INSTR: int - SNDCTL_MIDI_INFO: int - SNDCTL_MIDI_MPUCMD: int - SNDCTL_MIDI_MPUMODE: int - SNDCTL_MIDI_PRETIME: int - SNDCTL_SEQ_CTRLRATE: int - SNDCTL_SEQ_GETINCOUNT: int - SNDCTL_SEQ_GETOUTCOUNT: int - SNDCTL_SEQ_GETTIME: int - SNDCTL_SEQ_NRMIDIS: int - SNDCTL_SEQ_NRSYNTHS: int - SNDCTL_SEQ_OUTOFBAND: int - SNDCTL_SEQ_PANIC: int - SNDCTL_SEQ_PERCMODE: int - SNDCTL_SEQ_RESET: int - SNDCTL_SEQ_RESETSAMPLES: int - SNDCTL_SEQ_SYNC: int - SNDCTL_SEQ_TESTMIDI: int - SNDCTL_SEQ_THRESHOLD: int - SNDCTL_SYNTH_CONTROL: int - SNDCTL_SYNTH_ID: int - SNDCTL_SYNTH_INFO: int - SNDCTL_SYNTH_MEMAVL: int - SNDCTL_SYNTH_REMOVESAMPLE: int - SNDCTL_TMR_CONTINUE: int - SNDCTL_TMR_METRONOME: int - SNDCTL_TMR_SELECT: int - SNDCTL_TMR_SOURCE: int - SNDCTL_TMR_START: int - SNDCTL_TMR_STOP: int - SNDCTL_TMR_TEMPO: int - SNDCTL_TMR_TIMEBASE: int - SOUND_MIXER_ALTPCM: int - SOUND_MIXER_BASS: int - SOUND_MIXER_CD: int - SOUND_MIXER_DIGITAL1: int - SOUND_MIXER_DIGITAL2: int - SOUND_MIXER_DIGITAL3: int - SOUND_MIXER_IGAIN: int - SOUND_MIXER_IMIX: int - SOUND_MIXER_LINE: int - SOUND_MIXER_LINE1: int - SOUND_MIXER_LINE2: int - SOUND_MIXER_LINE3: int - SOUND_MIXER_MIC: int - SOUND_MIXER_MONITOR: int - SOUND_MIXER_NRDEVICES: int - SOUND_MIXER_OGAIN: int - SOUND_MIXER_PCM: int - SOUND_MIXER_PHONEIN: int - SOUND_MIXER_PHONEOUT: int - SOUND_MIXER_RADIO: int - SOUND_MIXER_RECLEV: int - SOUND_MIXER_SPEAKER: int - SOUND_MIXER_SYNTH: int - SOUND_MIXER_TREBLE: int - SOUND_MIXER_VIDEO: int - SOUND_MIXER_VOLUME: int + # Depends on soundcard.h + AFMT_AC3: Final[int] + AFMT_A_LAW: Final[int] + AFMT_IMA_ADPCM: Final[int] + AFMT_MPEG: Final[int] + AFMT_MU_LAW: Final[int] + AFMT_QUERY: Final[int] + AFMT_S16_BE: Final[int] + AFMT_S16_LE: Final[int] + AFMT_S16_NE: Final[int] + AFMT_S8: Final[int] + AFMT_U16_BE: Final[int] + AFMT_U16_LE: Final[int] + AFMT_U8: Final[int] + SNDCTL_COPR_HALT: Final[int] + SNDCTL_COPR_LOAD: Final[int] + SNDCTL_COPR_RCODE: Final[int] + SNDCTL_COPR_RCVMSG: Final[int] + SNDCTL_COPR_RDATA: Final[int] + SNDCTL_COPR_RESET: Final[int] + SNDCTL_COPR_RUN: Final[int] + SNDCTL_COPR_SENDMSG: Final[int] + SNDCTL_COPR_WCODE: Final[int] + SNDCTL_COPR_WDATA: Final[int] + SNDCTL_DSP_BIND_CHANNEL: Final[int] + SNDCTL_DSP_CHANNELS: Final[int] + SNDCTL_DSP_GETBLKSIZE: Final[int] + SNDCTL_DSP_GETCAPS: Final[int] + SNDCTL_DSP_GETCHANNELMASK: Final[int] + SNDCTL_DSP_GETFMTS: Final[int] + SNDCTL_DSP_GETIPTR: Final[int] + SNDCTL_DSP_GETISPACE: Final[int] + SNDCTL_DSP_GETODELAY: Final[int] + SNDCTL_DSP_GETOPTR: Final[int] + SNDCTL_DSP_GETOSPACE: Final[int] + SNDCTL_DSP_GETSPDIF: Final[int] + SNDCTL_DSP_GETTRIGGER: Final[int] + SNDCTL_DSP_MAPINBUF: Final[int] + SNDCTL_DSP_MAPOUTBUF: Final[int] + SNDCTL_DSP_NONBLOCK: Final[int] + SNDCTL_DSP_POST: Final[int] + SNDCTL_DSP_PROFILE: Final[int] + SNDCTL_DSP_RESET: Final[int] + SNDCTL_DSP_SAMPLESIZE: Final[int] + SNDCTL_DSP_SETDUPLEX: Final[int] + SNDCTL_DSP_SETFMT: Final[int] + SNDCTL_DSP_SETFRAGMENT: Final[int] + SNDCTL_DSP_SETSPDIF: Final[int] + SNDCTL_DSP_SETSYNCRO: Final[int] + SNDCTL_DSP_SETTRIGGER: Final[int] + SNDCTL_DSP_SPEED: Final[int] + SNDCTL_DSP_STEREO: Final[int] + SNDCTL_DSP_SUBDIVIDE: Final[int] + SNDCTL_DSP_SYNC: Final[int] + SNDCTL_FM_4OP_ENABLE: Final[int] + SNDCTL_FM_LOAD_INSTR: Final[int] + SNDCTL_MIDI_INFO: Final[int] + SNDCTL_MIDI_MPUCMD: Final[int] + SNDCTL_MIDI_MPUMODE: Final[int] + SNDCTL_MIDI_PRETIME: Final[int] + SNDCTL_SEQ_CTRLRATE: Final[int] + SNDCTL_SEQ_GETINCOUNT: Final[int] + SNDCTL_SEQ_GETOUTCOUNT: Final[int] + SNDCTL_SEQ_GETTIME: Final[int] + SNDCTL_SEQ_NRMIDIS: Final[int] + SNDCTL_SEQ_NRSYNTHS: Final[int] + SNDCTL_SEQ_OUTOFBAND: Final[int] + SNDCTL_SEQ_PANIC: Final[int] + SNDCTL_SEQ_PERCMODE: Final[int] + SNDCTL_SEQ_RESET: Final[int] + SNDCTL_SEQ_RESETSAMPLES: Final[int] + SNDCTL_SEQ_SYNC: Final[int] + SNDCTL_SEQ_TESTMIDI: Final[int] + SNDCTL_SEQ_THRESHOLD: Final[int] + SNDCTL_SYNTH_CONTROL: Final[int] + SNDCTL_SYNTH_ID: Final[int] + SNDCTL_SYNTH_INFO: Final[int] + SNDCTL_SYNTH_MEMAVL: Final[int] + SNDCTL_SYNTH_REMOVESAMPLE: Final[int] + SNDCTL_TMR_CONTINUE: Final[int] + SNDCTL_TMR_METRONOME: Final[int] + SNDCTL_TMR_SELECT: Final[int] + SNDCTL_TMR_SOURCE: Final[int] + SNDCTL_TMR_START: Final[int] + SNDCTL_TMR_STOP: Final[int] + SNDCTL_TMR_TEMPO: Final[int] + SNDCTL_TMR_TIMEBASE: Final[int] + SOUND_MIXER_ALTPCM: Final[int] + SOUND_MIXER_BASS: Final[int] + SOUND_MIXER_CD: Final[int] + SOUND_MIXER_DIGITAL1: Final[int] + SOUND_MIXER_DIGITAL2: Final[int] + SOUND_MIXER_DIGITAL3: Final[int] + SOUND_MIXER_IGAIN: Final[int] + SOUND_MIXER_IMIX: Final[int] + SOUND_MIXER_LINE: Final[int] + SOUND_MIXER_LINE1: Final[int] + SOUND_MIXER_LINE2: Final[int] + SOUND_MIXER_LINE3: Final[int] + SOUND_MIXER_MIC: Final[int] + SOUND_MIXER_MONITOR: Final[int] + SOUND_MIXER_NRDEVICES: Final[int] + SOUND_MIXER_OGAIN: Final[int] + SOUND_MIXER_PCM: Final[int] + SOUND_MIXER_PHONEIN: Final[int] + SOUND_MIXER_PHONEOUT: Final[int] + SOUND_MIXER_RADIO: Final[int] + SOUND_MIXER_RECLEV: Final[int] + SOUND_MIXER_SPEAKER: Final[int] + SOUND_MIXER_SYNTH: Final[int] + SOUND_MIXER_TREBLE: Final[int] + SOUND_MIXER_VIDEO: Final[int] + SOUND_MIXER_VOLUME: Final[int] control_labels: list[str] control_names: list[str] diff --git a/mypy/typeshed/stdlib/parser.pyi b/mypy/typeshed/stdlib/parser.pyi index bafc8015fed9c..9b287fcc6529d 100644 --- a/mypy/typeshed/stdlib/parser.pyi +++ b/mypy/typeshed/stdlib/parser.pyi @@ -1,14 +1,14 @@ from _typeshed import StrOrBytesPath from collections.abc import Sequence from types import CodeType -from typing import Any, final +from typing import Any, ClassVar, final def expr(source: str) -> STType: ... def suite(source: str) -> STType: ... def sequence2st(sequence: Sequence[Any]) -> STType: ... def tuple2st(sequence: Sequence[Any]) -> STType: ... -def st2list(st: STType, line_info: bool = ..., col_info: bool = ...) -> list[Any]: ... -def st2tuple(st: STType, line_info: bool = ..., col_info: bool = ...) -> tuple[Any, ...]: ... +def st2list(st: STType, line_info: bool = False, col_info: bool = False) -> list[Any]: ... +def st2tuple(st: STType, line_info: bool = False, col_info: bool = False) -> tuple[Any, ...]: ... def compilest(st: STType, filename: StrOrBytesPath = ...) -> CodeType: ... def isexpr(st: STType) -> bool: ... def issuite(st: STType) -> bool: ... @@ -17,8 +17,9 @@ class ParserError(Exception): ... @final class STType: + __hash__: ClassVar[None] # type: ignore[assignment] def compile(self, filename: StrOrBytesPath = ...) -> CodeType: ... def isexpr(self) -> bool: ... def issuite(self) -> bool: ... - def tolist(self, line_info: bool = ..., col_info: bool = ...) -> list[Any]: ... - def totuple(self, line_info: bool = ..., col_info: bool = ...) -> tuple[Any, ...]: ... + def tolist(self, line_info: bool = False, col_info: bool = False) -> list[Any]: ... + def totuple(self, line_info: bool = False, col_info: bool = False) -> tuple[Any, ...]: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib/__init__.pyi similarity index 69% rename from mypy/typeshed/stdlib/pathlib.pyi rename to mypy/typeshed/stdlib/pathlib/__init__.pyi index bdca375f626da..fa5143f202927 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib/__init__.pyi @@ -14,19 +14,46 @@ from _typeshed import ( from collections.abc import Callable, Generator, Iterator, Sequence from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from os import PathLike, stat_result -from types import TracebackType -from typing import IO, Any, BinaryIO, ClassVar, Literal, overload -from typing_extensions import Self, deprecated +from types import GenericAlias, TracebackType +from typing import IO, Any, BinaryIO, ClassVar, Literal, TypeVar, overload +from typing_extensions import Never, Self, deprecated -if sys.version_info >= (3, 9): - from types import GenericAlias +_PathT = TypeVar("_PathT", bound=PurePath) __all__ = ["PurePath", "PurePosixPath", "PureWindowsPath", "Path", "PosixPath", "WindowsPath"] +if sys.version_info >= (3, 14): + from pathlib.types import PathInfo + if sys.version_info >= (3, 13): __all__ += ["UnsupportedOperation"] class PurePath(PathLike[str]): + if sys.version_info >= (3, 13): + __slots__ = ( + "_raw_paths", + "_drv", + "_root", + "_tail_cached", + "_str", + "_str_normcase_cached", + "_parts_normcase_cached", + "_hash", + ) + elif sys.version_info >= (3, 12): + __slots__ = ( + "_raw_paths", + "_drv", + "_root", + "_tail_cached", + "_str", + "_str_normcase_cached", + "_parts_normcase_cached", + "_lines_cached", + "_hash", + ) + else: + __slots__ = ("_drv", "_root", "_parts", "_str", "_hash", "_pparts", "_cached_cparts") if sys.version_info >= (3, 13): parser: ClassVar[types.ModuleType] def full_match(self, pattern: StrPath, *, case_sensitive: bool | None = None) -> bool: ... @@ -65,10 +92,19 @@ class PurePath(PathLike[str]): def as_posix(self) -> str: ... def as_uri(self) -> str: ... def is_absolute(self) -> bool: ... - def is_reserved(self) -> bool: ... - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 13): + @deprecated( + "Deprecated since Python 3.13; will be removed in Python 3.15. " + "Use `os.path.isreserved()` to detect reserved paths on Windows." + ) + def is_reserved(self) -> bool: ... + else: + def is_reserved(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_relative_to(self, other: StrPath) -> bool: ... + elif sys.version_info >= (3, 12): def is_relative_to(self, other: StrPath, /, *_deprecated: StrPath) -> bool: ... - elif sys.version_info >= (3, 9): + else: def is_relative_to(self, *other: StrPath) -> bool: ... if sys.version_info >= (3, 12): @@ -76,31 +112,41 @@ class PurePath(PathLike[str]): else: def match(self, path_pattern: str) -> bool: ... - if sys.version_info >= (3, 12): + if sys.version_info >= (3, 14): + def relative_to(self, other: StrPath, *, walk_up: bool = False) -> Self: ... + elif sys.version_info >= (3, 12): def relative_to(self, other: StrPath, /, *_deprecated: StrPath, walk_up: bool = False) -> Self: ... else: def relative_to(self, *other: StrPath) -> Self: ... def with_name(self, name: str) -> Self: ... - if sys.version_info >= (3, 9): - def with_stem(self, stem: str) -> Self: ... - + def with_stem(self, stem: str) -> Self: ... def with_suffix(self, suffix: str) -> Self: ... def joinpath(self, *other: StrPath) -> Self: ... @property def parents(self) -> Sequence[Self]: ... @property def parent(self) -> Self: ... - if sys.version_info >= (3, 9) and sys.version_info < (3, 11): + if sys.version_info < (3, 11): def __class_getitem__(cls, type: Any) -> GenericAlias: ... if sys.version_info >= (3, 12): def with_segments(self, *args: StrPath) -> Self: ... -class PurePosixPath(PurePath): ... -class PureWindowsPath(PurePath): ... +class PurePosixPath(PurePath): + __slots__ = () + +class PureWindowsPath(PurePath): + __slots__ = () class Path(PurePath): + if sys.version_info >= (3, 14): + __slots__ = ("_info",) + elif sys.version_info >= (3, 10): + __slots__ = () + else: + __slots__ = ("_accessor",) + if sys.version_info >= (3, 12): def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... # pyright: ignore[reportInconsistentConstructor] else: @@ -129,12 +175,10 @@ class Path(PurePath): def read_text(self, encoding: str | None = None, errors: str | None = None) -> str: ... if sys.version_info >= (3, 13): - def glob( - self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False - ) -> Generator[Self, None, None]: ... + def glob(self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False) -> Iterator[Self]: ... def rglob( self, pattern: str, *, case_sensitive: bool | None = None, recurse_symlinks: bool = False - ) -> Generator[Self, None, None]: ... + ) -> Iterator[Self]: ... elif sys.version_info >= (3, 12): def glob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... def rglob(self, pattern: str, *, case_sensitive: bool | None = None) -> Generator[Self, None, None]: ... @@ -161,17 +205,24 @@ class Path(PurePath): 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: ... + @property + def info(self) -> PathInfo: ... + @overload + def move_into(self, target_dir: _PathT) -> _PathT: ... # type: ignore[overload-overlap] + @overload + def move_into(self, target_dir: StrPath) -> Self: ... # type: ignore[overload-overlap] + @overload + def move(self, target: _PathT) -> _PathT: ... # type: ignore[overload-overlap] + @overload + def move(self, target: StrPath) -> Self: ... # type: ignore[overload-overlap] + @overload + def copy_into(self, target_dir: _PathT, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> _PathT: ... # type: ignore[overload-overlap] + @overload + def copy_into(self, target_dir: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> Self: ... # type: ignore[overload-overlap] + @overload + def copy(self, target: _PathT, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> _PathT: ... # type: ignore[overload-overlap] + @overload + def copy(self, target: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> Self: ... # type: ignore[overload-overlap] # Adapted from builtins.open # Text mode: always returns a TextIOWrapper @@ -228,9 +279,17 @@ class Path(PurePath): def open( self, mode: str, buffering: int = -1, encoding: str | None = None, errors: str | None = None, newline: str | None = None ) -> IO[Any]: ... - if sys.platform != "win32": - # These methods do "exist" on Windows, but they always raise NotImplementedError, - # so it's safer to pretend they don't exist + + # These methods do "exist" on Windows, but they always raise NotImplementedError. + if sys.platform == "win32": + if sys.version_info >= (3, 13): + # raises UnsupportedOperation: + def owner(self: Never, *, follow_symlinks: bool = True) -> str: ... # type: ignore[misc] + def group(self: Never, *, follow_symlinks: bool = True) -> str: ... # type: ignore[misc] + else: + def owner(self: Never) -> str: ... # type: ignore[misc] + def group(self: Never) -> str: ... # type: ignore[misc] + else: if sys.version_info >= (3, 13): def owner(self, *, follow_symlinks: bool = True) -> str: ... def group(self, *, follow_symlinks: bool = True) -> str: ... @@ -240,11 +299,12 @@ class Path(PurePath): # This method does "exist" on Windows on <3.12, but always raises NotImplementedError # On py312+, it works properly on Windows, as with all other platforms - if sys.platform != "win32" or sys.version_info >= (3, 12): + if sys.platform == "win32" and sys.version_info < (3, 12): + def is_mount(self: Never) -> bool: ... # type: ignore[misc] + else: def is_mount(self) -> bool: ... - if sys.version_info >= (3, 9): - def readlink(self) -> Self: ... + def readlink(self) -> Self: ... if sys.version_info >= (3, 10): def rename(self, target: StrPath) -> Self: ... @@ -255,9 +315,6 @@ class Path(PurePath): 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: ... @@ -279,20 +336,20 @@ class Path(PurePath): def write_text(self, data: str, encoding: str | None = None, errors: str | None = None) -> int: ... if sys.version_info < (3, 12): if sys.version_info >= (3, 10): - @deprecated("Deprecated as of Python 3.10 and removed in Python 3.12. Use hardlink_to() instead.") + @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `hardlink_to()` instead.") def link_to(self, target: StrOrBytesPath) -> None: ... else: def link_to(self, target: StrOrBytesPath) -> None: ... if sys.version_info >= (3, 12): def walk( - self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ... + self, top_down: bool = True, on_error: Callable[[OSError], object] | None = None, follow_symlinks: bool = False ) -> 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): + __slots__ = () -class PosixPath(Path, PurePosixPath): ... -class WindowsPath(Path, PureWindowsPath): ... +class WindowsPath(Path, PureWindowsPath): + __slots__ = () if sys.version_info >= (3, 13): class UnsupportedOperation(NotImplementedError): ... diff --git a/mypy/typeshed/stdlib/pathlib/types.pyi b/mypy/typeshed/stdlib/pathlib/types.pyi new file mode 100644 index 0000000000000..9f9a650846deb --- /dev/null +++ b/mypy/typeshed/stdlib/pathlib/types.pyi @@ -0,0 +1,8 @@ +from typing import Protocol, runtime_checkable + +@runtime_checkable +class PathInfo(Protocol): + def exists(self, *, follow_symlinks: bool = True) -> bool: ... + def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... + def is_file(self, *, follow_symlinks: bool = True) -> bool: ... + def is_symlink(self) -> bool: ... diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index 61e8b7176e849..2f114b20572df 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -1,19 +1,24 @@ import signal import sys -from bdb import Bdb +from bdb import Bdb, _Backend from cmd import Cmd from collections.abc import Callable, Iterable, Mapping, Sequence from inspect import _SourceObjectType +from linecache import _ModuleGlobals +from rlcompleter import Completer from types import CodeType, FrameType, TracebackType -from typing import IO, Any, ClassVar, Final, TypeVar -from typing_extensions import ParamSpec, Self +from typing import IO, Any, ClassVar, Final, Literal, TypeVar +from typing_extensions import ParamSpec, Self, TypeAlias __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", "post_mortem", "help"] +if sys.version_info >= (3, 14): + __all__ += ["set_default_backend", "get_default_backend"] _T = TypeVar("_T") _P = ParamSpec("_P") +_Mode: TypeAlias = Literal["inline", "cli"] -line_prefix: str # undocumented +line_prefix: Final[str] # undocumented class Restart(Exception): ... @@ -21,7 +26,16 @@ def run(statement: str, globals: dict[str, Any] | None = None, locals: Mapping[s def runeval(expression: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> Any: ... def runctx(statement: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> None: ... def runcall(func: Callable[_P, _T], *args: _P.args, **kwds: _P.kwargs) -> _T | None: ... -def set_trace(*, header: str | None = None) -> None: ... + +if sys.version_info >= (3, 14): + def set_default_backend(backend: _Backend) -> None: ... + def get_default_backend() -> _Backend: ... + def set_trace(*, header: str | None = None, commands: Iterable[str] | None = None) -> None: ... + async def set_trace_async(*, header: str | None = None, commands: Iterable[str] | None = None) -> None: ... + +else: + def set_trace(*, header: str | None = None) -> None: ... + def post_mortem(t: TracebackType | None = None) -> None: ... def pm() -> None: ... @@ -47,15 +61,35 @@ class Pdb(Bdb, Cmd): curindex: int curframe: FrameType | None curframe_locals: Mapping[str, Any] - def __init__( - self, - completekey: str = "tab", - stdin: IO[str] | None = None, - stdout: IO[str] | None = None, - skip: Iterable[str] | None = None, - nosigint: bool = False, - readrc: bool = True, - ) -> None: ... + if sys.version_info >= (3, 14): + mode: _Mode | None + colorize: bool + def __init__( + self, + completekey: str = "tab", + stdin: IO[str] | None = None, + stdout: IO[str] | None = None, + skip: Iterable[str] | None = None, + nosigint: bool = False, + readrc: bool = True, + mode: _Mode | None = None, + backend: _Backend | None = None, + colorize: bool = False, + ) -> None: ... + else: + def __init__( + self, + completekey: str = "tab", + stdin: IO[str] | None = None, + stdout: IO[str] | None = None, + skip: Iterable[str] | None = None, + nosigint: bool = False, + readrc: bool = True, + ) -> None: ... + if sys.version_info >= (3, 14): + def set_trace(self, frame: FrameType | None = None, *, commands: Iterable[str] | None = None) -> None: ... + async def set_trace_async(self, frame: FrameType | None = None, *, commands: Iterable[str] | None = None) -> None: ... + def forget(self) -> None: ... def setup(self, f: FrameType | None, tb: TracebackType | None) -> None: ... if sys.version_info < (3, 11): @@ -75,19 +109,34 @@ class Pdb(Bdb, Cmd): def handle_command_def(self, line: str) -> bool: ... def defaultFile(self) -> str: ... def lineinfo(self, identifier: str) -> tuple[None, None, None] | tuple[str, str, int]: ... - def checkline(self, filename: str, lineno: int) -> int: ... + if sys.version_info >= (3, 14): + def checkline(self, filename: str, lineno: int, module_globals: _ModuleGlobals | None = None) -> int: ... + else: + def checkline(self, filename: str, lineno: int) -> int: ... + def _getval(self, arg: str) -> object: ... - def print_stack_trace(self) -> None: ... + if sys.version_info >= (3, 14): + def print_stack_trace(self, count: int | None = None) -> None: ... + else: + def print_stack_trace(self) -> None: ... + def print_stack_entry(self, frame_lineno: tuple[FrameType, int], prompt_prefix: str = "\n-> ") -> None: ... def lookupmodule(self, filename: str) -> str | None: ... if sys.version_info < (3, 11): def _runscript(self, filename: str) -> None: ... + if sys.version_info >= (3, 14): + def complete_multiline_names(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... + 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: ... + if sys.version_info >= (3, 14): + def do_break(self, arg: str, temporary: bool = False) -> bool | None: ... + else: + def do_break(self, arg: str, temporary: bool | Literal[0, 1] = 0) -> bool | None: ... + def do_tbreak(self, arg: str) -> bool | None: ... def do_enable(self, arg: str) -> bool | None: ... def do_disable(self, arg: str) -> bool | None: ... @@ -152,6 +201,10 @@ class Pdb(Bdb, Cmd): 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: ... + if sys.version_info >= (3, 13) and sys.version_info < (3, 14): + # Added in 3.13.8. + @property + def rlcompleter(self) -> type[Completer]: ... def _select_frame(self, number: int) -> None: ... def _getval_except(self, arg: str, frame: FrameType | None = None) -> object: ... diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 98ec80b0f14e2..d94fe208f4468 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -1,7 +1,21 @@ +from _pickle import ( + PickleError as PickleError, + Pickler as Pickler, + PicklingError as PicklingError, + Unpickler as Unpickler, + UnpicklingError as UnpicklingError, + _BufferCallback, + _ReadableFileobj, + _ReducedType, + dump as dump, + dumps as dumps, + load as load, + loads as loads, +) from _typeshed import ReadableBuffer, SupportsWrite -from collections.abc import Callable, Iterable, Iterator, Mapping -from typing import Any, ClassVar, Protocol, SupportsBytes, SupportsIndex, final -from typing_extensions import TypeAlias +from collections.abc import Callable, Iterable, Mapping +from typing import Any, ClassVar, Final, SupportsBytes, SupportsIndex, final +from typing_extensions import Self __all__ = [ "PickleBuffer", @@ -88,184 +102,132 @@ __all__ = [ "UNICODE", ] -HIGHEST_PROTOCOL: int -DEFAULT_PROTOCOL: int +HIGHEST_PROTOCOL: Final = 5 +DEFAULT_PROTOCOL: Final = 5 bytes_types: tuple[type[Any], ...] # undocumented -class _ReadableFileobj(Protocol): - def read(self, n: int, /) -> bytes: ... - def readline(self) -> bytes: ... - @final class PickleBuffer: - def __init__(self, buffer: ReadableBuffer) -> None: ... + def __new__(cls, buffer: ReadableBuffer) -> Self: ... def raw(self) -> memoryview: ... def release(self) -> None: ... def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... -_BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None +MARK: Final = b"(" +STOP: Final = b"." +POP: Final = b"0" +POP_MARK: Final = b"1" +DUP: Final = b"2" +FLOAT: Final = b"F" +INT: Final = b"I" +BININT: Final = b"J" +BININT1: Final = b"K" +LONG: Final = b"L" +BININT2: Final = b"M" +NONE: Final = b"N" +PERSID: Final = b"P" +BINPERSID: Final = b"Q" +REDUCE: Final = b"R" +STRING: Final = b"S" +BINSTRING: Final = b"T" +SHORT_BINSTRING: Final = b"U" +UNICODE: Final = b"V" +BINUNICODE: Final = b"X" +APPEND: Final = b"a" +BUILD: Final = b"b" +GLOBAL: Final = b"c" +DICT: Final = b"d" +EMPTY_DICT: Final = b"}" +APPENDS: Final = b"e" +GET: Final = b"g" +BINGET: Final = b"h" +INST: Final = b"i" +LONG_BINGET: Final = b"j" +LIST: Final = b"l" +EMPTY_LIST: Final = b"]" +OBJ: Final = b"o" +PUT: Final = b"p" +BINPUT: Final = b"q" +LONG_BINPUT: Final = b"r" +SETITEM: Final = b"s" +TUPLE: Final = b"t" +EMPTY_TUPLE: Final = b")" +SETITEMS: Final = b"u" +BINFLOAT: Final = b"G" + +TRUE: Final = b"I01\n" +FALSE: Final = b"I00\n" -def dump( - obj: Any, - file: SupportsWrite[bytes], - protocol: int | None = None, - *, - fix_imports: bool = True, - buffer_callback: _BufferCallback = None, -) -> None: ... -def dumps( - obj: Any, protocol: int | None = None, *, fix_imports: bool = True, buffer_callback: _BufferCallback = None -) -> bytes: ... -def load( - file: _ReadableFileobj, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), -) -> Any: ... -def loads( - data: ReadableBuffer, - /, - *, - fix_imports: bool = True, - encoding: str = "ASCII", - errors: str = "strict", - buffers: Iterable[Any] | None = (), -) -> Any: ... +# protocol 2 +PROTO: Final = b"\x80" +NEWOBJ: Final = b"\x81" +EXT1: Final = b"\x82" +EXT2: Final = b"\x83" +EXT4: Final = b"\x84" +TUPLE1: Final = b"\x85" +TUPLE2: Final = b"\x86" +TUPLE3: Final = b"\x87" +NEWTRUE: Final = b"\x88" +NEWFALSE: Final = b"\x89" +LONG1: Final = b"\x8a" +LONG4: Final = b"\x8b" -class PickleError(Exception): ... -class PicklingError(PickleError): ... -class UnpicklingError(PickleError): ... +# protocol 3 +BINBYTES: Final = b"B" +SHORT_BINBYTES: Final = b"C" -_ReducedType: TypeAlias = ( - str - | tuple[Callable[..., Any], tuple[Any, ...]] - | tuple[Callable[..., Any], tuple[Any, ...], Any] - | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None] - | tuple[Callable[..., Any], tuple[Any, ...], Any, Iterator[Any] | None, Iterator[Any] | None] -) +# protocol 4 +SHORT_BINUNICODE: Final = b"\x8c" +BINUNICODE8: Final = b"\x8d" +BINBYTES8: Final = b"\x8e" +EMPTY_SET: Final = b"\x8f" +ADDITEMS: Final = b"\x90" +FROZENSET: Final = b"\x91" +NEWOBJ_EX: Final = b"\x92" +STACK_GLOBAL: Final = b"\x93" +MEMOIZE: Final = b"\x94" +FRAME: Final = b"\x95" + +# protocol 5 +BYTEARRAY8: Final = b"\x96" +NEXT_BUFFER: Final = b"\x97" +READONLY_BUFFER: Final = b"\x98" + +def encode_long(x: int) -> bytes: ... # undocumented +def decode_long(data: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer) -> int: ... # undocumented -class Pickler: +# undocumented pure-Python implementations +class _Pickler: fast: bool dispatch_table: Mapping[type, Callable[[Any], _ReducedType]] bin: bool # undocumented dispatch: ClassVar[dict[type, Callable[[Unpickler, Any], None]]] # undocumented, _Pickler only - + reducer_override: Callable[[Any], Any] 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: ... + def dump(self, obj: Any) -> None: ... def clear_memo(self) -> None: ... def persistent_id(self, obj: Any) -> Any: ... -class Unpickler: +class _Unpickler: dispatch: ClassVar[dict[int, Callable[[Unpickler], None]]] # undocumented, _Unpickler only - def __init__( 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, ) -> None: ... def load(self) -> Any: ... - def find_class(self, module_name: str, global_name: str, /) -> Any: ... + def find_class(self, module: str, name: str) -> Any: ... def persistent_load(self, pid: Any) -> Any: ... - -MARK: bytes -STOP: bytes -POP: bytes -POP_MARK: bytes -DUP: bytes -FLOAT: bytes -INT: bytes -BININT: bytes -BININT1: bytes -LONG: bytes -BININT2: bytes -NONE: bytes -PERSID: bytes -BINPERSID: bytes -REDUCE: bytes -STRING: bytes -BINSTRING: bytes -SHORT_BINSTRING: bytes -UNICODE: bytes -BINUNICODE: bytes -APPEND: bytes -BUILD: bytes -GLOBAL: bytes -DICT: bytes -EMPTY_DICT: bytes -APPENDS: bytes -GET: bytes -BINGET: bytes -INST: bytes -LONG_BINGET: bytes -LIST: bytes -EMPTY_LIST: bytes -OBJ: bytes -PUT: bytes -BINPUT: bytes -LONG_BINPUT: bytes -SETITEM: bytes -TUPLE: bytes -EMPTY_TUPLE: bytes -SETITEMS: bytes -BINFLOAT: bytes - -TRUE: bytes -FALSE: bytes - -# protocol 2 -PROTO: bytes -NEWOBJ: bytes -EXT1: bytes -EXT2: bytes -EXT4: bytes -TUPLE1: bytes -TUPLE2: bytes -TUPLE3: bytes -NEWTRUE: bytes -NEWFALSE: bytes -LONG1: bytes -LONG4: bytes - -# protocol 3 -BINBYTES: bytes -SHORT_BINBYTES: bytes - -# protocol 4 -SHORT_BINUNICODE: bytes -BINUNICODE8: bytes -BINBYTES8: bytes -EMPTY_SET: bytes -ADDITEMS: bytes -FROZENSET: bytes -NEWOBJ_EX: bytes -STACK_GLOBAL: bytes -MEMOIZE: bytes -FRAME: bytes - -# protocol 5 -BYTEARRAY8: bytes -NEXT_BUFFER: bytes -READONLY_BUFFER: bytes - -def encode_long(x: int) -> bytes: ... # undocumented -def decode_long(data: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer) -> int: ... # undocumented - -# pure-Python implementations -_Pickler = Pickler # undocumented -_Unpickler = Unpickler # undocumented diff --git a/mypy/typeshed/stdlib/pickletools.pyi b/mypy/typeshed/stdlib/pickletools.pyi index 542172814926c..8bbfaba31b671 100644 --- a/mypy/typeshed/stdlib/pickletools.pyi +++ b/mypy/typeshed/stdlib/pickletools.pyi @@ -1,5 +1,6 @@ +import sys from collections.abc import Callable, Iterator, MutableMapping -from typing import IO, Any +from typing import IO, Any, Final from typing_extensions import TypeAlias __all__ = ["dis", "genops", "optimize"] @@ -7,13 +8,14 @@ __all__ = ["dis", "genops", "optimize"] _Reader: TypeAlias = Callable[[IO[bytes]], Any] bytes_types: tuple[type[Any], ...] -UP_TO_NEWLINE: int -TAKEN_FROM_ARGUMENT1: int -TAKEN_FROM_ARGUMENT4: int -TAKEN_FROM_ARGUMENT4U: int -TAKEN_FROM_ARGUMENT8U: int +UP_TO_NEWLINE: Final = -1 +TAKEN_FROM_ARGUMENT1: Final = -2 +TAKEN_FROM_ARGUMENT4: Final = -3 +TAKEN_FROM_ARGUMENT4U: Final = -4 +TAKEN_FROM_ARGUMENT8U: Final = -5 class ArgumentDescriptor: + __slots__ = ("name", "n", "reader", "doc") name: str n: int reader: _Reader @@ -40,7 +42,13 @@ def read_uint8(f: IO[bytes]) -> int: ... uint8: ArgumentDescriptor -def read_stringnl(f: IO[bytes], decode: bool = True, stripquotes: bool = True) -> bytes | str: ... +if sys.version_info >= (3, 12): + def read_stringnl( + f: IO[bytes], decode: bool = True, stripquotes: bool = True, *, encoding: str = "latin-1" + ) -> bytes | str: ... + +else: + def read_stringnl(f: IO[bytes], decode: bool = True, stripquotes: bool = True) -> bytes | str: ... stringnl: ArgumentDescriptor @@ -111,6 +119,7 @@ def read_long4(f: IO[bytes]) -> int: ... long4: ArgumentDescriptor class StackObject: + __slots__ = ("name", "obtype", "doc") name: str obtype: type[Any] | tuple[type[Any], ...] doc: str @@ -136,6 +145,7 @@ markobject: StackObject stackslice: StackObject class OpcodeInfo: + __slots__ = ("name", "code", "arg", "stack_before", "stack_after", "proto", "doc") name: str code: str arg: ArgumentDescriptor | None diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi index 7e7fa4fda9a18..7c70dcc4c5ab1 100644 --- a/mypy/typeshed/stdlib/pkgutil.pyi +++ b/mypy/typeshed/stdlib/pkgutil.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import SupportsRead +from _typeshed import StrOrBytesPath, SupportsRead from _typeshed.importlib import LoaderProtocol, MetaPathFinderProtocol, PathEntryFinderProtocol from collections.abc import Callable, Iterable, Iterator from typing import IO, Any, NamedTuple, TypeVar @@ -8,8 +8,6 @@ from typing_extensions import deprecated __all__ = [ "get_importer", "iter_importers", - "get_loader", - "find_loader", "walk_packages", "iter_modules", "get_data", @@ -17,6 +15,8 @@ __all__ = [ "extend_path", "ModuleInfo", ] +if sys.version_info < (3, 14): + __all__ += ["get_loader", "find_loader"] if sys.version_info < (3, 12): __all__ += ["ImpImporter", "ImpLoader"] @@ -30,24 +30,30 @@ class ModuleInfo(NamedTuple): def extend_path(path: _PathT, name: str) -> _PathT: ... if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use the `importlib` module instead.") class ImpImporter: - def __init__(self, path: str | None = None) -> None: ... + def __init__(self, path: StrOrBytesPath | None = None) -> None: ... + @deprecated("Deprecated since Python 3.3; removed in Python 3.12. Use the `importlib` module instead.") class ImpLoader: - def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ... - -@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.") -def find_loader(fullname: str) -> LoaderProtocol | None: ... -def get_importer(path_item: str) -> PathEntryFinderProtocol | None: ... -@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.") -def get_loader(module_or_name: str) -> LoaderProtocol | None: ... + def __init__(self, fullname: str, file: IO[str], filename: StrOrBytesPath, etc: tuple[str, str, int]) -> None: ... + +if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `importlib.util.find_spec()` instead.") + def find_loader(fullname: str) -> LoaderProtocol | None: ... + @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `importlib.util.find_spec()` instead.") + def get_loader(module_or_name: str) -> LoaderProtocol | None: ... + else: + def find_loader(fullname: str) -> LoaderProtocol | None: ... + def get_loader(module_or_name: str) -> LoaderProtocol | None: ... + +def get_importer(path_item: StrOrBytesPath) -> PathEntryFinderProtocol | None: ... def iter_importers(fullname: str = "") -> Iterator[MetaPathFinderProtocol | PathEntryFinderProtocol]: ... -def iter_modules(path: Iterable[str] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ... +def iter_modules(path: Iterable[StrOrBytesPath] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ... def read_code(stream: SupportsRead[bytes]) -> Any: ... # undocumented def walk_packages( - path: Iterable[str] | None = None, prefix: str = "", onerror: Callable[[str], object] | None = None + path: Iterable[StrOrBytesPath] | None = None, prefix: str = "", onerror: Callable[[str], object] | None = None ) -> Iterator[ModuleInfo]: ... def get_data(package: str, resource: str) -> bytes | None: ... - -if sys.version_info >= (3, 9): - def resolve_name(name: str) -> Any: ... +def resolve_name(name: str) -> Any: ... diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi index c47ecdc51df49..69d702bb155cd 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, deprecated, disjoint_base 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]: ... @@ -8,20 +9,61 @@ def win32_is_iot() -> bool: ... def mac_ver( release: str = "", versioninfo: tuple[str, str, str] = ("", "", ""), machine: str = "" ) -> tuple[str, tuple[str, str, str], str]: ... -def java_ver( - release: str = "", vendor: str = "", vminfo: tuple[str, str, str] = ("", "", ""), osinfo: tuple[str, str, str] = ("", "", "") -) -> tuple[str, str, tuple[str, str, str], tuple[str, str, str]]: ... + +if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def java_ver( + release: str = "", + vendor: str = "", + vminfo: tuple[str, str, str] = ("", "", ""), + osinfo: tuple[str, str, str] = ("", "", ""), + ) -> tuple[str, str, tuple[str, str, str], tuple[str, str, str]]: ... + +else: + def java_ver( + release: str = "", + vendor: str = "", + vminfo: tuple[str, str, str] = ("", "", ""), + osinfo: tuple[str, str, str] = ("", "", ""), + ) -> tuple[str, str, tuple[str, str, str], tuple[str, str, str]]: ... + 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): +# 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. +if sys.version_info >= (3, 12): + class uname_result(_uname_result_base): + __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: + @disjoint_base + 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: ... + def uname() -> uname_result: ... def system() -> str: ... def node() -> str: ... @@ -36,7 +78,7 @@ def python_branch() -> str: ... def python_revision() -> str: ... def python_build() -> tuple[str, str]: ... def python_compiler() -> str: ... -def platform(aliased: bool = ..., terse: bool = ...) -> str: ... +def platform(aliased: bool = False, terse: bool = False) -> str: ... if sys.version_info >= (3, 10): def freedesktop_os_release() -> dict[str, str]: ... @@ -65,3 +107,6 @@ if sys.version_info >= (3, 13): is_emulator: bool = False, ) -> AndroidVer: ... def ios_ver(system: str = "", release: str = "", model: str = "", is_simulator: bool = False) -> IOSVersionInfo: ... + +if sys.version_info >= (3, 14): + def invalidate_caches() -> None: ... diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi index 09637673ce212..dc3247ee47fb8 100644 --- a/mypy/typeshed/stdlib/plistlib.pyi +++ b/mypy/typeshed/stdlib/plistlib.pyi @@ -3,19 +3,17 @@ from _typeshed import ReadableBuffer from collections.abc import Mapping, MutableMapping from datetime import datetime from enum import Enum -from typing import IO, Any +from typing import IO, Any, Final from typing_extensions import Self __all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"] -if sys.version_info < (3, 9): - __all__ += ["readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", "Data"] class PlistFormat(Enum): FMT_XML = 1 FMT_BINARY = 2 -FMT_XML = PlistFormat.FMT_XML -FMT_BINARY = PlistFormat.FMT_BINARY +FMT_XML: Final = PlistFormat.FMT_XML +FMT_BINARY: Final = PlistFormat.FMT_BINARY if sys.version_info >= (3, 13): def load( fp: IO[bytes], @@ -32,28 +30,12 @@ if sys.version_info >= (3, 13): aware_datetime: bool = False, ) -> Any: ... -elif sys.version_info >= (3, 9): +else: def load(fp: IO[bytes], *, fmt: PlistFormat | None = None, dict_type: type[MutableMapping[str, Any]] = ...) -> Any: ... def loads( value: ReadableBuffer, *, fmt: PlistFormat | None = None, dict_type: type[MutableMapping[str, Any]] = ... ) -> Any: ... -else: - def load( - fp: IO[bytes], - *, - fmt: PlistFormat | None = None, - use_builtin_types: bool = True, - dict_type: type[MutableMapping[str, Any]] = ..., - ) -> Any: ... - def loads( - value: ReadableBuffer, - *, - fmt: PlistFormat | None = None, - use_builtin_types: bool = True, - dict_type: type[MutableMapping[str, Any]] = ..., - ) -> Any: ... - if sys.version_info >= (3, 13): def dump( value: Mapping[str, Any] | list[Any] | tuple[Any, ...] | str | bool | float | bytes | bytearray | datetime, @@ -90,17 +72,6 @@ else: sort_keys: bool = True, ) -> bytes: ... -if sys.version_info < (3, 9): - def readPlist(pathOrFile: str | IO[bytes]) -> Any: ... - def writePlist(value: Mapping[str, Any], pathOrFile: str | IO[bytes]) -> None: ... - def readPlistFromBytes(data: ReadableBuffer) -> Any: ... - def writePlistToBytes(value: Mapping[str, Any]) -> bytes: ... - -if sys.version_info < (3, 9): - class Data: - data: bytes - def __init__(self, data: bytes) -> None: ... - class UID: data: int def __init__(self, data: int) -> None: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index a1e41be86a7f8..9ff2b764aeb68 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -17,7 +17,7 @@ POP3_SSL_PORT: Final = 995 CR: Final = b"\r" LF: Final = b"\n" CRLF: Final = b"\r\n" -HAVE_SSL: bool +HAVE_SSL: Final[bool] class POP3: encoding: str diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index 1a4f22af82cf4..6d0d76ab82176 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -6,6 +6,8 @@ if sys.platform != "win32": CLD_CONTINUED as CLD_CONTINUED, CLD_DUMPED as CLD_DUMPED, CLD_EXITED as CLD_EXITED, + CLD_KILLED as CLD_KILLED, + CLD_STOPPED as CLD_STOPPED, CLD_TRAPPED as CLD_TRAPPED, EX_CANTCREAT as EX_CANTCREAT, EX_CONFIG as EX_CONFIG, @@ -29,22 +31,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 +64,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, @@ -226,12 +222,13 @@ if sys.platform != "win32": wait3 as wait3, wait4 as wait4, waitpid as waitpid, + waitstatus_to_exitcode as waitstatus_to_exitcode, write as write, writev as writev, ) - 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 @@ -253,11 +250,20 @@ if sys.platform != "win32": timerfd_settime_ns as timerfd_settime_ns, ) + if sys.version_info >= (3, 14): + from os import readinto as readinto + + if sys.version_info >= (3, 14) and sys.platform == "linux": + from os import SCHED_DEADLINE as SCHED_DEADLINE, SCHED_NORMAL as SCHED_NORMAL + 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 ( @@ -271,6 +277,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, @@ -315,7 +330,17 @@ 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, + P_PIDFD as P_PIDFD, 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, @@ -324,13 +349,11 @@ if sys.platform != "win32": getxattr as getxattr, listxattr as listxattr, memfd_create as memfd_create, + pidfd_open as pidfd_open, removexattr as removexattr, setxattr as setxattr, ) - if sys.version_info >= (3, 9): - from os import P_PIDFD as P_PIDFD, pidfd_open as pidfd_open - if sys.version_info >= (3, 10): from os import ( EFD_CLOEXEC as EFD_CLOEXEC, @@ -361,6 +384,7 @@ if sys.platform != "win32": CLONE_SYSVSEM as CLONE_SYSVSEM, CLONE_THREAD as CLONE_THREAD, CLONE_VM as CLONE_VM, + PIDFD_NONBLOCK as PIDFD_NONBLOCK, setns as setns, unshare as unshare, ) @@ -373,6 +397,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 31406f8df9501..84e1b1e028bde 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -2,6 +2,8 @@ import sys from _typeshed import AnyOrLiteralStr, BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath from collections.abc import Iterable from genericpath import ( + ALLOW_MISSING as ALLOW_MISSING, + _AllowMissingType, commonprefix as commonprefix, exists as exists, getatime as getatime, @@ -61,6 +63,7 @@ __all__ = [ "relpath", "commonpath", ] +__all__ += ["ALLOW_MISSING"] if sys.version_info >= (3, 12): __all__ += ["isjunction", "splitroot"] if sys.version_info >= (3, 13): @@ -77,7 +80,11 @@ pathsep: LiteralString defpath: LiteralString devnull: LiteralString -def abspath(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... +# Overloads are necessary to work around python/mypy#17952 & python/mypy#11880 +@overload +def abspath(path: PathLike[AnyStr]) -> AnyStr: ... +@overload +def abspath(path: AnyStr) -> AnyStr: ... @overload def basename(p: PathLike[AnyStr]) -> AnyStr: ... @overload @@ -86,8 +93,14 @@ def basename(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... def dirname(p: PathLike[AnyStr]) -> AnyStr: ... @overload def dirname(p: AnyOrLiteralStr) -> AnyOrLiteralStr: ... -def expanduser(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... -def expandvars(path: PathLike[AnyStr] | AnyStr) -> AnyStr: ... +@overload +def expanduser(path: PathLike[AnyStr]) -> AnyStr: ... +@overload +def expanduser(path: AnyStr) -> AnyStr: ... +@overload +def expandvars(path: PathLike[AnyStr]) -> AnyStr: ... +@overload +def expandvars(path: AnyStr) -> AnyStr: ... @overload def normcase(s: PathLike[AnyStr]) -> AnyStr: ... @overload @@ -112,19 +125,10 @@ def join(a: LiteralString, /, *paths: LiteralString) -> LiteralString: ... def join(a: StrPath, /, *paths: StrPath) -> str: ... @overload def join(a: BytesPath, /, *paths: BytesPath) -> bytes: ... - -if sys.version_info >= (3, 10): - @overload - def realpath(filename: PathLike[AnyStr], *, strict: bool = False) -> AnyStr: ... - @overload - def realpath(filename: AnyStr, *, strict: bool = False) -> AnyStr: ... - -else: - @overload - def realpath(filename: PathLike[AnyStr]) -> AnyStr: ... - @overload - def realpath(filename: AnyStr) -> AnyStr: ... - +@overload +def realpath(filename: PathLike[AnyStr], *, strict: bool | _AllowMissingType = False) -> AnyStr: ... +@overload +def realpath(filename: AnyStr, *, strict: bool | _AllowMissingType = False) -> AnyStr: ... @overload def relpath(path: LiteralString, start: LiteralString | None = None) -> LiteralString: ... @overload diff --git a/mypy/typeshed/stdlib/pprint.pyi b/mypy/typeshed/stdlib/pprint.pyi index 171878f4165dd..1e80462e25657 100644 --- a/mypy/typeshed/stdlib/pprint.pyi +++ b/mypy/typeshed/stdlib/pprint.pyi @@ -1,4 +1,6 @@ import sys +from _typeshed import SupportsWrite +from collections import deque from typing import IO __all__ = ["pprint", "pformat", "isreadable", "isrecursive", "saferepr", "PrettyPrinter", "pp"] @@ -29,25 +31,25 @@ else: if sys.version_info >= (3, 10): def pp( object: object, - stream: IO[str] | None = ..., - indent: int = ..., - width: int = ..., - depth: int | None = ..., + stream: IO[str] | None = None, + indent: int = 1, + width: int = 80, + depth: int | None = None, *, - compact: bool = ..., + compact: bool = False, sort_dicts: bool = False, - underscore_numbers: bool = ..., + underscore_numbers: bool = False, ) -> None: ... else: def pp( object: object, - stream: IO[str] | None = ..., - indent: int = ..., - width: int = ..., - depth: int | None = ..., + stream: IO[str] | None = None, + indent: int = 1, + width: int = 80, + depth: int | None = None, *, - compact: bool = ..., + compact: bool = False, sort_dicts: bool = False, ) -> None: ... @@ -110,3 +112,48 @@ class PrettyPrinter: def isreadable(self, object: object) -> bool: ... def isrecursive(self, object: object) -> bool: ... def format(self, object: object, context: dict[int, int], maxlevels: int, level: int) -> tuple[str, bool, bool]: ... + def _format( + self, object: object, stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int + ) -> None: ... + def _pprint_dict( + self, + object: dict[object, object], + stream: SupportsWrite[str], + indent: int, + allowance: int, + context: dict[int, int], + level: int, + ) -> None: ... + def _pprint_list( + self, object: list[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int + ) -> None: ... + def _pprint_tuple( + self, + object: tuple[object, ...], + stream: SupportsWrite[str], + indent: int, + allowance: int, + context: dict[int, int], + level: int, + ) -> None: ... + def _pprint_set( + self, object: set[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int + ) -> None: ... + def _pprint_deque( + self, object: deque[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int + ) -> None: ... + def _format_dict_items( + self, + items: list[tuple[object, object]], + stream: SupportsWrite[str], + indent: int, + allowance: int, + context: dict[int, int], + level: int, + ) -> None: ... + def _format_items( + self, items: list[object], stream: SupportsWrite[str], indent: int, allowance: int, context: dict[int, int], level: int + ) -> None: ... + def _repr(self, object: object, context: dict[int, int], level: int) -> str: ... + if sys.version_info >= (3, 10): + def _safe_repr(self, object: object, context: dict[int, int], maxlevels: int, level: int) -> tuple[str, bool, bool]: ... diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index 83256b433035a..c4dee1f6b8f69 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -1,46 +1,59 @@ import sys -from _typeshed import StrEnum, StrOrBytesPath +from _typeshed import StrOrBytesPath from collections.abc import Iterable from cProfile import Profile as _cProfile +from dataclasses import dataclass from profile import Profile from typing import IO, Any, Literal, overload from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 9): - __all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] +if sys.version_info >= (3, 11): + from enum import StrEnum else: - __all__ = ["Stats", "SortKey"] + from enum import Enum + +__all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] _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" -if sys.version_info >= (3, 9): - from dataclasses import dataclass +else: + class SortKey(str, Enum): + CALLS = "calls" + CUMULATIVE = "cumulative" + FILENAME = "filename" + LINE = "line" + NAME = "name" + NFL = "nfl" + PCALLS = "pcalls" + STDNAME = "stdname" + TIME = "time" - @dataclass(unsafe_hash=True) - class FunctionProfile: - ncalls: str - tottime: float - percall_tottime: float - cumtime: float - percall_cumtime: float - file_name: str - line_number: int +@dataclass(unsafe_hash=True) +class FunctionProfile: + ncalls: str + tottime: float + percall_tottime: float + cumtime: float + percall_cumtime: float + file_name: str + line_number: int - @dataclass(unsafe_hash=True) - class StatsProfile: - total_tt: float - func_profiles: dict[str, FunctionProfile] +@dataclass(unsafe_hash=True) +class StatsProfile: + total_tt: float + func_profiles: dict[str, FunctionProfile] _SortArgDict: TypeAlias = dict[str, tuple[tuple[tuple[int, int], ...], str]] @@ -67,9 +80,7 @@ class Stats: def strip_dirs(self) -> Self: ... def calc_callees(self) -> None: ... def eval_print_amount(self, sel: _Selector, list: list[str], msg: str) -> tuple[list[str], str]: ... - if sys.version_info >= (3, 9): - def get_stats_profile(self) -> StatsProfile: ... - + def get_stats_profile(self) -> StatsProfile: ... def get_print_list(self, sel_list: Iterable[_Selector]) -> tuple[int, list[str]]: ... def print_stats(self, *amount: _Selector) -> Self: ... def print_callees(self, *amount: _Selector) -> Self: ... diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi index 941915179c4a5..d1c78f9e3dd67 100644 --- a/mypy/typeshed/stdlib/pty.pyi +++ b/mypy/typeshed/stdlib/pty.pyi @@ -15,10 +15,14 @@ if sys.platform != "win32": def openpty() -> tuple[int, int]: ... 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: ... + if sys.version_info >= (3, 12): + @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `openpty()` instead.") + def master_open() -> tuple[int, str]: ... + @deprecated("Deprecated since Python 3.12; removed in Python 3.14. Use `openpty()` instead.") + def slave_open(tty_name: str) -> int: ... + else: + def master_open() -> tuple[int, str]: ... + 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 144f782acad57..935f9420f88c0 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -5,8 +5,8 @@ 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, Protocol, TypeVar -from typing_extensions import TypeGuard +from typing import IO, Any, AnyStr, Final, NoReturn, Protocol, TypeVar, type_check_only +from typing_extensions import TypeGuard, deprecated __all__ = ["help"] @@ -17,6 +17,7 @@ __date__: Final[str] __version__: Final[str] __credits__: Final[str] +@type_check_only class _Pager(Protocol): def __call__(self, text: str, title: str = "") -> None: ... @@ -31,7 +32,14 @@ def stripid(text: str) -> str: ... def allmethods(cl: type) -> MutableMapping[str, MethodType]: ... def visiblename(name: str, all: Container[str] | None = None, obj: object = None) -> bool: ... def classify_class_attrs(object: object) -> list[tuple[str, str, type, str]]: ... -def ispackage(path: str) -> bool: ... + +if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13.") + def ispackage(path: str) -> bool: ... # undocumented + +else: + def ispackage(path: str) -> bool: ... # undocumented + def source_synopsis(file: IO[AnyStr]) -> AnyStr | None: ... def synopsis(filename: str, cache: MutableMapping[str, tuple[int, str]] = {}) -> str | None: ... diff --git a/mypy/typeshed/stdlib/pydoc_data/topics.pyi b/mypy/typeshed/stdlib/pydoc_data/topics.pyi index 091d34300106e..ce907a41c0053 100644 --- a/mypy/typeshed/stdlib/pydoc_data/topics.pyi +++ b/mypy/typeshed/stdlib/pydoc_data/topics.pyi @@ -1 +1,3 @@ -topics: dict[str, str] +from typing import Final + +topics: Final[dict[str, str]] diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index dc0156ef13bd9..21e676052098d 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -2,18 +2,14 @@ from _typeshed import ReadableBuffer, SupportsRead from collections.abc import Callable from pyexpat import errors as errors, model as model from typing import Any, Final, final -from typing_extensions import TypeAlias +from typing_extensions import CapsuleType, TypeAlias +from xml.parsers.expat import ExpatError as ExpatError 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: Final = 0 XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: Final = 1 @@ -82,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 cae4da089161f..493ae03456044 100644 --- a/mypy/typeshed/stdlib/pyexpat/errors.pyi +++ b/mypy/typeshed/stdlib/pyexpat/errors.pyi @@ -49,3 +49,5 @@ if sys.version_info >= (3, 11): XML_ERROR_INVALID_ARGUMENT: Final[LiteralString] XML_ERROR_NO_BUFFER: Final[LiteralString] XML_ERROR_AMPLIFICATION_LIMIT_BREACH: Final[LiteralString] +if sys.version_info >= (3, 14): + XML_ERROR_NOT_STARTED: Final[LiteralString] diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi index 16643c99d08df..65e2ac1559adf 100644 --- a/mypy/typeshed/stdlib/queue.pyi +++ b/mypy/typeshed/stdlib/queue.pyi @@ -1,17 +1,16 @@ import sys +from _queue import Empty as Empty, SimpleQueue as SimpleQueue +from _typeshed import SupportsRichComparisonT from threading import Condition, Lock +from types import GenericAlias from typing import Any, Generic, TypeVar -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ["Empty", "Full", "Queue", "PriorityQueue", "LifoQueue", "SimpleQueue"] if sys.version_info >= (3, 13): __all__ += ["ShutDown"] _T = TypeVar("_T") -class Empty(Exception): ... class Full(Exception): ... if sys.version_info >= (3, 13): @@ -47,22 +46,10 @@ class Queue(Generic[_T]): def qsize(self) -> int: ... def _qsize(self) -> int: ... def task_done(self) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class PriorityQueue(Queue[_T]): - queue: list[_T] +class PriorityQueue(Queue[SupportsRichComparisonT]): + queue: list[SupportsRichComparisonT] 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/quopri.pyi b/mypy/typeshed/stdlib/quopri.pyi index b652e139bd0e2..be6892fcbcd78 100644 --- a/mypy/typeshed/stdlib/quopri.pyi +++ b/mypy/typeshed/stdlib/quopri.pyi @@ -1,8 +1,9 @@ from _typeshed import ReadableBuffer, SupportsNoArgReadline, SupportsRead, SupportsWrite -from typing import Protocol +from typing import Protocol, type_check_only __all__ = ["encode", "decode", "encodestring", "decodestring"] +@type_check_only class _Input(SupportsRead[bytes], SupportsNoArgReadline[bytes], Protocol): ... def encode(input: _Input, output: SupportsWrite[bytes], quotetabs: int, header: bool = False) -> None: ... diff --git a/mypy/typeshed/stdlib/random.pyi b/mypy/typeshed/stdlib/random.pyi index e7320369c3778..a797794b8050f 100644 --- a/mypy/typeshed/stdlib/random.pyi +++ b/mypy/typeshed/stdlib/random.pyi @@ -4,6 +4,7 @@ from _typeshed import SupportsLenAndGetItem from collections.abc import Callable, Iterable, MutableSequence, Sequence, Set as AbstractSet from fractions import Fraction from typing import Any, ClassVar, NoReturn, TypeVar +from typing_extensions import Self __all__ = [ "Random", @@ -30,10 +31,9 @@ __all__ = [ "getrandbits", "choices", "SystemRandom", + "randbytes", ] -if sys.version_info >= (3, 9): - __all__ += ["randbytes"] if sys.version_info >= (3, 12): __all__ += ["binomialvariate"] @@ -41,25 +41,20 @@ _T = TypeVar("_T") class Random(_random.Random): VERSION: ClassVar[int] - if sys.version_info >= (3, 9): - def __init__(self, x: int | float | str | bytes | bytearray | None = None) -> None: ... # noqa: Y041 - else: - def __init__(self, x: Any = None) -> None: ... + def __init__(self, x: int | float | str | bytes | bytearray | None = None) -> None: ... # noqa: Y041 # Using other `seed` types is deprecated since 3.9 and removed in 3.11 # Ignore Y041, since random.seed doesn't treat int like a float subtype. Having an explicit # int better documents conventional usage of random.seed. - if sys.version_info >= (3, 9): - def seed(self, a: int | float | str | bytes | bytearray | None = None, version: int = 2) -> None: ... # type: ignore[override] # noqa: Y041 - else: - def seed(self, a: Any = None, version: int = 2) -> None: ... + if sys.version_info < (3, 10): + # this is a workaround for pyright correctly flagging an inconsistent inherited constructor, see #14624 + def __new__(cls, x: int | float | str | bytes | bytearray | None = None) -> Self: ... # noqa: Y041 + def seed(self, a: int | float | str | bytes | bytearray | None = None, version: int = 2) -> None: ... # type: ignore[override] # noqa: Y041 def getstate(self) -> tuple[Any, ...]: ... def setstate(self, state: tuple[Any, ...]) -> None: ... def randrange(self, start: int, stop: int | None = None, step: int = 1) -> int: ... def randint(self, a: int, b: int) -> int: ... - if sys.version_info >= (3, 9): - def randbytes(self, n: int) -> bytes: ... - + def randbytes(self, n: int) -> bytes: ... def choice(self, seq: SupportsLenAndGetItem[_T]) -> _T: ... def choices( self, @@ -75,12 +70,10 @@ class Random(_random.Random): def shuffle(self, x: MutableSequence[Any], random: Callable[[], float] | None = None) -> None: ... if sys.version_info >= (3, 11): def sample(self, population: Sequence[_T], k: int, *, counts: Iterable[int] | None = None) -> list[_T]: ... - elif sys.version_info >= (3, 9): + else: def sample( self, population: Sequence[_T] | AbstractSet[_T], k: int, *, counts: Iterable[int] | None = None ) -> list[_T]: ... - else: - def sample(self, population: Sequence[_T] | AbstractSet[_T], k: int) -> list[_T]: ... def uniform(self, a: float, b: float) -> float: ... def triangular(self, low: float = 0.0, high: float = 1.0, mode: float | None = None) -> float: ... @@ -137,5 +130,4 @@ weibullvariate = _inst.weibullvariate getstate = _inst.getstate setstate = _inst.setstate getrandbits = _inst.getrandbits -if sys.version_info >= (3, 9): - randbytes = _inst.randbytes +randbytes = _inst.randbytes diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 76f98dd9f2a27..fb2a06d5e4c81 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -2,14 +2,11 @@ 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 - -if sys.version_info >= (3, 9): - from types import GenericAlias +from types import GenericAlias +from typing import Any, AnyStr, Final, Generic, Literal, TypeVar, final, overload +from typing_extensions import TypeAlias, deprecated __all__ = [ "match", @@ -54,6 +51,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 @@ -81,19 +88,19 @@ class Match(Generic[AnyStr]): @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,11 +112,10 @@ 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): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @final class Pattern(Generic[AnyStr]): @@ -142,11 +148,11 @@ class Pattern(Generic[AnyStr]): @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]: ... @@ -188,8 +194,7 @@ class Pattern(Generic[AnyStr]): def __deepcopy__(self, memo: Any, /) -> Pattern[AnyStr]: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # ----- re variables and constants ----- @@ -215,26 +220,26 @@ class RegexFlag(enum.IntFlag): if sys.version_info >= (3, 11): NOFLAG = 0 -A = RegexFlag.A -ASCII = RegexFlag.ASCII -DEBUG = RegexFlag.DEBUG -I = RegexFlag.I -IGNORECASE = RegexFlag.IGNORECASE -L = RegexFlag.L -LOCALE = RegexFlag.LOCALE -M = RegexFlag.M -MULTILINE = RegexFlag.MULTILINE -S = RegexFlag.S -DOTALL = RegexFlag.DOTALL -X = RegexFlag.X -VERBOSE = RegexFlag.VERBOSE -U = RegexFlag.U -UNICODE = RegexFlag.UNICODE +A: Final = RegexFlag.A +ASCII: Final = RegexFlag.ASCII +DEBUG: Final = RegexFlag.DEBUG +I: Final = RegexFlag.I +IGNORECASE: Final = RegexFlag.IGNORECASE +L: Final = RegexFlag.L +LOCALE: Final = RegexFlag.LOCALE +M: Final = RegexFlag.M +MULTILINE: Final = RegexFlag.MULTILINE +S: Final = RegexFlag.S +DOTALL: Final = RegexFlag.DOTALL +X: Final = RegexFlag.X +VERBOSE: Final = RegexFlag.VERBOSE +U: Final = RegexFlag.U +UNICODE: Final = RegexFlag.UNICODE if sys.version_info < (3, 13): - T = RegexFlag.T - TEMPLATE = RegexFlag.TEMPLATE + T: Final = RegexFlag.T + TEMPLATE: Final = RegexFlag.TEMPLATE if sys.version_info >= (3, 11): - NOFLAG = RegexFlag.NOFLAG + NOFLAG: Final = RegexFlag.NOFLAG _FlagsType: TypeAlias = int | RegexFlag # Type-wise the compile() overloads are unnecessary, they could also be modeled using @@ -261,11 +266,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 @@ -302,4 +307,8 @@ def escape(pattern: AnyStr) -> AnyStr: ... def purge() -> None: ... if sys.version_info < (3, 13): - def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ... + if sys.version_info >= (3, 11): + @deprecated("Deprecated since Python 3.11; removed in Python 3.13. Use `re.compile()` instead.") + def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ... # undocumented + else: + def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = 0) -> Pattern[AnyStr]: ... # undocumented diff --git a/mypy/typeshed/stdlib/resource.pyi b/mypy/typeshed/stdlib/resource.pyi index 5e468c2cead5f..f99cd5b088056 100644 --- a/mypy/typeshed/stdlib/resource.pyi +++ b/mypy/typeshed/stdlib/resource.pyi @@ -3,27 +3,28 @@ from _typeshed import structseq from typing import Final, final if sys.platform != "win32": - RLIMIT_AS: int - RLIMIT_CORE: int - RLIMIT_CPU: int - RLIMIT_DATA: int - RLIMIT_FSIZE: int - RLIMIT_MEMLOCK: int - RLIMIT_NOFILE: int - RLIMIT_NPROC: int - RLIMIT_RSS: int - RLIMIT_STACK: int - RLIM_INFINITY: int - RUSAGE_CHILDREN: int - RUSAGE_SELF: int + # Depends on resource.h + RLIMIT_AS: Final[int] + RLIMIT_CORE: Final[int] + RLIMIT_CPU: Final[int] + RLIMIT_DATA: Final[int] + RLIMIT_FSIZE: Final[int] + RLIMIT_MEMLOCK: Final[int] + RLIMIT_NOFILE: Final[int] + RLIMIT_NPROC: Final[int] + RLIMIT_RSS: Final[int] + RLIMIT_STACK: Final[int] + RLIM_INFINITY: Final[int] + RUSAGE_CHILDREN: Final[int] + RUSAGE_SELF: Final[int] if sys.platform == "linux": - RLIMIT_MSGQUEUE: int - RLIMIT_NICE: int - RLIMIT_OFILE: int - RLIMIT_RTPRIO: int - RLIMIT_RTTIME: int - RLIMIT_SIGPENDING: int - RUSAGE_THREAD: int + RLIMIT_MSGQUEUE: Final[int] + RLIMIT_NICE: Final[int] + RLIMIT_OFILE: Final[int] + RLIMIT_RTPRIO: Final[int] + RLIMIT_RTTIME: Final[int] + RLIMIT_SIGPENDING: Final[int] + RUSAGE_THREAD: Final[int] @final class struct_rusage( diff --git a/mypy/typeshed/stdlib/sched.pyi b/mypy/typeshed/stdlib/sched.pyi index 75dd63d0414af..52f87ab68ff54 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, ClassVar, NamedTuple, type_check_only from typing_extensions import TypeAlias __all__ = ["scheduler"] @@ -17,13 +17,17 @@ 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): + __hash__: ClassVar[None] # type: ignore[assignment] + class scheduler: timefunc: Callable[[], float] delayfunc: Callable[[float], object] diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index 6d4c8d8f4c157..43a9e4274b236 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -2,34 +2,42 @@ import sys from _typeshed import FileDescriptorLike from collections.abc import Iterable from types import TracebackType -from typing import Any, final -from typing_extensions import Self +from typing import Any, ClassVar, Final, TypeVar, final +from typing_extensions import Never, Self if sys.platform != "win32": - PIPE_BUF: int - POLLERR: int - POLLHUP: int - POLLIN: int - POLLMSG: int - POLLNVAL: int - POLLOUT: int - POLLPRI: int - POLLRDBAND: int + PIPE_BUF: Final[int] + POLLERR: Final[int] + POLLHUP: Final[int] + POLLIN: Final[int] if sys.platform == "linux": - POLLRDHUP: int - POLLRDNORM: int - POLLWRBAND: int - POLLWRNORM: int + POLLMSG: Final[int] + POLLNVAL: Final[int] + POLLOUT: Final[int] + POLLPRI: Final[int] + POLLRDBAND: Final[int] + if sys.platform == "linux": + POLLRDHUP: Final[int] + POLLRDNORM: Final[int] + POLLWRBAND: Final[int] + POLLWRNORM: Final[int] + + # This is actually a function that returns an instance of a class. + # The class is not accessible directly, and also calls itself select.poll. + class poll: + # default value is select.POLLIN | select.POLLPRI | select.POLLOUT + def register(self, fd: FileDescriptorLike, eventmask: int = 7, /) -> None: ... + def modify(self, fd: FileDescriptorLike, eventmask: int, /) -> None: ... + def unregister(self, fd: FileDescriptorLike, /) -> None: ... + def poll(self, timeout: float | None = None, /) -> list[tuple[int, int]]: ... -class poll: - def register(self, fd: FileDescriptorLike, eventmask: int = ...) -> None: ... - def modify(self, fd: FileDescriptorLike, eventmask: int) -> None: ... - def unregister(self, fd: FileDescriptorLike) -> None: ... - def poll(self, timeout: float | None = ...) -> list[tuple[int, int]]: ... +_R = TypeVar("_R", default=Never) +_W = TypeVar("_W", default=Never) +_X = TypeVar("_X", default=Never) def select( - rlist: Iterable[Any], wlist: Iterable[Any], xlist: Iterable[Any], timeout: float | None = None, / -) -> tuple[list[Any], list[Any], list[Any]]: ... + rlist: Iterable[_R], wlist: Iterable[_W], xlist: Iterable[_X], timeout: float | None = None, / +) -> tuple[list[_R], list[_W], list[_X]]: ... error = OSError @@ -52,6 +60,7 @@ if sys.platform != "linux" and sys.platform != "win32": data: Any = ..., udata: Any = ..., ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] # BSD only @final @@ -66,49 +75,50 @@ if sys.platform != "linux" and sys.platform != "win32": @classmethod def fromfd(cls, fd: FileDescriptorLike, /) -> kqueue: ... - KQ_EV_ADD: int - KQ_EV_CLEAR: int - KQ_EV_DELETE: int - KQ_EV_DISABLE: int - KQ_EV_ENABLE: int - KQ_EV_EOF: int - KQ_EV_ERROR: int - KQ_EV_FLAG1: int - KQ_EV_ONESHOT: int - KQ_EV_SYSFLAGS: int - KQ_FILTER_AIO: int - KQ_FILTER_NETDEV: int - KQ_FILTER_PROC: int - KQ_FILTER_READ: int - KQ_FILTER_SIGNAL: int - KQ_FILTER_TIMER: int - KQ_FILTER_VNODE: int - KQ_FILTER_WRITE: int - KQ_NOTE_ATTRIB: int - KQ_NOTE_CHILD: int - KQ_NOTE_DELETE: int - KQ_NOTE_EXEC: int - KQ_NOTE_EXIT: int - KQ_NOTE_EXTEND: int - KQ_NOTE_FORK: int - KQ_NOTE_LINK: int + KQ_EV_ADD: Final[int] + KQ_EV_CLEAR: Final[int] + KQ_EV_DELETE: Final[int] + KQ_EV_DISABLE: Final[int] + KQ_EV_ENABLE: Final[int] + KQ_EV_EOF: Final[int] + KQ_EV_ERROR: Final[int] + KQ_EV_FLAG1: Final[int] + KQ_EV_ONESHOT: Final[int] + KQ_EV_SYSFLAGS: Final[int] + KQ_FILTER_AIO: Final[int] + if sys.platform != "darwin": + KQ_FILTER_NETDEV: Final[int] + KQ_FILTER_PROC: Final[int] + KQ_FILTER_READ: Final[int] + KQ_FILTER_SIGNAL: Final[int] + KQ_FILTER_TIMER: Final[int] + KQ_FILTER_VNODE: Final[int] + KQ_FILTER_WRITE: Final[int] + KQ_NOTE_ATTRIB: Final[int] + KQ_NOTE_CHILD: Final[int] + KQ_NOTE_DELETE: Final[int] + KQ_NOTE_EXEC: Final[int] + KQ_NOTE_EXIT: Final[int] + KQ_NOTE_EXTEND: Final[int] + KQ_NOTE_FORK: Final[int] + KQ_NOTE_LINK: Final[int] if sys.platform != "darwin": - KQ_NOTE_LINKDOWN: int - KQ_NOTE_LINKINV: int - KQ_NOTE_LINKUP: int - KQ_NOTE_LOWAT: int - KQ_NOTE_PCTRLMASK: int - KQ_NOTE_PDATAMASK: int - KQ_NOTE_RENAME: int - KQ_NOTE_REVOKE: int - KQ_NOTE_TRACK: int - KQ_NOTE_TRACKERR: int - KQ_NOTE_WRITE: int + KQ_NOTE_LINKDOWN: Final[int] + KQ_NOTE_LINKINV: Final[int] + KQ_NOTE_LINKUP: Final[int] + KQ_NOTE_LOWAT: Final[int] + KQ_NOTE_PCTRLMASK: Final[int] + KQ_NOTE_PDATAMASK: Final[int] + KQ_NOTE_RENAME: Final[int] + KQ_NOTE_REVOKE: Final[int] + KQ_NOTE_TRACK: Final[int] + KQ_NOTE_TRACKERR: Final[int] + KQ_NOTE_WRITE: Final[int] if sys.platform == "linux": @final class epoll: - def __init__(self, sizehint: int = ..., flags: int = ...) -> None: ... + def __new__(self, sizehint: int = ..., flags: int = ...) -> Self: ... def __enter__(self) -> Self: ... def __exit__( self, @@ -127,21 +137,23 @@ if sys.platform == "linux": @classmethod def fromfd(cls, fd: FileDescriptorLike, /) -> epoll: ... - EPOLLERR: int - EPOLLEXCLUSIVE: int - EPOLLET: int - EPOLLHUP: int - EPOLLIN: int - EPOLLMSG: int - EPOLLONESHOT: int - EPOLLOUT: int - EPOLLPRI: int - EPOLLRDBAND: int - EPOLLRDHUP: int - EPOLLRDNORM: int - EPOLLWRBAND: int - EPOLLWRNORM: int - EPOLL_CLOEXEC: int + EPOLLERR: Final[int] + EPOLLEXCLUSIVE: Final[int] + EPOLLET: Final[int] + EPOLLHUP: Final[int] + EPOLLIN: Final[int] + EPOLLMSG: Final[int] + EPOLLONESHOT: Final[int] + EPOLLOUT: Final[int] + EPOLLPRI: Final[int] + EPOLLRDBAND: Final[int] + EPOLLRDHUP: Final[int] + EPOLLRDNORM: Final[int] + EPOLLWRBAND: Final[int] + EPOLLWRNORM: Final[int] + EPOLL_CLOEXEC: Final[int] + if sys.version_info >= (3, 14): + EPOLLWAKEUP: Final[int] if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32": # Solaris only diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi index a857d0e242ab5..bcca4e341b9a1 100644 --- a/mypy/typeshed/stdlib/selectors.pyi +++ b/mypy/typeshed/stdlib/selectors.pyi @@ -2,13 +2,13 @@ import sys from _typeshed import FileDescriptor, FileDescriptorLike, Unused from abc import ABCMeta, abstractmethod from collections.abc import Mapping -from typing import Any, NamedTuple +from typing import Any, Final, NamedTuple from typing_extensions import Self, TypeAlias _EventMask: TypeAlias = int -EVENT_READ: _EventMask -EVENT_WRITE: _EventMask +EVENT_READ: Final = 1 +EVENT_WRITE: Final = 2 class SelectorKey(NamedTuple): fileobj: FileDescriptorLike @@ -50,10 +50,12 @@ if sys.platform == "linux": class EpollSelector(_PollLikeSelector): def fileno(self) -> int: ... -class DevpollSelector(_PollLikeSelector): - def fileno(self) -> int: ... +if sys.platform != "linux" and sys.platform != "darwin" and sys.platform != "win32": + # Solaris only + class DevpollSelector(_PollLikeSelector): + def fileno(self) -> int: ... -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": class KqueueSelector(_BaseSelectorImpl): def fileno(self) -> int: ... def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, _EventMask]]: ... 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/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index dcff18d110bd8..cc26cfc556a00 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -1,9 +1,9 @@ import os import sys -from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite +from _typeshed import BytesPath, ExcInfo, FileDescriptorOrPath, MaybeNone, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite from collections.abc import Callable, Iterable, Sequence from tarfile import _TarfileFilter -from typing import Any, AnyStr, NamedTuple, Protocol, TypeVar, overload +from typing import Any, AnyStr, NamedTuple, NoReturn, Protocol, TypeVar, overload, type_check_only from typing_extensions import TypeAlias, deprecated __all__ = [ @@ -18,7 +18,6 @@ __all__ = [ "rmtree", "Error", "SpecialFileError", - "ExecError", "make_archive", "get_archive_formats", "register_archive_format", @@ -34,17 +33,23 @@ __all__ = [ "SameFileError", "disk_usage", ] +if sys.version_info < (3, 14): + __all__ += ["ExecError"] _StrOrBytesPathT = TypeVar("_StrOrBytesPathT", bound=StrOrBytesPath) _StrPathT = TypeVar("_StrPathT", bound=StrPath) -# Return value of some functions that may either return a path-like object that was passed in or -# a string -_PathReturn: TypeAlias = Any +_BytesPathT = TypeVar("_BytesPathT", bound=BytesPath) class Error(OSError): ... class SameFileError(Error): ... class SpecialFileError(OSError): ... -class ExecError(OSError): ... + +if sys.version_info >= (3, 14): + ExecError = RuntimeError # Deprecated in Python 3.14; removal scheduled for Python 3.16 + +else: + class ExecError(OSError): ... + class ReadError(OSError): ... class RegistryError(Exception): ... @@ -53,27 +58,28 @@ def copyfile(src: StrOrBytesPath, dst: _StrOrBytesPathT, *, follow_symlinks: boo def copymode(src: StrOrBytesPath, dst: StrOrBytesPath, *, follow_symlinks: bool = True) -> None: ... def copystat(src: StrOrBytesPath, dst: StrOrBytesPath, *, follow_symlinks: bool = True) -> None: ... @overload -def copy(src: StrPath, dst: StrPath, *, follow_symlinks: bool = True) -> _PathReturn: ... +def copy(src: StrPath, dst: _StrPathT, *, follow_symlinks: bool = True) -> _StrPathT | str: ... @overload -def copy(src: BytesPath, dst: BytesPath, *, follow_symlinks: bool = True) -> _PathReturn: ... +def copy(src: BytesPath, dst: _BytesPathT, *, follow_symlinks: bool = True) -> _BytesPathT | bytes: ... @overload -def copy2(src: StrPath, dst: StrPath, *, follow_symlinks: bool = True) -> _PathReturn: ... +def copy2(src: StrPath, dst: _StrPathT, *, follow_symlinks: bool = True) -> _StrPathT | str: ... @overload -def copy2(src: BytesPath, dst: BytesPath, *, follow_symlinks: bool = True) -> _PathReturn: ... +def copy2(src: BytesPath, dst: _BytesPathT, *, follow_symlinks: bool = True) -> _BytesPathT | bytes: ... def ignore_patterns(*patterns: StrPath) -> Callable[[Any, list[str]], set[str]]: ... def copytree( src: StrPath, - dst: StrPath, + dst: _StrPathT, symlinks: bool = False, ignore: None | Callable[[str, list[str]], Iterable[str]] | Callable[[StrPath, list[str]], Iterable[str]] = None, copy_function: Callable[[str, str], object] = ..., ignore_dangling_symlinks: bool = False, dirs_exist_ok: bool = False, -) -> _PathReturn: ... +) -> _StrPathT: ... _OnErrorCallback: TypeAlias = Callable[[Callable[..., Any], str, ExcInfo], object] _OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, BaseException], object] +@type_check_only class _RmtreeType(Protocol): avoids_symlink_attacks: bool if sys.version_info >= (3, 12): @@ -83,7 +89,7 @@ class _RmtreeType(Protocol): self, path: StrOrBytesPath, ignore_errors: bool, - onerror: _OnErrorCallback, + onerror: _OnErrorCallback | None, *, onexc: None = None, dir_fd: int | None = None, @@ -95,7 +101,7 @@ class _RmtreeType(Protocol): path: StrOrBytesPath, ignore_errors: bool = False, *, - onerror: _OnErrorCallback, + onerror: _OnErrorCallback | None, onexc: None = None, dir_fd: int | None = None, ) -> None: ... @@ -130,12 +136,7 @@ _CopyFn: TypeAlias = Callable[[str, str], object] | Callable[[StrPath, StrPath], # N.B. shutil.move appears to take bytes arguments, however, # this does not work when dst is (or is within) an existing directory. # (#6832) -if sys.version_info >= (3, 9): - def move(src: StrPath, dst: StrPath, copy_function: _CopyFn = ...) -> _PathReturn: ... - -else: - # See https://bugs.python.org/issue32689 - def move(src: str, dst: StrPath, copy_function: _CopyFn = ...) -> _PathReturn: ... +def move(src: StrPath, dst: _StrPathT, copy_function: _CopyFn = ...) -> _StrPathT | str | MaybeNone: ... class _ntuple_diskusage(NamedTuple): total: int @@ -185,8 +186,13 @@ else: @overload def chown(path: FileDescriptorOrPath, user: str | int, group: str | int) -> None: ... +if sys.platform == "win32" and sys.version_info < (3, 12): + @overload + @deprecated("On Windows before Python 3.12, using a PathLike as `cmd` would always fail or return `None`.") + def which(cmd: os.PathLike[str], mode: int = 1, path: StrPath | None = None) -> NoReturn: ... + @overload -def which(cmd: _StrPathT, mode: int = 1, path: StrPath | None = None) -> str | _StrPathT | None: ... +def which(cmd: StrPath, mode: int = 1, path: StrPath | None = None) -> str | None: ... @overload def which(cmd: bytes, mode: int = 1, path: StrPath | None = None) -> bytes | None: ... def make_archive( diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 2e3ac5bf24c37..c2668bd8b32d9 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -61,8 +61,8 @@ class Handlers(IntEnum): SIG_DFL = 0 SIG_IGN = 1 -SIG_DFL: Handlers -SIG_IGN: Handlers +SIG_DFL: Final = Handlers.SIG_DFL +SIG_IGN: Final = Handlers.SIG_IGN _SIGNUM: TypeAlias = int | Signals _HANDLER: TypeAlias = Callable[[int, FrameType | None], Any] | int | Handlers | None @@ -77,45 +77,45 @@ else: def getsignal(signalnum: _SIGNUM, /) -> _HANDLER: ... def signal(signalnum: _SIGNUM, handler: _HANDLER, /) -> _HANDLER: ... -SIGABRT: Signals -SIGFPE: Signals -SIGILL: Signals -SIGINT: Signals -SIGSEGV: Signals -SIGTERM: Signals +SIGABRT: Final = Signals.SIGABRT +SIGFPE: Final = Signals.SIGFPE +SIGILL: Final = Signals.SIGILL +SIGINT: Final = Signals.SIGINT +SIGSEGV: Final = Signals.SIGSEGV +SIGTERM: Final = Signals.SIGTERM if sys.platform == "win32": - SIGBREAK: Signals - CTRL_C_EVENT: Signals - CTRL_BREAK_EVENT: Signals + SIGBREAK: Final = Signals.SIGBREAK + CTRL_C_EVENT: Final = Signals.CTRL_C_EVENT + CTRL_BREAK_EVENT: Final = Signals.CTRL_BREAK_EVENT else: if sys.platform != "linux": - SIGINFO: Signals - SIGEMT: Signals - SIGALRM: Signals - SIGBUS: Signals - SIGCHLD: Signals - SIGCONT: Signals - SIGHUP: Signals - SIGIO: Signals - SIGIOT: Signals - SIGKILL: Signals - SIGPIPE: Signals - SIGPROF: Signals - SIGQUIT: Signals - SIGSTOP: Signals - SIGSYS: Signals - SIGTRAP: Signals - SIGTSTP: Signals - SIGTTIN: Signals - SIGTTOU: Signals - SIGURG: Signals - SIGUSR1: Signals - SIGUSR2: Signals - SIGVTALRM: Signals - SIGWINCH: Signals - SIGXCPU: Signals - SIGXFSZ: Signals + SIGINFO: Final = Signals.SIGINFO + SIGEMT: Final = Signals.SIGEMT + SIGALRM: Final = Signals.SIGALRM + SIGBUS: Final = Signals.SIGBUS + SIGCHLD: Final = Signals.SIGCHLD + SIGCONT: Final = Signals.SIGCONT + SIGHUP: Final = Signals.SIGHUP + SIGIO: Final = Signals.SIGIO + SIGIOT: Final = Signals.SIGABRT # alias + SIGKILL: Final = Signals.SIGKILL + SIGPIPE: Final = Signals.SIGPIPE + SIGPROF: Final = Signals.SIGPROF + SIGQUIT: Final = Signals.SIGQUIT + SIGSTOP: Final = Signals.SIGSTOP + SIGSYS: Final = Signals.SIGSYS + SIGTRAP: Final = Signals.SIGTRAP + SIGTSTP: Final = Signals.SIGTSTP + SIGTTIN: Final = Signals.SIGTTIN + SIGTTOU: Final = Signals.SIGTTOU + SIGURG: Final = Signals.SIGURG + SIGUSR1: Final = Signals.SIGUSR1 + SIGUSR2: Final = Signals.SIGUSR2 + SIGVTALRM: Final = Signals.SIGVTALRM + SIGWINCH: Final = Signals.SIGWINCH + SIGXCPU: Final = Signals.SIGXCPU + SIGXFSZ: Final = Signals.SIGXFSZ class ItimerError(OSError): ... ITIMER_PROF: int @@ -127,9 +127,9 @@ else: SIG_UNBLOCK = 1 SIG_SETMASK = 2 - SIG_BLOCK = Sigmasks.SIG_BLOCK - SIG_UNBLOCK = Sigmasks.SIG_UNBLOCK - SIG_SETMASK = Sigmasks.SIG_SETMASK + SIG_BLOCK: Final = Sigmasks.SIG_BLOCK + SIG_UNBLOCK: Final = Sigmasks.SIG_UNBLOCK + SIG_SETMASK: Final = Sigmasks.SIG_SETMASK def alarm(seconds: int, /) -> int: ... def getitimer(which: int, /) -> tuple[float, float]: ... def pause() -> None: ... @@ -147,13 +147,13 @@ else: else: def sigwait(sigset: Iterable[int], /) -> _SIGNUM: ... if sys.platform != "darwin": - SIGCLD: Signals - SIGPOLL: Signals - SIGPWR: Signals - SIGRTMAX: Signals - SIGRTMIN: Signals + SIGCLD: Final = Signals.SIGCHLD # alias + SIGPOLL: Final = Signals.SIGIO # alias + SIGPWR: Final = Signals.SIGPWR + SIGRTMAX: Final = Signals.SIGRTMAX + SIGRTMIN: Final = Signals.SIGRTMIN if sys.version_info >= (3, 11): - SIGSTKFLT: Signals + SIGSTKFLT: Final = Signals.SIGSTKFLT @final class struct_siginfo(structseq[int], tuple[int, int, int, int, int, int, int]): @@ -181,8 +181,7 @@ else: def strsignal(signalnum: _SIGNUM, /) -> str | None: ... def valid_signals() -> set[Signals]: ... def raise_signal(signalnum: _SIGNUM, /) -> None: ... -def set_wakeup_fd(fd: int, /, *, warn_on_full_buffer: bool = ...) -> int: ... +def set_wakeup_fd(fd: int, /, *, warn_on_full_buffer: bool = True) -> int: ... -if sys.version_info >= (3, 9): - if sys.platform == "linux": - def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = ..., /) -> None: ... +if sys.platform == "linux": + def pidfd_send_signal(pidfd: int, sig: int, siginfo: None = None, flags: int = 0, /) -> None: ... diff --git a/mypy/typeshed/stdlib/smtpd.pyi b/mypy/typeshed/stdlib/smtpd.pyi index 7392bd51627d9..dee7e949f42fa 100644 --- a/mypy/typeshed/stdlib/smtpd.pyi +++ b/mypy/typeshed/stdlib/smtpd.pyi @@ -4,7 +4,7 @@ import socket import sys from collections import defaultdict from typing import Any -from typing_extensions import TypeAlias +from typing_extensions import TypeAlias, deprecated if sys.version_info >= (3, 11): __all__ = ["SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy"] @@ -87,5 +87,6 @@ class PureProxy(SMTPServer): def process_message(self, peer: _Address, mailfrom: str, rcpttos: list[str], data: bytes | str) -> str | None: ... # type: ignore[override] if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.9; removed in Python 3.11.") class MailmanProxy(PureProxy): def process_message(self, peer: _Address, mailfrom: str, rcpttos: list[str], data: bytes | str) -> str | None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi index a762427bcab33..6a8467689367a 100644 --- a/mypy/typeshed/stdlib/smtplib.pyi +++ b/mypy/typeshed/stdlib/smtplib.pyi @@ -7,7 +7,7 @@ from re import Pattern from socket import socket from ssl import SSLContext from types import TracebackType -from typing import Any, Protocol, overload +from typing import Any, Final, Protocol, overload, type_check_only from typing_extensions import Self, TypeAlias __all__ = [ @@ -30,12 +30,12 @@ __all__ = [ _Reply: TypeAlias = tuple[int, bytes] _SendErrs: TypeAlias = dict[str, _Reply] -SMTP_PORT: int -SMTP_SSL_PORT: int -CRLF: str -bCRLF: bytes +SMTP_PORT: Final = 25 +SMTP_SSL_PORT: Final = 465 +CRLF: Final[str] +bCRLF: Final[bytes] -OLDSTYLE_AUTH: Pattern[str] +OLDSTYLE_AUTH: Final[Pattern[str]] class SMTPException(OSError): ... class SMTPNotSupportedError(SMTPException): ... @@ -65,7 +65,7 @@ class SMTPAuthenticationError(SMTPResponseException): ... def quoteaddr(addrstring: str) -> str: ... def quotedata(data: str) -> str: ... - +@type_check_only class _AuthObject(Protocol): @overload def __call__(self, challenge: None = None, /) -> str | None: ... @@ -182,23 +182,14 @@ class SMTP_SSL(SMTP): context: SSLContext | None = None, ) -> None: ... -LMTP_PORT: int +LMTP_PORT: Final = 2003 class LMTP(SMTP): - if sys.version_info >= (3, 9): - def __init__( - self, - host: str = "", - port: int = 2003, - local_hostname: str | None = None, - source_address: _SourceAddress | None = None, - timeout: float = ..., - ) -> None: ... - else: - def __init__( - self, - host: str = "", - port: int = 2003, - local_hostname: str | None = None, - source_address: _SourceAddress | None = None, - ) -> None: ... + def __init__( + self, + host: str = "", + port: int = 2003, + local_hostname: str | None = None, + source_address: _SourceAddress | None = None, + timeout: float = ..., + ) -> None: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index b626409d2dde9..b10b3560b91fa 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, @@ -58,12 +53,18 @@ from _socket import ( IPPROTO_TCP as IPPROTO_TCP, IPPROTO_UDP as IPPROTO_UDP, IPV6_CHECKSUM as IPV6_CHECKSUM, + IPV6_DONTFRAG as IPV6_DONTFRAG, + IPV6_HOPLIMIT as IPV6_HOPLIMIT, + IPV6_HOPOPTS as IPV6_HOPOPTS, IPV6_JOIN_GROUP as IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP as IPV6_LEAVE_GROUP, IPV6_MULTICAST_HOPS as IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF as IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP as IPV6_MULTICAST_LOOP, + IPV6_PKTINFO as IPV6_PKTINFO, + IPV6_RECVRTHDR as IPV6_RECVRTHDR, IPV6_RECVTCLASS as IPV6_RECVTCLASS, + IPV6_RTHDR as IPV6_RTHDR, IPV6_TCLASS as IPV6_TCLASS, IPV6_UNICAST_HOPS as IPV6_UNICAST_HOPS, IPV6_V6ONLY as IPV6_V6ONLY, @@ -93,7 +94,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 +109,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 +119,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,17 +131,190 @@ 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 from enum import IntEnum, IntFlag from io import BufferedReader, BufferedRWPair, BufferedWriter, IOBase, RawIOBase, TextIOWrapper -from typing import Any, Literal, Protocol, SupportsIndex, overload +from typing import Any, Final, Literal, Protocol, SupportsIndex, overload, type_check_only 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_DONTFRAG", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_PKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_RTHDR", + "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,46 +325,48 @@ if sys.platform == "win32": SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE, ) -if sys.platform != "darwin" or sys.version_info >= (3, 9): - from _socket import ( - IPV6_DONTFRAG as IPV6_DONTFRAG, - IPV6_HOPLIMIT as IPV6_HOPLIMIT, - IPV6_HOPOPTS as IPV6_HOPOPTS, - IPV6_PKTINFO as IPV6_PKTINFO, - IPV6_RECVRTHDR as IPV6_RECVRTHDR, - IPV6_RTHDR as IPV6_RTHDR, - ) + __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": 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, SO_DOMAIN as SO_DOMAIN, SO_MARK as SO_MARK, SO_PASSCRED as SO_PASSCRED, @@ -203,7 +375,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 +392,58 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_WINDOW_CLAMP as TCP_WINDOW_CLAMP, ) + __all__ += [ + "IP_TRANSPARENT", + "SCM_CREDENTIALS", + "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,47 +451,89 @@ 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, ) - if sys.platform != "darwin" or sys.version_info >= (3, 9): - from _socket import ( - IPV6_DSTOPTS as IPV6_DSTOPTS, - IPV6_NEXTHOP as IPV6_NEXTHOP, - IPV6_PATHMTU as IPV6_PATHMTU, - IPV6_RECVDSTOPTS as IPV6_RECVDSTOPTS, - IPV6_RECVHOPLIMIT as IPV6_RECVHOPLIMIT, - IPV6_RECVHOPOPTS as IPV6_RECVHOPOPTS, - IPV6_RECVPATHMTU as IPV6_RECVPATHMTU, - IPV6_RECVPKTINFO as IPV6_RECVPKTINFO, - IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS, - IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU, - ) + __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": - 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 + from _socket import ( + IPV6_DSTOPTS as IPV6_DSTOPTS, + IPV6_NEXTHOP as IPV6_NEXTHOP, + IPV6_PATHMTU as IPV6_PATHMTU, + IPV6_RECVDSTOPTS as IPV6_RECVDSTOPTS, + IPV6_RECVHOPLIMIT as IPV6_RECVHOPLIMIT, + IPV6_RECVHOPOPTS as IPV6_RECVHOPOPTS, + IPV6_RECVPATHMTU as IPV6_RECVPATHMTU, + IPV6_RECVPKTINFO as IPV6_RECVPKTINFO, + IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS, + ) + + __all__ += [ + "IPV6_DSTOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPATHMTU", + "IPV6_RECVPKTINFO", + "IPV6_RTHDRDSTOPTS", + ] + + if sys.platform != "darwin" or sys.version_info >= (3, 13): + from _socket import SO_BINDTODEVICE as SO_BINDTODEVICE + + __all__ += ["SO_BINDTODEVICE"] + +if sys.platform != "darwin" and sys.platform != "linux": + 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 +577,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 +584,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 +607,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 +656,134 @@ if sys.platform == "linux": VMADDR_PORT_ANY as VMADDR_PORT_ANY, ) -if sys.platform == "linux" and sys.version_info >= (3, 9): + __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.version_info >= (3, 13): + from _socket import CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER + + __all__ += ["CAN_RAW_ERR_FILTER"] + +if sys.platform == "linux": 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 +810,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 +910,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 +926,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,23 +948,137 @@ 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": + __all__ += ["send_fds", "recv_fds"] + +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, + IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU, + 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", + "IPV6_USE_MIN_MTU", + "LOCAL_PEERCRED", + "SCM_CREDS", + "AI_DEFAULT", + "AI_MASK", + "AI_V4MAPPED_CFG", + "MSG_EOF", + ] + +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"] + +if sys.version_info >= (3, 14): + from _socket import IP_RECVTTL as IP_RECVTTL + + __all__ += ["IP_RECVTTL"] + + if sys.platform == "win32" or sys.platform == "linux": + from _socket import IP_RECVERR as IP_RECVERR, IPV6_RECVERR as IPV6_RECVERR, SO_ORIGINAL_DST as SO_ORIGINAL_DST + + __all__ += ["IP_RECVERR", "IPV6_RECVERR", "SO_ORIGINAL_DST"] + + if sys.platform == "win32": + from _socket import ( + SO_BTH_ENCRYPT as SO_BTH_ENCRYPT, + SO_BTH_MTU as SO_BTH_MTU, + SO_BTH_MTU_MAX as SO_BTH_MTU_MAX, + SO_BTH_MTU_MIN as SO_BTH_MTU_MIN, + SOL_RFCOMM as SOL_RFCOMM, + TCP_QUICKACK as TCP_QUICKACK, + ) + + __all__ += ["SOL_RFCOMM", "SO_BTH_ENCRYPT", "SO_BTH_MTU", "SO_BTH_MTU_MAX", "SO_BTH_MTU_MIN", "TCP_QUICKACK"] + + if sys.platform == "linux": + from _socket import ( + IP_FREEBIND as IP_FREEBIND, + IP_RECVORIGDSTADDR as IP_RECVORIGDSTADDR, + VMADDR_CID_LOCAL as VMADDR_CID_LOCAL, + ) + + __all__ += ["IP_FREEBIND", "IP_RECVORIGDSTADDR", "VMADDR_CID_LOCAL"] + # Re-exported from errno -EBADF: int -EAGAIN: int -EWOULDBLOCK: int +EBADF: Final[int] +EAGAIN: Final[int] +EWOULDBLOCK: Final[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 AF_APPLETALK = 5 - AF_DECnet = ... AF_IPX = 4 AF_SNA = 22 AF_UNSPEC = 0 @@ -498,10 +1086,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 @@ -526,70 +1114,70 @@ class AddressFamily(IntEnum): AF_NETLINK = 16 AF_VSOCK = 40 AF_QIPCRTR = 42 - if sys.platform != "win32" or sys.version_info >= (3, 9): + if sys.platform != "linux": AF_LINK = 33 - if sys.platform != "darwin": - AF_BLUETOOTH = 32 + if sys.platform != "darwin" and sys.platform != "linux": + AF_BLUETOOTH = 32 if sys.platform == "win32" and sys.version_info >= (3, 12): AF_HYPERV = 34 if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12): # FreeBSD >= 14.0 AF_DIVERT = 44 -AF_INET = AddressFamily.AF_INET -AF_INET6 = AddressFamily.AF_INET6 -AF_APPLETALK = AddressFamily.AF_APPLETALK -AF_DECnet = AddressFamily.AF_DECnet -AF_IPX = AddressFamily.AF_IPX -AF_SNA = AddressFamily.AF_SNA -AF_UNSPEC = AddressFamily.AF_UNSPEC +AF_INET: Final = AddressFamily.AF_INET +AF_INET6: Final = AddressFamily.AF_INET6 +AF_APPLETALK: Final = AddressFamily.AF_APPLETALK +AF_DECnet: Final = 12 +AF_IPX: Final = AddressFamily.AF_IPX +AF_SNA: Final = AddressFamily.AF_SNA +AF_UNSPEC: Final = AddressFamily.AF_UNSPEC if sys.platform != "darwin": - AF_IRDA = AddressFamily.AF_IRDA + AF_IRDA: Final = AddressFamily.AF_IRDA if sys.platform != "win32": - AF_ROUTE = AddressFamily.AF_ROUTE - AF_SYSTEM = AddressFamily.AF_SYSTEM - AF_UNIX = AddressFamily.AF_UNIX + AF_ROUTE: Final = AddressFamily.AF_ROUTE + AF_UNIX: Final = AddressFamily.AF_UNIX + +if sys.platform == "darwin": + AF_SYSTEM: Final = 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 - AF_AX25 = AddressFamily.AF_AX25 - AF_BRIDGE = AddressFamily.AF_BRIDGE - AF_ECONET = AddressFamily.AF_ECONET - AF_KEY = AddressFamily.AF_KEY - AF_LLC = AddressFamily.AF_LLC - AF_NETBEUI = AddressFamily.AF_NETBEUI - AF_NETROM = AddressFamily.AF_NETROM - AF_PPPOX = AddressFamily.AF_PPPOX - AF_ROSE = AddressFamily.AF_ROSE - AF_SECURITY = AddressFamily.AF_SECURITY - AF_WANPIPE = AddressFamily.AF_WANPIPE - AF_X25 = AddressFamily.AF_X25 + AF_ASH: Final = AddressFamily.AF_ASH + AF_ATMPVC: Final = AddressFamily.AF_ATMPVC + AF_ATMSVC: Final = AddressFamily.AF_ATMSVC + AF_AX25: Final = AddressFamily.AF_AX25 + AF_BRIDGE: Final = AddressFamily.AF_BRIDGE + AF_ECONET: Final = AddressFamily.AF_ECONET + AF_KEY: Final = AddressFamily.AF_KEY + AF_LLC: Final = AddressFamily.AF_LLC + AF_NETBEUI: Final = AddressFamily.AF_NETBEUI + AF_NETROM: Final = AddressFamily.AF_NETROM + AF_PPPOX: Final = AddressFamily.AF_PPPOX + AF_ROSE: Final = AddressFamily.AF_ROSE + AF_SECURITY: Final = AddressFamily.AF_SECURITY + AF_WANPIPE: Final = AddressFamily.AF_WANPIPE + AF_X25: Final = AddressFamily.AF_X25 if sys.platform == "linux": - AF_CAN = AddressFamily.AF_CAN - AF_PACKET = AddressFamily.AF_PACKET - AF_RDS = AddressFamily.AF_RDS - AF_TIPC = AddressFamily.AF_TIPC - AF_ALG = AddressFamily.AF_ALG - AF_NETLINK = AddressFamily.AF_NETLINK - AF_VSOCK = AddressFamily.AF_VSOCK - AF_QIPCRTR = AddressFamily.AF_QIPCRTR - -if sys.platform != "win32" or sys.version_info >= (3, 9): - AF_LINK = AddressFamily.AF_LINK - if sys.platform != "darwin": - AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH + AF_CAN: Final = AddressFamily.AF_CAN + AF_PACKET: Final = AddressFamily.AF_PACKET + AF_RDS: Final = AddressFamily.AF_RDS + AF_TIPC: Final = AddressFamily.AF_TIPC + AF_ALG: Final = AddressFamily.AF_ALG + AF_NETLINK: Final = AddressFamily.AF_NETLINK + AF_VSOCK: Final = AddressFamily.AF_VSOCK + AF_QIPCRTR: Final = AddressFamily.AF_QIPCRTR +if sys.platform != "linux": + AF_LINK: Final = AddressFamily.AF_LINK +if sys.platform != "darwin" and sys.platform != "linux": + AF_BLUETOOTH: Final = AddressFamily.AF_BLUETOOTH if sys.platform == "win32" and sys.version_info >= (3, 12): - AF_HYPERV = AddressFamily.AF_HYPERV + AF_HYPERV: Final = AddressFamily.AF_HYPERV if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 12): # FreeBSD >= 14.0 - AF_DIVERT = AddressFamily.AF_DIVERT + AF_DIVERT: Final = AddressFamily.AF_DIVERT class SocketKind(IntEnum): SOCK_STREAM = 1 @@ -601,14 +1189,14 @@ class SocketKind(IntEnum): SOCK_CLOEXEC = 524288 SOCK_NONBLOCK = 2048 -SOCK_STREAM = SocketKind.SOCK_STREAM -SOCK_DGRAM = SocketKind.SOCK_DGRAM -SOCK_RAW = SocketKind.SOCK_RAW -SOCK_RDM = SocketKind.SOCK_RDM -SOCK_SEQPACKET = SocketKind.SOCK_SEQPACKET +SOCK_STREAM: Final = SocketKind.SOCK_STREAM +SOCK_DGRAM: Final = SocketKind.SOCK_DGRAM +SOCK_RAW: Final = SocketKind.SOCK_RAW +SOCK_RDM: Final = SocketKind.SOCK_RDM +SOCK_SEQPACKET: Final = SocketKind.SOCK_SEQPACKET if sys.platform == "linux": - SOCK_CLOEXEC = SocketKind.SOCK_CLOEXEC - SOCK_NONBLOCK = SocketKind.SOCK_NONBLOCK + SOCK_CLOEXEC: Final = SocketKind.SOCK_CLOEXEC + SOCK_NONBLOCK: Final = SocketKind.SOCK_NONBLOCK class MsgFlag(IntFlag): MSG_CTRUNC = 8 @@ -617,53 +1205,59 @@ 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 -MSG_OOB = MsgFlag.MSG_OOB -MSG_PEEK = MsgFlag.MSG_PEEK -MSG_TRUNC = MsgFlag.MSG_TRUNC -MSG_WAITALL = MsgFlag.MSG_WAITALL +MSG_CTRUNC: Final = MsgFlag.MSG_CTRUNC +MSG_DONTROUTE: Final = MsgFlag.MSG_DONTROUTE +MSG_OOB: Final = MsgFlag.MSG_OOB +MSG_PEEK: Final = MsgFlag.MSG_PEEK +MSG_TRUNC: Final = MsgFlag.MSG_TRUNC +MSG_WAITALL: Final = MsgFlag.MSG_WAITALL + +if sys.platform == "win32": + MSG_BCAST: Final = MsgFlag.MSG_BCAST + MSG_MCAST: Final = MsgFlag.MSG_MCAST if sys.platform != "darwin": - MSG_BCAST = MsgFlag.MSG_BCAST - MSG_MCAST = MsgFlag.MSG_MCAST - MSG_ERRQUEUE = MsgFlag.MSG_ERRQUEUE + MSG_ERRQUEUE: Final = 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 + MSG_DONTWAIT: Final = MsgFlag.MSG_DONTWAIT + MSG_EOR: Final = MsgFlag.MSG_EOR + MSG_NOSIGNAL: Final = 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 - MSG_NOTIFICATION = MsgFlag.MSG_NOTIFICATION + MSG_CMSG_CLOEXEC: Final = MsgFlag.MSG_CMSG_CLOEXEC + MSG_CONFIRM: Final = MsgFlag.MSG_CONFIRM + MSG_FASTOPEN: Final = MsgFlag.MSG_FASTOPEN + MSG_MORE: Final = MsgFlag.MSG_MORE + +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + MSG_NOTIFICATION: Final = MsgFlag.MSG_NOTIFICATION + +if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF: Final = MsgFlag.MSG_EOF class AddressInfo(IntFlag): AI_ADDRCONFIG = 32 @@ -673,27 +1267,28 @@ 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 -AI_ADDRCONFIG = AddressInfo.AI_ADDRCONFIG -AI_ALL = AddressInfo.AI_ALL -AI_CANONNAME = AddressInfo.AI_CANONNAME -AI_NUMERICHOST = AddressInfo.AI_NUMERICHOST -AI_NUMERICSERV = AddressInfo.AI_NUMERICSERV -AI_PASSIVE = AddressInfo.AI_PASSIVE -AI_V4MAPPED = AddressInfo.AI_V4MAPPED +AI_ADDRCONFIG: Final = AddressInfo.AI_ADDRCONFIG +AI_ALL: Final = AddressInfo.AI_ALL +AI_CANONNAME: Final = AddressInfo.AI_CANONNAME +AI_NUMERICHOST: Final = AddressInfo.AI_NUMERICHOST +AI_NUMERICSERV: Final = AddressInfo.AI_NUMERICSERV +AI_PASSIVE: Final = AddressInfo.AI_PASSIVE +AI_V4MAPPED: Final = AddressInfo.AI_V4MAPPED -if sys.platform != "win32": - AI_DEFAULT = AddressInfo.AI_DEFAULT - AI_MASK = AddressInfo.AI_MASK - AI_V4MAPPED_CFG = AddressInfo.AI_V4MAPPED_CFG +if sys.platform != "win32" and sys.platform != "linux": + AI_DEFAULT: Final = AddressInfo.AI_DEFAULT + AI_MASK: Final = AddressInfo.AI_MASK + AI_V4MAPPED_CFG: Final = AddressInfo.AI_V4MAPPED_CFG if sys.platform == "win32": errorTab: dict[int, str] # undocumented +@type_check_only class _SendableFile(Protocol): def read(self, size: int, /) -> bytes: ... def seek(self, offset: int, /) -> object: ... @@ -705,12 +1300,13 @@ class _SendableFile(Protocol): # def fileno(self) -> int: ... class socket(_socket.socket): + __slots__ = ["__weakref__", "_io_refs", "_closed"] def __init__( self, family: AddressFamily | int = -1, type: SocketKind | int = -1, proto: int = -1, fileno: int | None = None ) -> 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 @@ -785,11 +1381,10 @@ class socket(_socket.socket): def fromfd(fd: SupportsIndex, family: AddressFamily | int, type: SocketKind | int, proto: int = 0) -> socket: ... if sys.platform != "win32": - if sys.version_info >= (3, 9): - def send_fds( - sock: socket, buffers: Iterable[ReadableBuffer], fds: Iterable[int], flags: Unused = 0, address: Unused = None - ) -> int: ... - def recv_fds(sock: socket, bufsize: int, maxfds: int, flags: int = 0) -> tuple[bytes, list[int], int, Any]: ... + def send_fds( + sock: socket, buffers: Iterable[ReadableBuffer], fds: Iterable[int], flags: Unused = 0, address: Unused = None + ) -> int: ... + def recv_fds(sock: socket, bufsize: int, maxfds: int, flags: int = 0) -> tuple[bytes, list[int], int, Any]: ... if sys.platform == "win32": def fromshare(info: bytes) -> socket: ... @@ -832,7 +1427,7 @@ def create_server( address: _Address, *, family: int = ..., backlog: int | None = None, reuse_port: bool = False, dualstack_ipv6: bool = False ) -> socket: ... -# the 5th tuple item is an address +# The 5th tuple item is the socket address, for IP4, IP6, or IP6 if Python is compiled with --disable-ipv6, respectively. def getaddrinfo( host: bytes | str | None, port: bytes | str | int | None, family: int = 0, type: int = 0, proto: int = 0, flags: int = 0 -) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ... +) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes]]]: ... diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index ae6575d850822..f321d14a792b2 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -35,32 +35,26 @@ if sys.platform != "win32": _RequestType: TypeAlias = _socket | tuple[bytes, _socket] _AfUnixAddress: TypeAlias = str | ReadableBuffer # address acceptable for an AF_UNIX socket _AfInetAddress: TypeAlias = tuple[str | bytes | bytearray, int] # address acceptable for an AF_INET socket +_AfInet6Address: TypeAlias = tuple[str | bytes | bytearray, int, int, int] # address acceptable for an AF_INET6 socket # This can possibly be generic at some point: class BaseServer: - address_family: int server_address: _Address - socket: _socket - allow_reuse_address: bool - request_queue_size: int - socket_type: int timeout: float | None RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler] def __init__( self, server_address: _Address, RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler] ) -> None: ... - def fileno(self) -> int: ... def handle_request(self) -> None: ... def serve_forever(self, poll_interval: float = 0.5) -> None: ... def shutdown(self) -> None: ... def server_close(self) -> None: ... def finish_request(self, request: _RequestType, client_address: _RetAddress) -> None: ... - def get_request(self) -> tuple[Any, Any]: ... + def get_request(self) -> tuple[Any, Any]: ... # Not implemented here, but expected to exist on subclasses def handle_error(self, request: _RequestType, client_address: _RetAddress) -> None: ... def handle_timeout(self) -> None: ... def process_request(self, request: _RequestType, client_address: _RetAddress) -> None: ... def server_activate(self) -> None: ... - def server_bind(self) -> None: ... def verify_request(self, request: _RequestType, client_address: _RetAddress) -> bool: ... def __enter__(self) -> Self: ... def __exit__( @@ -71,16 +65,23 @@ class BaseServer: def close_request(self, request: _RequestType) -> None: ... # undocumented class TCPServer(BaseServer): + address_family: int + socket: _socket + allow_reuse_address: bool + request_queue_size: int + socket_type: int if sys.version_info >= (3, 11): allow_reuse_port: bool - server_address: _AfInetAddress + server_address: _AfInetAddress | _AfInet6Address def __init__( self, - server_address: _AfInetAddress, + server_address: _AfInetAddress | _AfInet6Address, RequestHandlerClass: Callable[[Any, _RetAddress, Self], BaseRequestHandler], bind_and_activate: bool = True, ) -> None: ... + def fileno(self) -> int: ... def get_request(self) -> tuple[_socket, _RetAddress]: ... + def server_bind(self) -> None: ... class UDPServer(TCPServer): max_packet_size: ClassVar[int] diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index d747be90fd0a5..882cd143c29ce 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -1 +1,478 @@ -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, +) +from types import TracebackType +from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload, type_check_only +from typing_extensions import Self, TypeAlias, disjoint_base + +if sys.version_info < (3, 14): + from sqlite3.dbapi2 import version_info as version_info + +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] +# Controls the legacy transaction handling mode of sqlite3. +_IsolationLevel: TypeAlias = Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None + +@type_check_only +class _AnyParamWindowAggregateClass(Protocol): + def step(self, *args: Any) -> object: ... + def inverse(self, *args: Any) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +@type_check_only +class _WindowAggregateClass(Protocol): + step: Callable[..., object] + inverse: Callable[..., object] + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +@type_check_only +class _AggregateProtocol(Protocol): + def step(self, value: int, /) -> object: ... + def finalize(self) -> int: ... + +@type_check_only +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): ... + +@disjoint_base +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: _IsolationLevel + @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 = 5.0, + detect_types: int = 0, + isolation_level: _IsolationLevel = "DEFERRED", + check_same_thread: bool = True, + factory: type[Connection] | None = ..., + cached_statements: int = 128, + uri: bool = False, + autocommit: bool = ..., + ) -> None: ... + else: + def __init__( + self, + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: _IsolationLevel = "DEFERRED", + check_same_thread: bool = True, + factory: type[Connection] | None = ..., + cached_statements: int = 128, + uri: bool = False, + ) -> 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]: ... + +@disjoint_base +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: ... + +@disjoint_base +class Row(Sequence[Any]): + def __new__(cls, cursor: Cursor, data: tuple[Any, ...], /) -> Self: ... + 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 0ee511df4e374..9e170a81243d8 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -1,561 +1,247 @@ -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, Final, 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, +) +from typing import Final, Literal +from typing_extensions import deprecated -_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] - -paramstyle: str -threadsafety: int -apilevel: str -Date = date -Time = time -Timestamp = datetime - -def DateFromTicks(ticks: float) -> Date: ... -def TimeFromTicks(ticks: float) -> Time: ... -def TimestampFromTicks(ticks: float) -> Timestamp: ... - -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. +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, + ) -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] if sys.version_info >= (3, 11): - SQLITE_LIMIT_LENGTH: Final[int] - SQLITE_LIMIT_SQL_LENGTH: Final[int] - SQLITE_LIMIT_COLUMN: Final[int] - SQLITE_LIMIT_EXPR_DEPTH: Final[int] - SQLITE_LIMIT_COMPOUND_SELECT: Final[int] - SQLITE_LIMIT_VDBE_OP: Final[int] - SQLITE_LIMIT_FUNCTION_ARG: Final[int] - SQLITE_LIMIT_ATTACHED: Final[int] - SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int] - SQLITE_LIMIT_VARIABLE_NUMBER: Final[int] - SQLITE_LIMIT_TRIGGER_DEPTH: Final[int] - SQLITE_LIMIT_WORKER_THREADS: Final[int] -SQLITE_PRAGMA: Final[int] -SQLITE_READ: Final[int] -SQLITE_REINDEX: Final[int] -SQLITE_RECURSIVE: 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 + 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 in 3.12, removed in 3.14. - version: str - -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_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] - -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] - -# 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: ... + # Deprecated and removed from _sqlite3 in 3.12, but removed from here in 3.14. + version: Final[str] 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: ... + if sys.version_info >= (3, 10): + # deprecation wrapper that has a different name for the argument... + @deprecated( + "Deprecated since Python 3.10; removed in Python 3.12. " + "Open database in URI mode using `cache=shared` parameter instead." + ) + def enable_shared_cache(enable: int) -> 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): ... + from _sqlite3 import enable_shared_cache as enable_shared_cache if sys.version_info < (3, 10): - OptimizedUnicode = str - -@final -class PrepareProtocol: - def __init__(self, *args: object, **kwargs: object) -> None: ... + from _sqlite3 import OptimizedUnicode as OptimizedUnicode -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: ... +paramstyle: Final = "qmark" +threadsafety: Literal[0, 1, 3] +apilevel: Final[str] +Date = date +Time = time +Timestamp = datetime -@final -class _Statement: ... +def DateFromTicks(ticks: float) -> Date: ... +def TimeFromTicks(ticks: float) -> Time: ... +def TimestampFromTicks(ticks: float) -> Timestamp: ... -class Warning(Exception): ... +if sys.version_info < (3, 14): + # Deprecated in 3.12, removed in 3.14. + version_info: Final[tuple[int, int, int]] -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: ... +sqlite_version_info: Final[tuple[int, int, int]] +Binary = memoryview 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_compile.pyi b/mypy/typeshed/stdlib/sre_compile.pyi index 2d04a886c931b..d8f0b7937e994 100644 --- a/mypy/typeshed/stdlib/sre_compile.pyi +++ b/mypy/typeshed/stdlib/sre_compile.pyi @@ -2,9 +2,9 @@ from re import Pattern from sre_constants import * from sre_constants import _NamedIntConstant from sre_parse import SubPattern -from typing import Any +from typing import Any, Final -MAXCODE: int +MAXCODE: Final[int] def dis(code: list[_NamedIntConstant]) -> None: ... def isstring(obj: Any) -> bool: ... diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi index 0c1e484bb07e3..9a1da4ac89e7e 100644 --- a/mypy/typeshed/stdlib/sre_constants.pyi +++ b/mypy/typeshed/stdlib/sre_constants.pyi @@ -1,24 +1,24 @@ import sys -from typing import Any -from typing_extensions import Self +from re import error as error +from typing import Final +from typing_extensions import Self, disjoint_base -MAXGROUPS: int +MAXGROUPS: Final[int] -MAGIC: int +MAGIC: Final[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: ... +if sys.version_info >= (3, 12): + class _NamedIntConstant(int): + name: str + def __new__(cls, value: int, name: str) -> Self: ... -class _NamedIntConstant(int): - name: Any - def __new__(cls, value: int, name: str) -> Self: ... +else: + @disjoint_base + class _NamedIntConstant(int): + name: str + def __new__(cls, value: int, name: str) -> Self: ... -MAXREPEAT: _NamedIntConstant +MAXREPEAT: Final[_NamedIntConstant] OPCODES: list[_NamedIntConstant] ATCODES: list[_NamedIntConstant] CHCODES: list[_NamedIntConstant] @@ -30,102 +30,106 @@ AT_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] AT_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] CH_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] CH_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] +if sys.version_info >= (3, 14): + CH_NEGATE: dict[_NamedIntConstant, _NamedIntConstant] +# flags if sys.version_info < (3, 13): - SRE_FLAG_TEMPLATE: int -SRE_FLAG_IGNORECASE: int -SRE_FLAG_LOCALE: int -SRE_FLAG_MULTILINE: int -SRE_FLAG_DOTALL: int -SRE_FLAG_UNICODE: int -SRE_FLAG_VERBOSE: int -SRE_FLAG_DEBUG: int -SRE_FLAG_ASCII: int -SRE_INFO_PREFIX: int -SRE_INFO_LITERAL: int -SRE_INFO_CHARSET: int + SRE_FLAG_TEMPLATE: Final = 1 +SRE_FLAG_IGNORECASE: Final = 2 +SRE_FLAG_LOCALE: Final = 4 +SRE_FLAG_MULTILINE: Final = 8 +SRE_FLAG_DOTALL: Final = 16 +SRE_FLAG_UNICODE: Final = 32 +SRE_FLAG_VERBOSE: Final = 64 +SRE_FLAG_DEBUG: Final = 128 +SRE_FLAG_ASCII: Final = 256 +# flags for INFO primitive +SRE_INFO_PREFIX: Final = 1 +SRE_INFO_LITERAL: Final = 2 +SRE_INFO_CHARSET: Final = 4 # Stubgen above; manually defined constants below (dynamic at runtime) # from OPCODES -FAILURE: _NamedIntConstant -SUCCESS: _NamedIntConstant -ANY: _NamedIntConstant -ANY_ALL: _NamedIntConstant -ASSERT: _NamedIntConstant -ASSERT_NOT: _NamedIntConstant -AT: _NamedIntConstant -BRANCH: _NamedIntConstant +FAILURE: Final[_NamedIntConstant] +SUCCESS: Final[_NamedIntConstant] +ANY: Final[_NamedIntConstant] +ANY_ALL: Final[_NamedIntConstant] +ASSERT: Final[_NamedIntConstant] +ASSERT_NOT: Final[_NamedIntConstant] +AT: Final[_NamedIntConstant] +BRANCH: Final[_NamedIntConstant] if sys.version_info < (3, 11): - CALL: _NamedIntConstant -CATEGORY: _NamedIntConstant -CHARSET: _NamedIntConstant -BIGCHARSET: _NamedIntConstant -GROUPREF: _NamedIntConstant -GROUPREF_EXISTS: _NamedIntConstant -GROUPREF_IGNORE: _NamedIntConstant -IN: _NamedIntConstant -IN_IGNORE: _NamedIntConstant -INFO: _NamedIntConstant -JUMP: _NamedIntConstant -LITERAL: _NamedIntConstant -LITERAL_IGNORE: _NamedIntConstant -MARK: _NamedIntConstant -MAX_UNTIL: _NamedIntConstant -MIN_UNTIL: _NamedIntConstant -NOT_LITERAL: _NamedIntConstant -NOT_LITERAL_IGNORE: _NamedIntConstant -NEGATE: _NamedIntConstant -RANGE: _NamedIntConstant -REPEAT: _NamedIntConstant -REPEAT_ONE: _NamedIntConstant -SUBPATTERN: _NamedIntConstant -MIN_REPEAT_ONE: _NamedIntConstant + CALL: Final[_NamedIntConstant] +CATEGORY: Final[_NamedIntConstant] +CHARSET: Final[_NamedIntConstant] +BIGCHARSET: Final[_NamedIntConstant] +GROUPREF: Final[_NamedIntConstant] +GROUPREF_EXISTS: Final[_NamedIntConstant] +GROUPREF_IGNORE: Final[_NamedIntConstant] +IN: Final[_NamedIntConstant] +IN_IGNORE: Final[_NamedIntConstant] +INFO: Final[_NamedIntConstant] +JUMP: Final[_NamedIntConstant] +LITERAL: Final[_NamedIntConstant] +LITERAL_IGNORE: Final[_NamedIntConstant] +MARK: Final[_NamedIntConstant] +MAX_UNTIL: Final[_NamedIntConstant] +MIN_UNTIL: Final[_NamedIntConstant] +NOT_LITERAL: Final[_NamedIntConstant] +NOT_LITERAL_IGNORE: Final[_NamedIntConstant] +NEGATE: Final[_NamedIntConstant] +RANGE: Final[_NamedIntConstant] +REPEAT: Final[_NamedIntConstant] +REPEAT_ONE: Final[_NamedIntConstant] +SUBPATTERN: Final[_NamedIntConstant] +MIN_REPEAT_ONE: Final[_NamedIntConstant] if sys.version_info >= (3, 11): - ATOMIC_GROUP: _NamedIntConstant - POSSESSIVE_REPEAT: _NamedIntConstant - POSSESSIVE_REPEAT_ONE: _NamedIntConstant -RANGE_UNI_IGNORE: _NamedIntConstant -GROUPREF_LOC_IGNORE: _NamedIntConstant -GROUPREF_UNI_IGNORE: _NamedIntConstant -IN_LOC_IGNORE: _NamedIntConstant -IN_UNI_IGNORE: _NamedIntConstant -LITERAL_LOC_IGNORE: _NamedIntConstant -LITERAL_UNI_IGNORE: _NamedIntConstant -NOT_LITERAL_LOC_IGNORE: _NamedIntConstant -NOT_LITERAL_UNI_IGNORE: _NamedIntConstant -MIN_REPEAT: _NamedIntConstant -MAX_REPEAT: _NamedIntConstant + ATOMIC_GROUP: Final[_NamedIntConstant] + POSSESSIVE_REPEAT: Final[_NamedIntConstant] + POSSESSIVE_REPEAT_ONE: Final[_NamedIntConstant] +RANGE_UNI_IGNORE: Final[_NamedIntConstant] +GROUPREF_LOC_IGNORE: Final[_NamedIntConstant] +GROUPREF_UNI_IGNORE: Final[_NamedIntConstant] +IN_LOC_IGNORE: Final[_NamedIntConstant] +IN_UNI_IGNORE: Final[_NamedIntConstant] +LITERAL_LOC_IGNORE: Final[_NamedIntConstant] +LITERAL_UNI_IGNORE: Final[_NamedIntConstant] +NOT_LITERAL_LOC_IGNORE: Final[_NamedIntConstant] +NOT_LITERAL_UNI_IGNORE: Final[_NamedIntConstant] +MIN_REPEAT: Final[_NamedIntConstant] +MAX_REPEAT: Final[_NamedIntConstant] # from ATCODES -AT_BEGINNING: _NamedIntConstant -AT_BEGINNING_LINE: _NamedIntConstant -AT_BEGINNING_STRING: _NamedIntConstant -AT_BOUNDARY: _NamedIntConstant -AT_NON_BOUNDARY: _NamedIntConstant -AT_END: _NamedIntConstant -AT_END_LINE: _NamedIntConstant -AT_END_STRING: _NamedIntConstant -AT_LOC_BOUNDARY: _NamedIntConstant -AT_LOC_NON_BOUNDARY: _NamedIntConstant -AT_UNI_BOUNDARY: _NamedIntConstant -AT_UNI_NON_BOUNDARY: _NamedIntConstant +AT_BEGINNING: Final[_NamedIntConstant] +AT_BEGINNING_LINE: Final[_NamedIntConstant] +AT_BEGINNING_STRING: Final[_NamedIntConstant] +AT_BOUNDARY: Final[_NamedIntConstant] +AT_NON_BOUNDARY: Final[_NamedIntConstant] +AT_END: Final[_NamedIntConstant] +AT_END_LINE: Final[_NamedIntConstant] +AT_END_STRING: Final[_NamedIntConstant] +AT_LOC_BOUNDARY: Final[_NamedIntConstant] +AT_LOC_NON_BOUNDARY: Final[_NamedIntConstant] +AT_UNI_BOUNDARY: Final[_NamedIntConstant] +AT_UNI_NON_BOUNDARY: Final[_NamedIntConstant] # from CHCODES -CATEGORY_DIGIT: _NamedIntConstant -CATEGORY_NOT_DIGIT: _NamedIntConstant -CATEGORY_SPACE: _NamedIntConstant -CATEGORY_NOT_SPACE: _NamedIntConstant -CATEGORY_WORD: _NamedIntConstant -CATEGORY_NOT_WORD: _NamedIntConstant -CATEGORY_LINEBREAK: _NamedIntConstant -CATEGORY_NOT_LINEBREAK: _NamedIntConstant -CATEGORY_LOC_WORD: _NamedIntConstant -CATEGORY_LOC_NOT_WORD: _NamedIntConstant -CATEGORY_UNI_DIGIT: _NamedIntConstant -CATEGORY_UNI_NOT_DIGIT: _NamedIntConstant -CATEGORY_UNI_SPACE: _NamedIntConstant -CATEGORY_UNI_NOT_SPACE: _NamedIntConstant -CATEGORY_UNI_WORD: _NamedIntConstant -CATEGORY_UNI_NOT_WORD: _NamedIntConstant -CATEGORY_UNI_LINEBREAK: _NamedIntConstant -CATEGORY_UNI_NOT_LINEBREAK: _NamedIntConstant +CATEGORY_DIGIT: Final[_NamedIntConstant] +CATEGORY_NOT_DIGIT: Final[_NamedIntConstant] +CATEGORY_SPACE: Final[_NamedIntConstant] +CATEGORY_NOT_SPACE: Final[_NamedIntConstant] +CATEGORY_WORD: Final[_NamedIntConstant] +CATEGORY_NOT_WORD: Final[_NamedIntConstant] +CATEGORY_LINEBREAK: Final[_NamedIntConstant] +CATEGORY_NOT_LINEBREAK: Final[_NamedIntConstant] +CATEGORY_LOC_WORD: Final[_NamedIntConstant] +CATEGORY_LOC_NOT_WORD: Final[_NamedIntConstant] +CATEGORY_UNI_DIGIT: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_DIGIT: Final[_NamedIntConstant] +CATEGORY_UNI_SPACE: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_SPACE: Final[_NamedIntConstant] +CATEGORY_UNI_WORD: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_WORD: Final[_NamedIntConstant] +CATEGORY_UNI_LINEBREAK: Final[_NamedIntConstant] +CATEGORY_UNI_NOT_LINEBREAK: Final[_NamedIntConstant] diff --git a/mypy/typeshed/stdlib/sre_parse.pyi b/mypy/typeshed/stdlib/sre_parse.pyi index c242bd2a065fb..eaacbff312a92 100644 --- a/mypy/typeshed/stdlib/sre_parse.pyi +++ b/mypy/typeshed/stdlib/sre_parse.pyi @@ -3,24 +3,24 @@ from collections.abc import Iterable from re import Match, Pattern as _Pattern from sre_constants import * from sre_constants import _NamedIntConstant as _NIC, error as _Error -from typing import Any, overload +from typing import Any, Final, overload from typing_extensions import TypeAlias -SPECIAL_CHARS: str -REPEAT_CHARS: str -DIGITS: frozenset[str] -OCTDIGITS: frozenset[str] -HEXDIGITS: frozenset[str] -ASCIILETTERS: frozenset[str] -WHITESPACE: frozenset[str] -ESCAPES: dict[str, tuple[_NIC, int]] -CATEGORIES: dict[str, tuple[_NIC, _NIC] | tuple[_NIC, list[tuple[_NIC, _NIC]]]] -FLAGS: dict[str, int] -TYPE_FLAGS: int -GLOBAL_FLAGS: int +SPECIAL_CHARS: Final = ".\\[{()*+?^$|" +REPEAT_CHARS: Final = "*+?{" +DIGITS: Final[frozenset[str]] +OCTDIGITS: Final[frozenset[str]] +HEXDIGITS: Final[frozenset[str]] +ASCIILETTERS: Final[frozenset[str]] +WHITESPACE: Final[frozenset[str]] +ESCAPES: Final[dict[str, tuple[_NIC, int]]] +CATEGORIES: Final[dict[str, tuple[_NIC, _NIC] | tuple[_NIC, list[tuple[_NIC, _NIC]]]]] +FLAGS: Final[dict[str, int]] +TYPE_FLAGS: Final[int] +GLOBAL_FLAGS: Final[int] if sys.version_info >= (3, 11): - MAXWIDTH: int + MAXWIDTH: Final[int] if sys.version_info < (3, 11): class Verbose(Exception): ... @@ -39,7 +39,7 @@ class State: lookbehindgroups: int | None @property def groups(self) -> int: ... - def opengroup(self, name: str | None = ...) -> int: ... + def opengroup(self, name: str | None = None) -> int: ... def closegroup(self, gid: int, p: SubPattern) -> None: ... def checkgroup(self, gid: int) -> bool: ... def checklookbehindgroup(self, gid: int, source: Tokenizer) -> None: ... diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 81c68c69ec4e9..aa94fc84255e5 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -1,22 +1,59 @@ 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_extensions import Never, Self, TypeAlias +from typing import Any, Final, Literal, NamedTuple, TypedDict, overload, type_check_only +from typing_extensions import Never, Self, TypeAlias, deprecated + +if sys.version_info >= (3, 13): + from _ssl import HAS_PSK as HAS_PSK + +if sys.version_info >= (3, 14): + from _ssl import HAS_PHA as HAS_PHA + +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 +@type_check_only class _Cipher(TypedDict): aead: bool alg_bits: int @@ -47,6 +84,7 @@ class SSLCertVerificationError(SSLError, ValueError): CertificateError = SSLCertVerificationError if sys.version_info < (3, 12): + @deprecated("Deprecated since Python 3.7; removed in Python 3.12. Use `SSLContext.wrap_socket()` instead.") def wrap_socket( sock: socket.socket, keyfile: StrOrBytesPath | None = None, @@ -59,55 +97,7 @@ if sys.version_info < (3, 12): suppress_ragged_eofs: bool = True, ciphers: str | None = None, ) -> SSLSocket: ... - -def create_default_context( - purpose: Purpose = ..., - *, - cafile: StrOrBytesPath | None = None, - capath: StrOrBytesPath | None = None, - cadata: str | ReadableBuffer | None = None, -) -> SSLContext: ... - -if sys.version_info >= (3, 10): - def _create_unverified_context( - protocol: int | None = None, - *, - cert_reqs: int = ..., - check_hostname: bool = False, - purpose: Purpose = ..., - certfile: StrOrBytesPath | None = None, - keyfile: StrOrBytesPath | None = None, - cafile: StrOrBytesPath | None = None, - capath: StrOrBytesPath | None = None, - cadata: str | ReadableBuffer | None = None, - ) -> SSLContext: ... - -else: - def _create_unverified_context( - protocol: int = ..., - *, - cert_reqs: int = ..., - check_hostname: bool = False, - purpose: Purpose = ..., - certfile: StrOrBytesPath | None = None, - keyfile: StrOrBytesPath | None = None, - cafile: StrOrBytesPath | None = None, - capath: StrOrBytesPath | None = None, - cadata: str | ReadableBuffer | None = None, - ) -> SSLContext: ... - -_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): + @deprecated("Deprecated since Python 3.7; removed in Python 3.12.") def match_hostname(cert: _PeerCertRetDictType, hostname: str) -> None: ... def cert_time_to_seconds(cert_time: str) -> int: ... @@ -133,18 +123,14 @@ 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 CERT_REQUIRED = 2 -CERT_NONE: VerifyMode -CERT_OPTIONAL: VerifyMode -CERT_REQUIRED: VerifyMode +CERT_NONE: Final = VerifyMode.CERT_NONE +CERT_OPTIONAL: Final = VerifyMode.CERT_OPTIONAL +CERT_REQUIRED: Final = VerifyMode.CERT_REQUIRED class VerifyFlags(enum.IntFlag): VERIFY_DEFAULT = 0 @@ -156,15 +142,15 @@ class VerifyFlags(enum.IntFlag): VERIFY_ALLOW_PROXY_CERTS = 64 VERIFY_X509_PARTIAL_CHAIN = 524288 -VERIFY_DEFAULT: VerifyFlags -VERIFY_CRL_CHECK_LEAF: VerifyFlags -VERIFY_CRL_CHECK_CHAIN: VerifyFlags -VERIFY_X509_STRICT: VerifyFlags -VERIFY_X509_TRUSTED_FIRST: VerifyFlags +VERIFY_DEFAULT: Final = VerifyFlags.VERIFY_DEFAULT +VERIFY_CRL_CHECK_LEAF: Final = VerifyFlags.VERIFY_CRL_CHECK_LEAF +VERIFY_CRL_CHECK_CHAIN: Final = VerifyFlags.VERIFY_CRL_CHECK_CHAIN +VERIFY_X509_STRICT: Final = VerifyFlags.VERIFY_X509_STRICT +VERIFY_X509_TRUSTED_FIRST: Final = VerifyFlags.VERIFY_X509_TRUSTED_FIRST if sys.version_info >= (3, 10): - VERIFY_ALLOW_PROXY_CERTS: VerifyFlags - VERIFY_X509_PARTIAL_CHAIN: VerifyFlags + VERIFY_ALLOW_PROXY_CERTS: Final = VerifyFlags.VERIFY_ALLOW_PROXY_CERTS + VERIFY_X509_PARTIAL_CHAIN: Final = VerifyFlags.VERIFY_X509_PARTIAL_CHAIN class _SSLMethod(enum.IntEnum): PROTOCOL_SSLv23 = 2 @@ -177,15 +163,15 @@ class _SSLMethod(enum.IntEnum): PROTOCOL_TLS_CLIENT = 16 PROTOCOL_TLS_SERVER = 17 -PROTOCOL_SSLv23: _SSLMethod -PROTOCOL_SSLv2: _SSLMethod -PROTOCOL_SSLv3: _SSLMethod -PROTOCOL_TLSv1: _SSLMethod -PROTOCOL_TLSv1_1: _SSLMethod -PROTOCOL_TLSv1_2: _SSLMethod -PROTOCOL_TLS: _SSLMethod -PROTOCOL_TLS_CLIENT: _SSLMethod -PROTOCOL_TLS_SERVER: _SSLMethod +PROTOCOL_SSLv23: Final = _SSLMethod.PROTOCOL_SSLv23 +PROTOCOL_SSLv2: Final = _SSLMethod.PROTOCOL_SSLv2 +PROTOCOL_SSLv3: Final = _SSLMethod.PROTOCOL_SSLv3 +PROTOCOL_TLSv1: Final = _SSLMethod.PROTOCOL_TLSv1 +PROTOCOL_TLSv1_1: Final = _SSLMethod.PROTOCOL_TLSv1_1 +PROTOCOL_TLSv1_2: Final = _SSLMethod.PROTOCOL_TLSv1_2 +PROTOCOL_TLS: Final = _SSLMethod.PROTOCOL_TLS +PROTOCOL_TLS_CLIENT: Final = _SSLMethod.PROTOCOL_TLS_CLIENT +PROTOCOL_TLS_SERVER: Final = _SSLMethod.PROTOCOL_TLS_SERVER class Options(enum.IntFlag): OP_ALL = 2147483728 @@ -208,42 +194,29 @@ class Options(enum.IntFlag): if sys.version_info >= (3, 11) or sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF = 128 -OP_ALL: Options -OP_NO_SSLv2: Options -OP_NO_SSLv3: Options -OP_NO_TLSv1: Options -OP_NO_TLSv1_1: Options -OP_NO_TLSv1_2: Options -OP_NO_TLSv1_3: Options -OP_CIPHER_SERVER_PREFERENCE: Options -OP_SINGLE_DH_USE: Options -OP_SINGLE_ECDH_USE: Options -OP_NO_COMPRESSION: Options -OP_NO_TICKET: Options -OP_NO_RENEGOTIATION: Options -OP_ENABLE_MIDDLEBOX_COMPAT: Options +OP_ALL: Final = Options.OP_ALL +OP_NO_SSLv2: Final = Options.OP_NO_SSLv2 +OP_NO_SSLv3: Final = Options.OP_NO_SSLv3 +OP_NO_TLSv1: Final = Options.OP_NO_TLSv1 +OP_NO_TLSv1_1: Final = Options.OP_NO_TLSv1_1 +OP_NO_TLSv1_2: Final = Options.OP_NO_TLSv1_2 +OP_NO_TLSv1_3: Final = Options.OP_NO_TLSv1_3 +OP_CIPHER_SERVER_PREFERENCE: Final = Options.OP_CIPHER_SERVER_PREFERENCE +OP_SINGLE_DH_USE: Final = Options.OP_SINGLE_DH_USE +OP_SINGLE_ECDH_USE: Final = Options.OP_SINGLE_ECDH_USE +OP_NO_COMPRESSION: Final = Options.OP_NO_COMPRESSION +OP_NO_TICKET: Final = Options.OP_NO_TICKET +OP_NO_RENEGOTIATION: Final = Options.OP_NO_RENEGOTIATION +OP_ENABLE_MIDDLEBOX_COMPAT: Final = Options.OP_ENABLE_MIDDLEBOX_COMPAT if sys.version_info >= (3, 12): - OP_LEGACY_SERVER_CONNECT: Options - OP_ENABLE_KTLS: Options + OP_LEGACY_SERVER_CONNECT: Final = Options.OP_LEGACY_SERVER_CONNECT + OP_ENABLE_KTLS: Final = Options.OP_ENABLE_KTLS 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 + OP_IGNORE_UNEXPECTED_EOF: Final = Options.OP_IGNORE_UNEXPECTED_EOF + +HAS_NEVER_CHECK_COMMON_NAME: Final[bool] + +CHANNEL_BINDING_TYPES: Final[list[str]] class AlertDescription(enum.IntEnum): ALERT_DESCRIPTION_ACCESS_DENIED = 49 @@ -274,34 +247,36 @@ class AlertDescription(enum.IntEnum): ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION = 110 ALERT_DESCRIPTION_USER_CANCELLED = 90 -ALERT_DESCRIPTION_HANDSHAKE_FAILURE: AlertDescription -ALERT_DESCRIPTION_INTERNAL_ERROR: AlertDescription -ALERT_DESCRIPTION_ACCESS_DENIED: AlertDescription -ALERT_DESCRIPTION_BAD_CERTIFICATE: AlertDescription -ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: AlertDescription -ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: AlertDescription -ALERT_DESCRIPTION_BAD_RECORD_MAC: AlertDescription -ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: AlertDescription -ALERT_DESCRIPTION_CERTIFICATE_REVOKED: AlertDescription -ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: AlertDescription -ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: AlertDescription -ALERT_DESCRIPTION_CLOSE_NOTIFY: AlertDescription -ALERT_DESCRIPTION_DECODE_ERROR: AlertDescription -ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: AlertDescription -ALERT_DESCRIPTION_DECRYPT_ERROR: AlertDescription -ALERT_DESCRIPTION_ILLEGAL_PARAMETER: AlertDescription -ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: AlertDescription -ALERT_DESCRIPTION_NO_RENEGOTIATION: AlertDescription -ALERT_DESCRIPTION_PROTOCOL_VERSION: AlertDescription -ALERT_DESCRIPTION_RECORD_OVERFLOW: AlertDescription -ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: AlertDescription -ALERT_DESCRIPTION_UNKNOWN_CA: AlertDescription -ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: AlertDescription -ALERT_DESCRIPTION_UNRECOGNIZED_NAME: AlertDescription -ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: AlertDescription -ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: AlertDescription -ALERT_DESCRIPTION_USER_CANCELLED: AlertDescription - +ALERT_DESCRIPTION_HANDSHAKE_FAILURE: Final = AlertDescription.ALERT_DESCRIPTION_HANDSHAKE_FAILURE +ALERT_DESCRIPTION_INTERNAL_ERROR: Final = AlertDescription.ALERT_DESCRIPTION_INTERNAL_ERROR +ALERT_DESCRIPTION_ACCESS_DENIED: Final = AlertDescription.ALERT_DESCRIPTION_ACCESS_DENIED +ALERT_DESCRIPTION_BAD_CERTIFICATE: Final = AlertDescription.ALERT_DESCRIPTION_BAD_CERTIFICATE +ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: Final = AlertDescription.ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE +ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: Final = AlertDescription.ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE +ALERT_DESCRIPTION_BAD_RECORD_MAC: Final = AlertDescription.ALERT_DESCRIPTION_BAD_RECORD_MAC +ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_EXPIRED +ALERT_DESCRIPTION_CERTIFICATE_REVOKED: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_REVOKED +ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN +ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: Final = AlertDescription.ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE +ALERT_DESCRIPTION_CLOSE_NOTIFY: Final = AlertDescription.ALERT_DESCRIPTION_CLOSE_NOTIFY +ALERT_DESCRIPTION_DECODE_ERROR: Final = AlertDescription.ALERT_DESCRIPTION_DECODE_ERROR +ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: Final = AlertDescription.ALERT_DESCRIPTION_DECOMPRESSION_FAILURE +ALERT_DESCRIPTION_DECRYPT_ERROR: Final = AlertDescription.ALERT_DESCRIPTION_DECRYPT_ERROR +ALERT_DESCRIPTION_ILLEGAL_PARAMETER: Final = AlertDescription.ALERT_DESCRIPTION_ILLEGAL_PARAMETER +ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: Final = AlertDescription.ALERT_DESCRIPTION_INSUFFICIENT_SECURITY +ALERT_DESCRIPTION_NO_RENEGOTIATION: Final = AlertDescription.ALERT_DESCRIPTION_NO_RENEGOTIATION +ALERT_DESCRIPTION_PROTOCOL_VERSION: Final = AlertDescription.ALERT_DESCRIPTION_PROTOCOL_VERSION +ALERT_DESCRIPTION_RECORD_OVERFLOW: Final = AlertDescription.ALERT_DESCRIPTION_RECORD_OVERFLOW +ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: Final = AlertDescription.ALERT_DESCRIPTION_UNEXPECTED_MESSAGE +ALERT_DESCRIPTION_UNKNOWN_CA: Final = AlertDescription.ALERT_DESCRIPTION_UNKNOWN_CA +ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: Final = AlertDescription.ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY +ALERT_DESCRIPTION_UNRECOGNIZED_NAME: Final = AlertDescription.ALERT_DESCRIPTION_UNRECOGNIZED_NAME +ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: Final = AlertDescription.ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE +ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: Final = AlertDescription.ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION +ALERT_DESCRIPTION_USER_CANCELLED: Final = AlertDescription.ALERT_DESCRIPTION_USER_CANCELLED + +# This class is not exposed. It calls itself ssl._ASN1Object. +@type_check_only class _ASN1ObjectBase(NamedTuple): nid: int shortname: str @@ -316,6 +291,10 @@ class _ASN1Object(_ASN1ObjectBase): def fromname(cls, name: str) -> Self: ... class Purpose(_ASN1Object, enum.Enum): + # Normally this class would inherit __new__ from _ASN1Object, but + # because this is an enum, the inherited __new__ is replaced at runtime with + # Enum.__new__. + def __new__(cls, value: object) -> Self: ... SERVER_AUTH = (129, "serverAuth", "TLS Web Server Authentication", "1.3.6.1.5.5.7.3.2") # pyright: ignore[reportCallIssue] CLIENT_AUTH = (130, "clientAuth", "TLS Web Client Authentication", "1.3.6.1.5.5.7.3.1") # pyright: ignore[reportCallIssue] @@ -356,7 +335,12 @@ class SSLSocket(socket.socket): def compression(self) -> str | None: ... def get_channel_binding(self, cb_type: str = "tls-unique") -> bytes | None: ... def selected_alpn_protocol(self) -> str | None: ... - def selected_npn_protocol(self) -> str | None: ... + if sys.version_info >= (3, 10): + @deprecated("Deprecated since Python 3.10. Use ALPN instead.") + def selected_npn_protocol(self) -> str | None: ... + else: + def selected_npn_protocol(self) -> str | None: ... + def accept(self) -> tuple[SSLSocket, socket._RetAddress]: ... def unwrap(self) -> socket.socket: ... def version(self) -> str | None: ... @@ -379,17 +363,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 @@ -400,17 +382,15 @@ class SSLContext: if sys.version_info >= (3, 10): security_level: int if sys.version_info >= (3, 10): - # Using the default (None) for the `protocol` parameter is deprecated, - # but there isn't a good way of marking that in the stub unless/until PEP 702 is accepted - def __new__(cls, protocol: int | None = None, *args: Any, **kwargs: Any) -> Self: ... + @overload + def __new__(cls, protocol: int, *args: Any, **kwargs: Any) -> Self: ... + @overload + @deprecated("Deprecated since Python 3.10. Use a specific version of the SSL protocol.") + def __new__(cls, protocol: None = None, *args: Any, **kwargs: Any) -> Self: ... 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_default_certs(self, purpose: Purpose = Purpose.SERVER_AUTH) -> None: ... def load_verify_locations( self, cafile: StrOrBytesPath | None = None, @@ -427,7 +407,12 @@ class SSLContext: def set_default_verify_paths(self) -> None: ... def set_ciphers(self, cipherlist: str, /) -> None: ... def set_alpn_protocols(self, alpn_protocols: Iterable[str]) -> None: ... - def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ... + if sys.version_info >= (3, 10): + @deprecated("Deprecated since Python 3.10. Use ALPN instead.") + def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ... + else: + def set_npn_protocols(self, npn_protocols: Iterable[str]) -> None: ... + def set_servername_callback(self, server_name_callback: _SrvnmeCbType | None) -> None: ... def load_dh_params(self, path: str, /) -> None: ... def set_ecdh_curve(self, name: str, /) -> None: ... @@ -448,7 +433,44 @@ class SSLContext: server_hostname: str | bytes | None = None, session: SSLSession | None = None, ) -> SSLObject: ... - def session_stats(self) -> dict[str, int]: ... + +def create_default_context( + purpose: Purpose = Purpose.SERVER_AUTH, + *, + cafile: StrOrBytesPath | None = None, + capath: StrOrBytesPath | None = None, + cadata: str | ReadableBuffer | None = None, +) -> SSLContext: ... + +if sys.version_info >= (3, 10): + def _create_unverified_context( + protocol: int | None = None, + *, + cert_reqs: int = ..., + check_hostname: bool = False, + purpose: Purpose = Purpose.SERVER_AUTH, + certfile: StrOrBytesPath | None = None, + keyfile: StrOrBytesPath | None = None, + cafile: StrOrBytesPath | None = None, + capath: StrOrBytesPath | None = None, + cadata: str | ReadableBuffer | None = None, + ) -> SSLContext: ... + +else: + def _create_unverified_context( + protocol: int = ..., + *, + cert_reqs: int = ..., + check_hostname: bool = False, + purpose: Purpose = Purpose.SERVER_AUTH, + certfile: StrOrBytesPath | None = None, + keyfile: StrOrBytesPath | None = None, + cafile: StrOrBytesPath | None = None, + capath: StrOrBytesPath | None = None, + cadata: str | ReadableBuffer | None = None, + ) -> SSLContext: ... + +_create_default_https_context = create_default_context class SSLObject: context: SSLContext @@ -469,7 +491,12 @@ class SSLObject: @overload def getpeercert(self, binary_form: bool) -> _PeerCertRetType: ... def selected_alpn_protocol(self) -> str | None: ... - def selected_npn_protocol(self) -> str | None: ... + if sys.version_info >= (3, 10): + @deprecated("Deprecated since Python 3.10. Use ALPN instead.") + def selected_npn_protocol(self) -> str | None: ... + else: + def selected_npn_protocol(self) -> str | None: ... + def cipher(self) -> tuple[str, str, int] | None: ... def shared_ciphers(self) -> list[tuple[str, str, int]] | None: ... def compression(self) -> str | None: ... @@ -483,28 +510,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 @@ -516,22 +521,20 @@ class SSLErrorNumber(enum.IntEnum): SSL_ERROR_WANT_X509_LOOKUP = 4 SSL_ERROR_ZERO_RETURN = 6 -SSL_ERROR_EOF: SSLErrorNumber # undocumented -SSL_ERROR_INVALID_ERROR_CODE: SSLErrorNumber # undocumented -SSL_ERROR_SSL: SSLErrorNumber # undocumented -SSL_ERROR_SYSCALL: SSLErrorNumber # undocumented -SSL_ERROR_WANT_CONNECT: SSLErrorNumber # undocumented -SSL_ERROR_WANT_READ: SSLErrorNumber # undocumented -SSL_ERROR_WANT_WRITE: SSLErrorNumber # undocumented -SSL_ERROR_WANT_X509_LOOKUP: SSLErrorNumber # undocumented -SSL_ERROR_ZERO_RETURN: SSLErrorNumber # undocumented +SSL_ERROR_EOF: Final = SSLErrorNumber.SSL_ERROR_EOF # undocumented +SSL_ERROR_INVALID_ERROR_CODE: Final = SSLErrorNumber.SSL_ERROR_INVALID_ERROR_CODE # undocumented +SSL_ERROR_SSL: Final = SSLErrorNumber.SSL_ERROR_SSL # undocumented +SSL_ERROR_SYSCALL: Final = SSLErrorNumber.SSL_ERROR_SYSCALL # undocumented +SSL_ERROR_WANT_CONNECT: Final = SSLErrorNumber.SSL_ERROR_WANT_CONNECT # undocumented +SSL_ERROR_WANT_READ: Final = SSLErrorNumber.SSL_ERROR_WANT_READ # undocumented +SSL_ERROR_WANT_WRITE: Final = SSLErrorNumber.SSL_ERROR_WANT_WRITE # undocumented +SSL_ERROR_WANT_X509_LOOKUP: Final = SSLErrorNumber.SSL_ERROR_WANT_X509_LOOKUP # undocumented +SSL_ERROR_ZERO_RETURN: Final = SSLErrorNumber.SSL_ERROR_ZERO_RETURN # undocumented def get_protocol_name(protocol_code: int) -> str: ... -if sys.version_info < (3, 9): - AF_INET: int -PEM_FOOTER: str -PEM_HEADER: str -SOCK_STREAM: int -SOL_SOCKET: int -SO_TYPE: int +PEM_FOOTER: Final[str] +PEM_HEADER: Final[str] +SOCK_STREAM: Final = socket.SOCK_STREAM +SOL_SOCKET: Final = socket.SOL_SOCKET +SO_TYPE: Final = socket.SO_TYPE diff --git a/mypy/typeshed/stdlib/stat.pyi b/mypy/typeshed/stdlib/stat.pyi index face28ab0cbb6..6c26080e06653 100644 --- a/mypy/typeshed/stdlib/stat.pyi +++ b/mypy/typeshed/stdlib/stat.pyi @@ -1,7 +1,114 @@ import sys -from _stat import * +from _stat import ( + S_ENFMT as S_ENFMT, + S_IEXEC as S_IEXEC, + S_IFBLK as S_IFBLK, + S_IFCHR as S_IFCHR, + S_IFDIR as S_IFDIR, + S_IFDOOR as S_IFDOOR, + S_IFIFO as S_IFIFO, + S_IFLNK as S_IFLNK, + S_IFMT as S_IFMT, + S_IFPORT as S_IFPORT, + S_IFREG as S_IFREG, + S_IFSOCK as S_IFSOCK, + S_IFWHT as S_IFWHT, + S_IMODE as S_IMODE, + S_IREAD as S_IREAD, + S_IRGRP as S_IRGRP, + S_IROTH as S_IROTH, + S_IRUSR as S_IRUSR, + S_IRWXG as S_IRWXG, + S_IRWXO as S_IRWXO, + S_IRWXU as S_IRWXU, + S_ISBLK as S_ISBLK, + S_ISCHR as S_ISCHR, + S_ISDIR as S_ISDIR, + S_ISDOOR as S_ISDOOR, + S_ISFIFO as S_ISFIFO, + S_ISGID as S_ISGID, + S_ISLNK as S_ISLNK, + S_ISPORT as S_ISPORT, + S_ISREG as S_ISREG, + S_ISSOCK as S_ISSOCK, + S_ISUID as S_ISUID, + S_ISVTX as S_ISVTX, + S_ISWHT as S_ISWHT, + S_IWGRP as S_IWGRP, + S_IWOTH as S_IWOTH, + S_IWRITE as S_IWRITE, + S_IWUSR as S_IWUSR, + S_IXGRP as S_IXGRP, + S_IXOTH as S_IXOTH, + S_IXUSR as S_IXUSR, + SF_APPEND as SF_APPEND, + SF_ARCHIVED as SF_ARCHIVED, + SF_IMMUTABLE as SF_IMMUTABLE, + SF_NOUNLINK as SF_NOUNLINK, + SF_SNAPSHOT as SF_SNAPSHOT, + ST_ATIME as ST_ATIME, + ST_CTIME as ST_CTIME, + ST_DEV as ST_DEV, + ST_GID as ST_GID, + ST_INO as ST_INO, + ST_MODE as ST_MODE, + ST_MTIME as ST_MTIME, + ST_NLINK as ST_NLINK, + ST_SIZE as ST_SIZE, + ST_UID as ST_UID, + UF_APPEND as UF_APPEND, + UF_COMPRESSED as UF_COMPRESSED, + UF_HIDDEN as UF_HIDDEN, + UF_IMMUTABLE as UF_IMMUTABLE, + UF_NODUMP as UF_NODUMP, + UF_NOUNLINK as UF_NOUNLINK, + UF_OPAQUE as UF_OPAQUE, + filemode as filemode, +) from typing import Final +if sys.platform == "win32": + from _stat import ( + IO_REPARSE_TAG_APPEXECLINK as IO_REPARSE_TAG_APPEXECLINK, + IO_REPARSE_TAG_MOUNT_POINT as IO_REPARSE_TAG_MOUNT_POINT, + IO_REPARSE_TAG_SYMLINK as IO_REPARSE_TAG_SYMLINK, + ) + +if sys.version_info >= (3, 13): + from _stat import ( + SF_DATALESS as SF_DATALESS, + SF_FIRMLINK as SF_FIRMLINK, + SF_SETTABLE as SF_SETTABLE, + UF_DATAVAULT as UF_DATAVAULT, + UF_SETTABLE as UF_SETTABLE, + UF_TRACKED as UF_TRACKED, + ) + + if sys.platform == "darwin": + from _stat import SF_SUPPORTED as SF_SUPPORTED, SF_SYNTHETIC as SF_SYNTHETIC + +# _stat.c defines FILE_ATTRIBUTE_* constants conditionally, +# making them available only at runtime on Windows. +# stat.py unconditionally redefines the same FILE_ATTRIBUTE_* constants +# on all platforms. +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): # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 SF_RESTRICTED: Final = 0x00080000 diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index c8ecbbceab1ab..ba9e5f1b6b71f 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -3,7 +3,7 @@ from _typeshed import SupportsRichComparisonT from collections.abc import Callable, Hashable, Iterable, Sequence from decimal import Decimal from fractions import Fraction -from typing import Any, Literal, NamedTuple, SupportsFloat, TypeVar +from typing import Literal, NamedTuple, SupportsFloat, SupportsIndex, TypeVar from typing_extensions import Self, TypeAlias __all__ = [ @@ -38,6 +38,9 @@ _NumberT = TypeVar("_NumberT", float, Decimal, Fraction) # Used in mode, multimode _HashableT = TypeVar("_HashableT", bound=Hashable) +# Used in NormalDist.samples and kde_random +_Seed: TypeAlias = int | float | str | bytes | bytearray # noqa: Y041 + class StatisticsError(ValueError): ... if sys.version_info >= (3, 11): @@ -76,6 +79,7 @@ def stdev(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: . def variance(data: Iterable[_NumberT], xbar: _NumberT | None = None) -> _NumberT: ... class NormalDist: + __slots__ = {"_mu": "Arithmetic mean of a normal distribution", "_sigma": "Standard deviation of a normal distribution"} def __init__(self, mu: float = 0.0, sigma: float = 1.0) -> None: ... @property def mean(self) -> float: ... @@ -89,24 +93,22 @@ class NormalDist: def variance(self) -> float: ... @classmethod def from_samples(cls, data: Iterable[SupportsFloat]) -> Self: ... - def samples(self, n: int, *, seed: Any | None = None) -> list[float]: ... + def samples(self, n: SupportsIndex, *, seed: _Seed | None = None) -> list[float]: ... def pdf(self, x: float) -> float: ... def cdf(self, x: float) -> float: ... def inv_cdf(self, p: float) -> float: ... def overlap(self, other: NormalDist) -> float: ... def quantiles(self, n: int = 4) -> list[float]: ... - if sys.version_info >= (3, 9): - def zscore(self, x: float) -> float: ... - - def __eq__(self, x2: object) -> bool: ... - def __add__(self, x2: float | NormalDist) -> NormalDist: ... - def __sub__(self, x2: float | NormalDist) -> NormalDist: ... - def __mul__(self, x2: float) -> NormalDist: ... - def __truediv__(self, x2: float) -> NormalDist: ... - def __pos__(self) -> NormalDist: ... - def __neg__(self) -> NormalDist: ... + def zscore(self, x: float) -> float: ... + def __eq__(x1, x2: object) -> bool: ... + def __add__(x1, x2: float | NormalDist) -> NormalDist: ... + def __sub__(x1, x2: float | NormalDist) -> NormalDist: ... + def __mul__(x1, x2: float) -> NormalDist: ... + def __truediv__(x1, x2: float) -> NormalDist: ... + def __pos__(x1) -> NormalDist: ... + def __neg__(x1) -> NormalDist: ... __radd__ = __add__ - def __rsub__(self, x2: float | NormalDist) -> NormalDist: ... + def __rsub__(x1, x2: float | NormalDist) -> NormalDist: ... __rmul__ = __mul__ def __hash__(self) -> int: ... @@ -153,9 +155,5 @@ if sys.version_info >= (3, 13): data: Sequence[float], h: float, kernel: _Kernel = "normal", *, cumulative: bool = False ) -> Callable[[float], float]: ... def kde_random( - data: Sequence[float], - h: float, - kernel: _Kernel = "normal", - *, - seed: int | float | str | bytes | bytearray | None = None, # noqa: Y041 + data: Sequence[float], h: float, kernel: _Kernel = "normal", *, seed: _Seed | None = None ) -> Callable[[], float]: ... diff --git a/mypy/typeshed/stdlib/string.pyi b/mypy/typeshed/stdlib/string/__init__.pyi similarity index 78% rename from mypy/typeshed/stdlib/string.pyi rename to mypy/typeshed/stdlib/string/__init__.pyi index 35a76e9c8628d..c8b32a98e26d7 100644 --- a/mypy/typeshed/stdlib/string.pyi +++ b/mypy/typeshed/stdlib/string/__init__.pyi @@ -2,8 +2,8 @@ import sys from _typeshed import StrOrLiteralStr from collections.abc import Iterable, Mapping, Sequence from re import Pattern, RegexFlag -from typing import Any, ClassVar, overload -from typing_extensions import LiteralString, TypeAlias +from typing import Any, ClassVar, Final, overload +from typing_extensions import LiteralString __all__ = [ "ascii_letters", @@ -20,31 +20,27 @@ __all__ = [ "Template", ] -ascii_letters: LiteralString -ascii_lowercase: LiteralString -ascii_uppercase: LiteralString -digits: LiteralString -hexdigits: LiteralString -octdigits: LiteralString -punctuation: LiteralString -printable: LiteralString -whitespace: LiteralString +whitespace: Final = " \t\n\r\v\f" +ascii_lowercase: Final = "abcdefghijklmnopqrstuvwxyz" +ascii_uppercase: Final = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +ascii_letters: Final[LiteralString] # string too long +digits: Final = "0123456789" +hexdigits: Final = "0123456789abcdefABCDEF" +octdigits: Final = "01234567" +punctuation: Final = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" +printable: Final[LiteralString] # string too long def capwords(s: StrOrLiteralStr, sep: StrOrLiteralStr | None = None) -> StrOrLiteralStr: ... -if sys.version_info >= (3, 9): - _TemplateMetaclass: TypeAlias = type -else: - class _TemplateMetaclass(type): - pattern: ClassVar[str] - def __init__(cls, name: str, bases: tuple[type, ...], dct: dict[str, Any]) -> None: ... - -class Template(metaclass=_TemplateMetaclass): +class Template: template: str delimiter: ClassVar[str] idpattern: ClassVar[str] braceidpattern: ClassVar[str | None] - flags: ClassVar[RegexFlag] + if sys.version_info >= (3, 14): + flags: ClassVar[RegexFlag | None] + else: + flags: ClassVar[RegexFlag] pattern: ClassVar[Pattern[str]] def __init__(self, template: str) -> None: ... def substitute(self, mapping: Mapping[str, object] = {}, /, **kwds: object) -> str: ... diff --git a/mypy/typeshed/stdlib/string/templatelib.pyi b/mypy/typeshed/stdlib/string/templatelib.pyi new file mode 100644 index 0000000000000..9906d31c63915 --- /dev/null +++ b/mypy/typeshed/stdlib/string/templatelib.pyi @@ -0,0 +1,36 @@ +from collections.abc import Iterator +from types import GenericAlias +from typing import Any, Literal, TypeVar, final, overload + +_T = TypeVar("_T") + +@final +class Template: # TODO: consider making `Template` generic on `TypeVarTuple` + strings: tuple[str, ...] + interpolations: tuple[Interpolation, ...] + + def __new__(cls, *args: str | Interpolation) -> Template: ... + def __iter__(self) -> Iterator[str | Interpolation]: ... + def __add__(self, other: Template, /) -> Template: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + @property + def values(self) -> tuple[Any, ...]: ... # Tuple of interpolation values, which can have any type + +@final +class Interpolation: + value: Any # TODO: consider making `Interpolation` generic in runtime + expression: str + conversion: Literal["a", "r", "s"] | None + format_spec: str + + __match_args__ = ("value", "expression", "conversion", "format_spec") + + def __new__( + cls, value: Any, expression: str = "", conversion: Literal["a", "r", "s"] | None = None, format_spec: str = "" + ) -> Interpolation: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +@overload +def convert(obj: _T, /, conversion: None) -> _T: ... +@overload +def convert(obj: object, /, conversion: Literal["r", "s", "a"]) -> str: ... diff --git a/mypy/typeshed/stdlib/stringprep.pyi b/mypy/typeshed/stdlib/stringprep.pyi index fc28c027ca9b1..d67955e499c85 100644 --- a/mypy/typeshed/stdlib/stringprep.pyi +++ b/mypy/typeshed/stdlib/stringprep.pyi @@ -1,10 +1,12 @@ -b1_set: set[int] -b3_exceptions: dict[int, str] -c22_specials: set[int] -c6_set: set[int] -c7_set: set[int] -c8_set: set[int] -c9_set: set[int] +from typing import Final + +b1_set: Final[set[int]] +b3_exceptions: Final[dict[int, str]] +c22_specials: Final[set[int]] +c6_set: Final[set[int]] +c7_set: Final[set[int]] +c8_set: Final[set[int]] +c9_set: Final[set[int]] def in_table_a1(code: str) -> bool: ... def in_table_b1(code: str) -> bool: ... 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 703a5012012cf..e1e25bcb50cbe 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -1,13 +1,10 @@ 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 types import GenericAlias, TracebackType from typing import IO, Any, AnyStr, Final, Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = [ "Popen", "PIPE", @@ -87,8 +84,7 @@ class CompletedProcess(Generic[_T]): stderr: _T def __init__(self, args: _CMD, returncode: int, stdout: _T | None = None, stderr: _T | None = None) -> None: ... def check_returncode(self) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 11): # 3.11 adds "process_group" argument @@ -110,7 +106,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -144,7 +140,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -178,7 +174,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -213,7 +209,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), # where the *real* keyword only args start capture_output: bool = False, check: bool = False, @@ -247,7 +243,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -281,7 +277,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -318,7 +314,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -351,7 +347,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -384,7 +380,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -418,7 +414,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), # where the *real* keyword only args start capture_output: bool = False, check: bool = False, @@ -451,7 +447,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -484,7 +480,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -500,7 +496,7 @@ elif sys.version_info >= (3, 10): pipesize: int = -1, ) -> CompletedProcess[Any]: ... -elif sys.version_info >= (3, 9): +else: # 3.9 adds arguments "user", "group", "extra_groups" and "umask" @overload def run( @@ -520,7 +516,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -552,7 +548,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -584,7 +580,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -617,7 +613,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), # where the *real* keyword only args start capture_output: bool = False, check: bool = False, @@ -649,7 +645,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -681,7 +677,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, capture_output: bool = False, check: bool = False, @@ -696,177 +692,6 @@ elif sys.version_info >= (3, 9): umask: int = -1, ) -> CompletedProcess[Any]: ... -else: - @overload - def run( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - capture_output: bool = False, - check: bool = False, - encoding: str | None = None, - errors: str | None = None, - input: str | None = None, - text: Literal[True], - timeout: float | None = None, - ) -> CompletedProcess[str]: ... - @overload - def run( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - capture_output: bool = False, - check: bool = False, - encoding: str, - errors: str | None = None, - input: str | None = None, - text: bool | None = None, - timeout: float | None = None, - ) -> CompletedProcess[str]: ... - @overload - def run( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - capture_output: bool = False, - check: bool = False, - encoding: str | None = None, - errors: str, - input: str | None = None, - text: bool | None = None, - timeout: float | None = None, - ) -> CompletedProcess[str]: ... - @overload - def run( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - *, - universal_newlines: Literal[True], - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - # where the *real* keyword only args start - capture_output: bool = False, - check: bool = False, - encoding: str | None = None, - errors: str | None = None, - input: str | None = None, - text: bool | None = None, - timeout: float | None = None, - ) -> CompletedProcess[str]: ... - @overload - def run( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: Literal[False] | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - capture_output: bool = False, - check: bool = False, - encoding: None = None, - errors: None = None, - input: ReadableBuffer | None = None, - text: Literal[False] | None = None, - timeout: float | None = None, - ) -> CompletedProcess[bytes]: ... - @overload - def run( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - capture_output: bool = False, - check: bool = False, - encoding: str | None = None, - errors: str | None = None, - input: _InputString | None = None, - text: bool | None = None, - timeout: float | None = None, - ) -> CompletedProcess[Any]: ... - # Same args as Popen.__init__ if sys.version_info >= (3, 11): # 3.11 adds "process_group" argument @@ -887,7 +712,7 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, encoding: str | None = None, timeout: float | None = None, @@ -919,7 +744,7 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, encoding: str | None = None, timeout: float | None = None, @@ -931,8 +756,7 @@ elif sys.version_info >= (3, 10): pipesize: int = -1, ) -> int: ... -elif sys.version_info >= (3, 9): - # 3.9 adds arguments "user", "group", "extra_groups" and "umask" +else: def call( args: _CMD, bufsize: int = -1, @@ -950,7 +774,7 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, encoding: str | None = None, timeout: float | None = None, @@ -961,31 +785,6 @@ elif sys.version_info >= (3, 9): umask: int = -1, ) -> int: ... -else: - def call( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - encoding: str | None = None, - timeout: float | None = None, - text: bool | None = None, - ) -> int: ... - # Same args as Popen.__init__ if sys.version_info >= (3, 11): # 3.11 adds "process_group" argument @@ -1006,8 +805,8 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., - timeout: float | None = ..., + pass_fds: Collection[int] = (), + timeout: float | None = None, *, encoding: str | None = None, text: bool | None = None, @@ -1038,8 +837,8 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., - timeout: float | None = ..., + pass_fds: Collection[int] = (), + timeout: float | None = None, *, encoding: str | None = None, text: bool | None = None, @@ -1050,8 +849,7 @@ elif sys.version_info >= (3, 10): pipesize: int = -1, ) -> int: ... -elif sys.version_info >= (3, 9): - # 3.9 adds arguments "user", "group", "extra_groups" and "umask" +else: def check_call( args: _CMD, bufsize: int = -1, @@ -1069,8 +867,8 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., - timeout: float | None = ..., + pass_fds: Collection[int] = (), + timeout: float | None = None, *, encoding: str | None = None, text: bool | None = None, @@ -1080,31 +878,6 @@ elif sys.version_info >= (3, 9): umask: int = -1, ) -> int: ... -else: - def check_call( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stdout: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - timeout: float | None = ..., - *, - encoding: str | None = None, - text: bool | None = None, - ) -> int: ... - if sys.version_info >= (3, 11): # 3.11 adds "process_group" argument @overload @@ -1124,10 +897,10 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: Literal[True], @@ -1155,10 +928,10 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str, errors: str | None = None, text: bool | None = None, @@ -1186,10 +959,10 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str, text: bool | None = None, @@ -1218,10 +991,10 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), # where the real keyword only ones start timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: bool | None = None, @@ -1249,10 +1022,10 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: None = None, errors: None = None, text: Literal[False] | None = None, @@ -1280,10 +1053,10 @@ if sys.version_info >= (3, 11): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: bool | None = None, @@ -1314,10 +1087,10 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: Literal[True], @@ -1344,10 +1117,10 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str, errors: str | None = None, text: bool | None = None, @@ -1374,10 +1147,10 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str, text: bool | None = None, @@ -1405,10 +1178,10 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), # where the real keyword only ones start timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: bool | None = None, @@ -1435,10 +1208,10 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: None = None, errors: None = None, text: Literal[False] | None = None, @@ -1465,10 +1238,10 @@ elif sys.version_info >= (3, 10): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: bool | None = None, @@ -1479,8 +1252,7 @@ elif sys.version_info >= (3, 10): pipesize: int = -1, ) -> Any: ... # morally: -> str | bytes -elif sys.version_info >= (3, 9): - # 3.9 adds arguments "user", "group", "extra_groups" and "umask" +else: @overload def check_output( args: _CMD, @@ -1498,10 +1270,10 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: Literal[True], @@ -1527,10 +1299,10 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str, errors: str | None = None, text: bool | None = None, @@ -1556,10 +1328,10 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str, text: bool | None = None, @@ -1586,10 +1358,10 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), # where the real keyword only ones start timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: bool | None = None, @@ -1615,10 +1387,10 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: None = None, errors: None = None, text: Literal[False] | None = None, @@ -1644,10 +1416,10 @@ elif sys.version_info >= (3, 9): creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, - pass_fds: Collection[int] = ..., + pass_fds: Collection[int] = (), *, timeout: float | None = None, - input: _InputString | None = ..., + input: _InputString | None = None, encoding: str | None = None, errors: str | None = None, text: bool | None = None, @@ -1657,159 +1429,6 @@ elif sys.version_info >= (3, 9): umask: int = -1, ) -> Any: ... # morally: -> str | bytes -else: - @overload - def check_output( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - timeout: float | None = None, - input: _InputString | None = ..., - encoding: str | None = None, - errors: str | None = None, - text: Literal[True], - ) -> str: ... - @overload - def check_output( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - timeout: float | None = None, - input: _InputString | None = ..., - encoding: str, - errors: str | None = None, - text: bool | None = None, - ) -> str: ... - @overload - def check_output( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - timeout: float | None = None, - input: _InputString | None = ..., - encoding: str | None = None, - errors: str, - text: bool | None = None, - ) -> str: ... - @overload - def check_output( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - *, - universal_newlines: Literal[True], - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - # where the real keyword only ones start - timeout: float | None = None, - input: _InputString | None = ..., - encoding: str | None = None, - errors: str | None = None, - text: bool | None = None, - ) -> str: ... - @overload - def check_output( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: Literal[False] | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - timeout: float | None = None, - input: _InputString | None = ..., - encoding: None = None, - errors: None = None, - text: Literal[False] | None = None, - ) -> bytes: ... - @overload - def check_output( - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE = None, - stderr: _FILE = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = ..., - *, - timeout: float | None = None, - input: _InputString | None = ..., - encoding: str | None = None, - errors: str | None = None, - text: bool | None = None, - ) -> Any: ... # morally: -> str | bytes - PIPE: Final[int] STDOUT: Final[int] DEVNULL: Final[int] @@ -1848,7 +1467,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): @@ -2223,8 +1842,7 @@ class Popen(Generic[AnyStr]): umask: int = -1, pipesize: int = -1, ) -> None: ... - elif sys.version_info >= (3, 9): - # user, group, extra_groups, umask were added in 3.9 + else: @overload def __init__( self: Popen[str], @@ -2400,163 +2018,11 @@ class Popen(Generic[AnyStr]): extra_groups: Iterable[str | int] | None = None, umask: int = -1, ) -> None: ... - else: - @overload - def __init__( - self: Popen[str], - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE | None = None, - stdout: _FILE | None = None, - stderr: _FILE | None = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any | None = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = (), - *, - text: bool | None = None, - encoding: str, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self: Popen[str], - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE | None = None, - stdout: _FILE | None = None, - stderr: _FILE | None = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any | None = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = (), - *, - text: bool | None = None, - encoding: str | None = None, - errors: str, - ) -> None: ... - @overload - def __init__( - self: Popen[str], - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE | None = None, - stdout: _FILE | None = None, - stderr: _FILE | None = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - *, - universal_newlines: Literal[True], - startupinfo: Any | None = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = (), - # where the *real* keyword only args start - text: bool | None = None, - encoding: str | None = None, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self: Popen[str], - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE | None = None, - stdout: _FILE | None = None, - stderr: _FILE | None = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any | None = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = (), - *, - text: Literal[True], - encoding: str | None = None, - errors: str | None = None, - ) -> None: ... - @overload - def __init__( - self: Popen[bytes], - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE | None = None, - stdout: _FILE | None = None, - stderr: _FILE | None = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: Literal[False] | None = None, - startupinfo: Any | None = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = (), - *, - text: Literal[False] | None = None, - encoding: None = None, - errors: None = None, - ) -> None: ... - @overload - def __init__( - self: Popen[Any], - args: _CMD, - bufsize: int = -1, - executable: StrOrBytesPath | None = None, - stdin: _FILE | None = None, - stdout: _FILE | None = None, - stderr: _FILE | None = None, - preexec_fn: Callable[[], Any] | None = None, - close_fds: bool = True, - shell: bool = False, - cwd: StrOrBytesPath | None = None, - env: _ENV | None = None, - universal_newlines: bool | None = None, - startupinfo: Any | None = None, - creationflags: int = 0, - restore_signals: bool = True, - start_new_session: bool = False, - pass_fds: Collection[int] = (), - *, - text: bool | None = None, - encoding: str | None = None, - errors: str | None = None, - ) -> None: ... def poll(self) -> int | None: ... def wait(self, timeout: float | None = None) -> int: ... # morally the members of the returned tuple should be optional - # TODO this should allow ReadableBuffer for Popen[bytes], but adding + # TODO: this should allow ReadableBuffer for Popen[bytes], but adding # overloads for that runs into a mypy bug (python/mypy#14070). def communicate(self, input: AnyStr | None = None, timeout: float | None = None) -> tuple[AnyStr, AnyStr]: ... def send_signal(self, sig: int) -> None: ... @@ -2567,8 +2033,7 @@ class Popen(Generic[AnyStr]): self, exc_type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... def __del__(self) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # The result really is always a str. if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/sunau.pyi b/mypy/typeshed/stdlib/sunau.pyi index 9b051e82b64bc..f83a0a4c520e7 100644 --- a/mypy/typeshed/stdlib/sunau.pyi +++ b/mypy/typeshed/stdlib/sunau.pyi @@ -1,26 +1,25 @@ -import sys from _typeshed import Unused -from typing import IO, Any, Literal, NamedTuple, NoReturn, overload +from typing import IO, Any, Final, Literal, NamedTuple, NoReturn, overload from typing_extensions import Self, TypeAlias _File: TypeAlias = str | IO[bytes] class Error(Exception): ... -AUDIO_FILE_MAGIC: int -AUDIO_FILE_ENCODING_MULAW_8: int -AUDIO_FILE_ENCODING_LINEAR_8: int -AUDIO_FILE_ENCODING_LINEAR_16: int -AUDIO_FILE_ENCODING_LINEAR_24: int -AUDIO_FILE_ENCODING_LINEAR_32: int -AUDIO_FILE_ENCODING_FLOAT: int -AUDIO_FILE_ENCODING_DOUBLE: int -AUDIO_FILE_ENCODING_ADPCM_G721: int -AUDIO_FILE_ENCODING_ADPCM_G722: int -AUDIO_FILE_ENCODING_ADPCM_G723_3: int -AUDIO_FILE_ENCODING_ADPCM_G723_5: int -AUDIO_FILE_ENCODING_ALAW_8: int -AUDIO_UNKNOWN_SIZE: int +AUDIO_FILE_MAGIC: Final = 0x2E736E64 +AUDIO_FILE_ENCODING_MULAW_8: Final = 1 +AUDIO_FILE_ENCODING_LINEAR_8: Final = 2 +AUDIO_FILE_ENCODING_LINEAR_16: Final = 3 +AUDIO_FILE_ENCODING_LINEAR_24: Final = 4 +AUDIO_FILE_ENCODING_LINEAR_32: Final = 5 +AUDIO_FILE_ENCODING_FLOAT: Final = 6 +AUDIO_FILE_ENCODING_DOUBLE: Final = 7 +AUDIO_FILE_ENCODING_ADPCM_G721: Final = 23 +AUDIO_FILE_ENCODING_ADPCM_G722: Final = 24 +AUDIO_FILE_ENCODING_ADPCM_G723_3: Final = 25 +AUDIO_FILE_ENCODING_ADPCM_G723_5: Final = 26 +AUDIO_FILE_ENCODING_ALAW_8: Final = 27 +AUDIO_UNKNOWN_SIZE: Final = 0xFFFFFFFF class _sunau_params(NamedTuple): nchannels: int @@ -81,6 +80,3 @@ def open(f: _File, mode: Literal["r", "rb"]) -> Au_read: ... def open(f: _File, mode: Literal["w", "wb"]) -> Au_write: ... @overload def open(f: _File, mode: str | None = None) -> Any: ... - -if sys.version_info < (3, 9): - openfp = open diff --git a/mypy/typeshed/stdlib/symbol.pyi b/mypy/typeshed/stdlib/symbol.pyi index 48ae3567a1a56..5344ce504c6c7 100644 --- a/mypy/typeshed/stdlib/symbol.pyi +++ b/mypy/typeshed/stdlib/symbol.pyi @@ -1,93 +1,95 @@ -single_input: int -file_input: int -eval_input: int -decorator: int -decorators: int -decorated: int -async_funcdef: int -funcdef: int -parameters: int -typedargslist: int -tfpdef: int -varargslist: int -vfpdef: int -stmt: int -simple_stmt: int -small_stmt: int -expr_stmt: int -annassign: int -testlist_star_expr: int -augassign: int -del_stmt: int -pass_stmt: int -flow_stmt: int -break_stmt: int -continue_stmt: int -return_stmt: int -yield_stmt: int -raise_stmt: int -import_stmt: int -import_name: int -import_from: int -import_as_name: int -dotted_as_name: int -import_as_names: int -dotted_as_names: int -dotted_name: int -global_stmt: int -nonlocal_stmt: int -assert_stmt: int -compound_stmt: int -async_stmt: int -if_stmt: int -while_stmt: int -for_stmt: int -try_stmt: int -with_stmt: int -with_item: int -except_clause: int -suite: int -test: int -test_nocond: int -lambdef: int -lambdef_nocond: int -or_test: int -and_test: int -not_test: int -comparison: int -comp_op: int -star_expr: int -expr: int -xor_expr: int -and_expr: int -shift_expr: int -arith_expr: int -term: int -factor: int -power: int -atom_expr: int -atom: int -testlist_comp: int -trailer: int -subscriptlist: int -subscript: int -sliceop: int -exprlist: int -testlist: int -dictorsetmaker: int -classdef: int -arglist: int -argument: int -comp_iter: int -comp_for: int -comp_if: int -encoding_decl: int -yield_expr: int -yield_arg: int -sync_comp_for: int -func_body_suite: int -func_type: int -func_type_input: int -namedexpr_test: int -typelist: int -sym_name: dict[int, str] +from typing import Final + +single_input: Final[int] +file_input: Final[int] +eval_input: Final[int] +decorator: Final[int] +decorators: Final[int] +decorated: Final[int] +async_funcdef: Final[int] +funcdef: Final[int] +parameters: Final[int] +typedargslist: Final[int] +tfpdef: Final[int] +varargslist: Final[int] +vfpdef: Final[int] +stmt: Final[int] +simple_stmt: Final[int] +small_stmt: Final[int] +expr_stmt: Final[int] +annassign: Final[int] +testlist_star_expr: Final[int] +augassign: Final[int] +del_stmt: Final[int] +pass_stmt: Final[int] +flow_stmt: Final[int] +break_stmt: Final[int] +continue_stmt: Final[int] +return_stmt: Final[int] +yield_stmt: Final[int] +raise_stmt: Final[int] +import_stmt: Final[int] +import_name: Final[int] +import_from: Final[int] +import_as_name: Final[int] +dotted_as_name: Final[int] +import_as_names: Final[int] +dotted_as_names: Final[int] +dotted_name: Final[int] +global_stmt: Final[int] +nonlocal_stmt: Final[int] +assert_stmt: Final[int] +compound_stmt: Final[int] +async_stmt: Final[int] +if_stmt: Final[int] +while_stmt: Final[int] +for_stmt: Final[int] +try_stmt: Final[int] +with_stmt: Final[int] +with_item: Final[int] +except_clause: Final[int] +suite: Final[int] +test: Final[int] +test_nocond: Final[int] +lambdef: Final[int] +lambdef_nocond: Final[int] +or_test: Final[int] +and_test: Final[int] +not_test: Final[int] +comparison: Final[int] +comp_op: Final[int] +star_expr: Final[int] +expr: Final[int] +xor_expr: Final[int] +and_expr: Final[int] +shift_expr: Final[int] +arith_expr: Final[int] +term: Final[int] +factor: Final[int] +power: Final[int] +atom_expr: Final[int] +atom: Final[int] +testlist_comp: Final[int] +trailer: Final[int] +subscriptlist: Final[int] +subscript: Final[int] +sliceop: Final[int] +exprlist: Final[int] +testlist: Final[int] +dictorsetmaker: Final[int] +classdef: Final[int] +arglist: Final[int] +argument: Final[int] +comp_iter: Final[int] +comp_for: Final[int] +comp_if: Final[int] +encoding_decl: Final[int] +yield_expr: Final[int] +yield_arg: Final[int] +sync_comp_for: Final[int] +func_body_suite: Final[int] +func_type: Final[int] +func_type_input: Final[int] +namedexpr_test: Final[int] +typelist: Final[int] +sym_name: Final[dict[int, str]] diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi index ee0a1eb2f1cb5..a727b878688ed 100644 --- a/mypy/typeshed/stdlib/symtable.pyi +++ b/mypy/typeshed/stdlib/symtable.pyi @@ -36,9 +36,6 @@ class SymbolTable: def is_optimized(self) -> bool: ... def is_nested(self) -> bool: ... def has_children(self) -> bool: ... - if sys.version_info < (3, 9): - def has_exec(self) -> bool: ... - def get_identifiers(self) -> dict_keys[str, int]: ... def lookup(self, name: str) -> Symbol: ... def get_symbols(self) -> list[Symbol]: ... @@ -52,8 +49,10 @@ class Function(SymbolTable): def get_nonlocals(self) -> tuple[str, ...]: ... class Class(SymbolTable): - if sys.version_info < (3, 16): - @deprecated("deprecated in Python 3.14, will be removed in Python 3.16") + if sys.version_info >= (3, 14): + @deprecated("Deprecated since Python 3.14; will be removed in Python 3.16.") + def get_methods(self) -> tuple[str, ...]: ... + else: def get_methods(self) -> tuple[str, ...]: ... class Symbol: diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index d65ddfe3825d5..6abef85dfb7f1 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -1,18 +1,17 @@ import sys -from _typeshed import MaybeNone, OptExcInfo, ProfileFunction, TraceFunction, structseq +from _typeshed import MaybeNone, OptExcInfo, ProfileFunction, StrOrBytesPath, TraceFunction, structseq from _typeshed.importlib import MetaPathFinderProtocol, PathEntryFinderProtocol 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_extensions import TypeAlias +from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final, overload, type_check_only +from typing_extensions import LiteralString, TypeAlias, deprecated _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": @@ -46,9 +45,8 @@ if sys.version_info >= (3, 10): path: list[str] path_hooks: list[Callable[[str], PathEntryFinderProtocol]] path_importer_cache: dict[str, PathEntryFinderProtocol | None] -platform: str -if sys.version_info >= (3, 9): - platlibdir: str +platform: LiteralString +platlibdir: str prefix: str pycache_prefix: str | None ps1: object @@ -74,7 +72,7 @@ if sys.version_info >= (3, 10): __stdin__: Final[TextIOWrapper | None] # Contains the original value of stdin __stdout__: Final[TextIOWrapper | None] # Contains the original value of stdout __stderr__: Final[TextIOWrapper | None] # Contains the original value of stderr -tracebacklimit: int +tracebacklimit: int | None version: str api_version: int warnoptions: Any @@ -90,13 +88,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.9 is 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 +177,47 @@ 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: ... + if sys.version_info >= (3, 13): + @property + def gil(self) -> Literal[0, 1]: ... + if sys.version_info >= (3, 14): + @property + def thread_inherit_context(self) -> Literal[0, 1]: ... + @property + def context_aware_warnings(self) -> Literal[0, 1]: ... + # 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.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 +243,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 +271,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 +286,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 +305,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 +322,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 @@ -240,9 +343,27 @@ class _version_info(_UninstantiableStructseq, tuple[int, int, int, _ReleaseLevel version_info: _version_info def call_tracing(func: Callable[..., _T], args: Any, /) -> _T: ... -def _clear_type_cache() -> None: ... + +if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13. Use `_clear_internal_caches()` instead.") + def _clear_type_cache() -> None: ... + +else: + def _clear_type_cache() -> None: ... + def _current_frames() -> dict[int, FrameType]: ... def _getframe(depth: int = 0, /) -> FrameType: ... + +# documented -- see https://docs.python.org/3/library/sys.html#sys._current_exceptions +if sys.version_info >= (3, 12): + def _current_exceptions() -> dict[int, BaseException | None]: ... + +else: + def _current_exceptions() -> dict[int, OptExcInfo]: ... + +if sys.version_info >= (3, 12): + def _getframemodulename(depth: int = 0) -> str | None: ... + def _debugmallocstats() -> None: ... def __displayhook__(object: object, /) -> None: ... def __excepthook__(exctype: type[BaseException], value: BaseException, traceback: TracebackType | None, /) -> None: ... @@ -252,14 +373,18 @@ if sys.version_info >= (3, 11): def exception() -> BaseException | None: ... def exit(status: _ExitCode = None, /) -> NoReturn: ... + +if sys.platform == "android": # noqa: Y008 + def getandroidapilevel() -> int: ... + def getallocatedblocks() -> int: ... -def getdefaultencoding() -> str: ... +def getdefaultencoding() -> Literal["utf-8"]: ... if sys.platform != "win32": def getdlopenflags() -> int: ... -def getfilesystemencoding() -> str: ... -def getfilesystemencodeerrors() -> str: ... +def getfilesystemencoding() -> LiteralString: ... +def getfilesystemencodeerrors() -> LiteralString: ... def getrefcount(object: Any, /) -> int: ... def getrecursionlimit() -> int: ... def getsizeof(obj: object, default: int = ...) -> int: ... @@ -272,6 +397,7 @@ def settrace(function: TraceFunction | None, /) -> None: ... if sys.platform == "win32": # A tuple of length 5, even though it has more than 5 attributes. @final + @type_check_only class _WinVersion(_UninstantiableStructseq, tuple[int, int, int, int, str]): @property def major(self) -> int: ... @@ -296,7 +422,18 @@ if sys.platform == "win32": def getwindowsversion() -> _WinVersion: ... -def intern(string: str, /) -> str: ... +@overload +def intern(string: LiteralString, /) -> LiteralString: ... +@overload +def intern(string: str, /) -> str: ... # type: ignore[misc] + +__interactivehook__: Callable[[], object] + +if sys.version_info >= (3, 13): + def _is_gil_enabled() -> bool: ... + def _clear_internal_caches() -> None: ... + def _is_interned(string: str, /) -> bool: ... + def is_finalizing() -> bool: ... def breakpointhook(*args: Any, **kwargs: Any) -> Any: ... @@ -309,15 +446,8 @@ def setrecursionlimit(limit: int, /) -> None: ... def setswitchinterval(interval: float, /) -> None: ... def gettotalrefcount() -> int: ... # Debug builds only -if sys.version_info < (3, 9): - def getcheckinterval() -> int: ... # deprecated - def setcheckinterval(n: int, /) -> None: ... # deprecated - -if sys.version_info < (3, 9): - # An 11-tuple or None - 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 +463,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 @@ -344,12 +479,19 @@ def get_asyncgen_hooks() -> _asyncgen_hooks: ... def set_asyncgen_hooks(firstiter: _AsyncgenHook = ..., finalizer: _AsyncgenHook = ...) -> None: ... if sys.platform == "win32": - def _enablelegacywindowsfsencoding() -> None: ... + if sys.version_info >= (3, 13): + @deprecated( + "Deprecated since Python 3.13; will be removed in Python 3.16. " + "Use the `PYTHONLEGACYWINDOWSFSENCODING` environment variable instead." + ) + def _enablelegacywindowsfsencoding() -> None: ... + else: + def _enablelegacywindowsfsencoding() -> None: ... def get_coroutine_origin_tracking_depth() -> int: ... def set_coroutine_origin_tracking_depth(depth: int) -> None: ... -# The following two functions were added in 3.11.0, 3.10.7, 3.9.14, and 3.8.14, +# The following two functions were added in 3.11.0, 3.10.7, and 3.9.14, # as part of the response to CVE-2020-10735 def set_int_max_str_digits(maxdigits: int) -> None: ... def get_int_max_str_digits() -> int: ... @@ -371,3 +513,8 @@ if sys.version_info >= (3, 12): from . import _monitoring monitoring = _monitoring + +if sys.version_info >= (3, 14): + def is_remote_debug_enabled() -> bool: ... + def remote_exec(pid: int, script: StrOrBytesPath) -> None: ... + def _is_immortal(op: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/sys/_monitoring.pyi b/mypy/typeshed/stdlib/sys/_monitoring.pyi index 0507eeedc26d0..db799e6f32f83 100644 --- a/mypy/typeshed/stdlib/sys/_monitoring.pyi +++ b/mypy/typeshed/stdlib/sys/_monitoring.pyi @@ -5,40 +5,56 @@ # of being a `types.ModuleType` instance that cannot be directly imported, # and exists in the `sys`-module namespace despite `sys` not being a package. +import sys from collections.abc import Callable from types import CodeType -from typing import Any +from typing import Any, Final, type_check_only +from typing_extensions import deprecated -DEBUGGER_ID: int -COVERAGE_ID: int -PROFILER_ID: int -OPTIMIZER_ID: int +DEBUGGER_ID: Final = 0 +COVERAGE_ID: Final = 1 +PROFILER_ID: Final = 2 +OPTIMIZER_ID: Final = 5 def use_tool_id(tool_id: int, name: str, /) -> None: ... + +if sys.version_info >= (3, 14): + def clear_tool_id(tool_id: int, /) -> None: ... + def free_tool_id(tool_id: int, /) -> None: ... def get_tool(tool_id: int, /) -> str | None: ... -events: _events +events: Final[_events] +@type_check_only class _events: - BRANCH: int - CALL: int - C_RAISE: int - C_RETURN: int - EXCEPTION_HANDLED: int - INSTRUCTION: int - JUMP: int - LINE: int - NO_EVENTS: int - PY_RESUME: int - PY_RETURN: int - PY_START: int - PY_THROW: int - PY_UNWIND: int - PY_YIELD: int - RAISE: int - RERAISE: int - STOP_ITERATION: int + CALL: Final[int] + C_RAISE: Final[int] + C_RETURN: Final[int] + EXCEPTION_HANDLED: Final[int] + INSTRUCTION: Final[int] + JUMP: Final[int] + LINE: Final[int] + NO_EVENTS: Final[int] + PY_RESUME: Final[int] + PY_RETURN: Final[int] + PY_START: Final[int] + PY_THROW: Final[int] + PY_UNWIND: Final[int] + PY_YIELD: Final[int] + RAISE: Final[int] + RERAISE: Final[int] + STOP_ITERATION: Final[int] + if sys.version_info >= (3, 14): + BRANCH_LEFT: Final[int] + BRANCH_RIGHT: Final[int] + + @property + @deprecated("Deprecated since Python 3.14. Use `BRANCH_LEFT` or `BRANCH_RIGHT` instead.") + def BRANCH(self) -> int: ... + + else: + BRANCH: Final[int] def get_events(tool_id: int, /) -> int: ... def set_events(tool_id: int, event_set: int, /) -> None: ... @@ -46,7 +62,7 @@ def get_local_events(tool_id: int, code: CodeType, /) -> int: ... def set_local_events(tool_id: int, code: CodeType, event_set: int, /) -> int: ... def restart_events() -> None: ... -DISABLE: object -MISSING: object +DISABLE: Final[object] +MISSING: Final[object] def register_callback(tool_id: int, event: int, func: Callable[..., Any] | None, /) -> Callable[..., Any] | None: ... diff --git a/mypy/typeshed/stdlib/sysconfig.pyi b/mypy/typeshed/stdlib/sysconfig.pyi index 807a979050e80..c6419222df970 100644 --- a/mypy/typeshed/stdlib/sysconfig.pyi +++ b/mypy/typeshed/stdlib/sysconfig.pyi @@ -1,6 +1,6 @@ import sys from typing import IO, Any, Literal, overload -from typing_extensions import deprecated +from typing_extensions import LiteralString, deprecated __all__ = [ "get_config_h_filename", @@ -28,8 +28,10 @@ def get_config_vars(arg: str, /, *args: str) -> list[Any]: ... def get_scheme_names() -> tuple[str, ...]: ... if sys.version_info >= (3, 10): - def get_default_scheme() -> str: ... - def get_preferred_scheme(key: Literal["prefix", "home", "user"]) -> str: ... + def get_default_scheme() -> LiteralString: ... + def get_preferred_scheme(key: Literal["prefix", "home", "user"]) -> LiteralString: ... + # Documented -- see https://docs.python.org/3/library/sysconfig.html#sysconfig._get_preferred_schemes + def _get_preferred_schemes() -> dict[Literal["prefix", "home", "user"], LiteralString]: ... def get_path_names() -> tuple[str, ...]: ... def get_path(name: str, scheme: str = ..., vars: dict[str, Any] | None = None, expand: bool = True) -> str: ... diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index e46903bf610f9..f6623ea9929d4 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -1,13 +1,16 @@ import bz2 import io import sys -from _typeshed import StrOrBytesPath, StrPath +from _typeshed import ReadableBuffer, StrOrBytesPath, StrPath, SupportsRead, WriteableBuffer 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 from types import TracebackType -from typing import IO, ClassVar, Literal, Protocol, overload -from typing_extensions import Self, TypeAlias +from typing import IO, ClassVar, Final, Literal, Protocol, overload, type_check_only +from typing_extensions import Self, TypeAlias, deprecated + +if sys.version_info >= (3, 14): + from compression.zstd import ZstdDict __all__ = [ "TarFile", @@ -38,10 +41,13 @@ if sys.version_info >= (3, 12): "AbsolutePathError", "LinkOutsideDestinationError", ] +if sys.version_info >= (3, 13): + __all__ += ["LinkFallbackError"] _FilterFunction: TypeAlias = Callable[[TarInfo, str], TarInfo | None] _TarfileFilter: TypeAlias = Literal["fully_trusted", "tar", "data"] | _FilterFunction +@type_check_only class _Fileobj(Protocol): def read(self, size: int, /) -> bytes: ... def write(self, b: bytes, /) -> object: ... @@ -52,99 +58,61 @@ class _Fileobj(Protocol): # name: str | bytes # mode: Literal["rb", "r+b", "wb", "xb"] +@type_check_only class _Bz2ReadableFileobj(bz2._ReadableFileobj): def close(self) -> object: ... +@type_check_only class _Bz2WritableFileobj(bz2._WritableFileobj): def close(self) -> object: ... # tar constants -NUL: bytes -BLOCKSIZE: int -RECORDSIZE: int -GNU_MAGIC: bytes -POSIX_MAGIC: bytes - -LENGTH_NAME: int -LENGTH_LINK: int -LENGTH_PREFIX: int - -REGTYPE: bytes -AREGTYPE: bytes -LNKTYPE: bytes -SYMTYPE: bytes -CONTTYPE: bytes -BLKTYPE: bytes -DIRTYPE: bytes -FIFOTYPE: bytes -CHRTYPE: bytes - -GNUTYPE_LONGNAME: bytes -GNUTYPE_LONGLINK: bytes -GNUTYPE_SPARSE: bytes - -XHDTYPE: bytes -XGLTYPE: bytes -SOLARIS_XHDTYPE: bytes - -USTAR_FORMAT: int -GNU_FORMAT: int -PAX_FORMAT: int -DEFAULT_FORMAT: int +NUL: Final = b"\0" +BLOCKSIZE: Final = 512 +RECORDSIZE: Final = 10240 +GNU_MAGIC: Final = b"ustar \0" +POSIX_MAGIC: Final = b"ustar\x0000" + +LENGTH_NAME: Final = 100 +LENGTH_LINK: Final = 100 +LENGTH_PREFIX: Final = 155 + +REGTYPE: Final = b"0" +AREGTYPE: Final = b"\0" +LNKTYPE: Final = b"1" +SYMTYPE: Final = b"2" +CHRTYPE: Final = b"3" +BLKTYPE: Final = b"4" +DIRTYPE: Final = b"5" +FIFOTYPE: Final = b"6" +CONTTYPE: Final = b"7" + +GNUTYPE_LONGNAME: Final = b"L" +GNUTYPE_LONGLINK: Final = b"K" +GNUTYPE_SPARSE: Final = b"S" + +XHDTYPE: Final = b"x" +XGLTYPE: Final = b"g" +SOLARIS_XHDTYPE: Final = b"X" + +_TarFormat: TypeAlias = Literal[0, 1, 2] # does not exist at runtime +USTAR_FORMAT: Final = 0 +GNU_FORMAT: Final = 1 +PAX_FORMAT: Final = 2 +DEFAULT_FORMAT: Final = PAX_FORMAT # tarfile constants -SUPPORTED_TYPES: tuple[bytes, ...] -REGULAR_TYPES: tuple[bytes, ...] -GNU_TYPES: tuple[bytes, ...] -PAX_FIELDS: tuple[str, ...] -PAX_NUMBER_FIELDS: dict[str, type] -PAX_NAME_FIELDS: set[str] - -ENCODING: str - -_FileCreationModes: TypeAlias = Literal["a", "w", "x"] - -@overload -def open( - name: StrOrBytesPath | None = None, - mode: str = "r", - 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 = ..., - compresslevel: int | None = ..., - preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., -) -> TarFile: ... -@overload -def open( - name: StrOrBytesPath | None = None, - mode: _FileCreationModes = ..., - 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 | None = ..., - preset: int | None = ..., -) -> TarFile: ... - -class ExFileObject(io.BufferedReader): +SUPPORTED_TYPES: Final[tuple[bytes, ...]] +REGULAR_TYPES: Final[tuple[bytes, ...]] +GNU_TYPES: Final[tuple[bytes, ...]] +PAX_FIELDS: Final[tuple[str, ...]] +PAX_NUMBER_FIELDS: Final[dict[str, type]] +PAX_NAME_FIELDS: Final[set[str]] + +ENCODING: Final[str] + +class ExFileObject(io.BufferedReader): # undocumented def __init__(self, tarfile: TarFile, tarinfo: TarInfo) -> None: ... class TarFile: @@ -152,13 +120,13 @@ class TarFile: name: StrOrBytesPath | None mode: Literal["r", "a", "w", "x"] fileobj: _Fileobj | None - format: int | None + format: _TarFormat | None tarinfo: type[TarInfo] dereference: bool | None ignore_zeros: bool | None encoding: str | None errors: str - fileobject: type[ExFileObject] + fileobject: type[ExFileObject] # undocumented pax_headers: Mapping[str, str] | None debug: int | None errorlevel: int | None @@ -206,12 +174,13 @@ class TarFile: self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... def __iter__(self) -> Iterator[TarInfo]: ... + @overload @classmethod def open( cls, 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: _Fileobj | None = None, bufsize: int = 10240, *, format: int | None = ..., @@ -224,6 +193,308 @@ class TarFile: debug: int | None = ..., errorlevel: int | None = ..., ) -> Self: ... + if sys.version_info >= (3, 14): + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None, + mode: Literal["r:zst"], + 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 = ..., + level: None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + ) -> Self: ... + + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None, + mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None = None, + *, + mode: Literal["x", "x:", "a", "a:", "w", "w:", "w:tar"], + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + 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, + ) -> Self: ... + @overload + @classmethod + def open( + cls, + 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, + ) -> Self: ... + @overload + @classmethod + def open( + cls, + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + 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 = ..., + ) -> Self: ... + if sys.version_info >= (3, 14): + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None, + mode: Literal["x:zst", "w:zst"], + 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 = ..., + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | None = None, + *, + mode: Literal["x:zst", "w:zst"], + 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 = ..., + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + ) -> Self: ... + + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | ReadableBuffer | None, + mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz", "r|zst"], + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | ReadableBuffer | None = None, + *, + mode: Literal["r|*", "r|", "r|gz", "r|bz2", "r|xz", "r|zst"], + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | WriteableBuffer | None, + mode: Literal["w|", "w|xz", "w|zst"], + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | WriteableBuffer | None = None, + *, + mode: Literal["w|", "w|xz", "w|zst"], + 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 = ..., + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | WriteableBuffer | None, + mode: Literal["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, + ) -> Self: ... + @overload + @classmethod + def open( + cls, + name: StrOrBytesPath | WriteableBuffer | None = None, + *, + mode: Literal["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, + ) -> Self: ... @classmethod def taropen( cls, @@ -330,10 +601,52 @@ class TarFile: debug: int | None = ..., errorlevel: int | None = ..., ) -> Self: ... + if sys.version_info >= (3, 14): + @overload + @classmethod + def zstopen( + cls, + name: StrOrBytesPath | None, + mode: Literal["r"] = "r", + fileobj: IO[bytes] | None = None, + level: None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + ) -> Self: ... + @overload + @classmethod + def zstopen( + cls, + name: StrOrBytesPath | None, + mode: Literal["w", "x"], + fileobj: IO[bytes] | None = None, + level: int | None = None, + options: Mapping[int, int] | None = None, + zstd_dict: ZstdDict | None = None, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + ) -> Self: ... + def getmember(self, name: str) -> TarInfo: ... def getmembers(self) -> _list[TarInfo]: ... def getnames(self) -> _list[str]: ... - def list(self, verbose: bool = True, *, members: _list[TarInfo] | None = None) -> None: ... + def list(self, verbose: bool = True, *, members: Iterable[TarInfo] | None = None) -> None: ... def next(self) -> TarInfo | None: ... # Calling this method without `filter` is deprecated, but it may be set either on the class or in an # individual call, so we can't mark it as @deprecated here. @@ -343,7 +656,7 @@ class TarFile: members: Iterable[TarInfo] | None = None, *, numeric_owner: bool = False, - filter: _TarfileFilter | None = ..., + filter: _TarfileFilter | None = None, ) -> None: ... # Same situation as for `extractall`. def extract( @@ -353,10 +666,17 @@ class TarFile: set_attrs: bool = True, *, numeric_owner: bool = False, - filter: _TarfileFilter | None = ..., + filter: _TarfileFilter | None = None, ) -> None: ... def _extract_member( - self, tarinfo: TarInfo, targetpath: str, set_attrs: bool = True, numeric_owner: bool = False + self, + tarinfo: TarInfo, + targetpath: str, + set_attrs: bool = True, + numeric_owner: bool = False, + *, + filter_function: _FilterFunction | None = None, + extraction_root: str | None = None, ) -> None: ... # undocumented def extractfile(self, member: str | TarInfo) -> IO[bytes] | None: ... def makedir(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ... # undocumented @@ -365,6 +685,9 @@ class TarFile: def makefifo(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ... # undocumented def makedev(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ... # undocumented def makelink(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ... # undocumented + def makelink_with_filter( + self, tarinfo: TarInfo, targetpath: StrOrBytesPath, filter_function: _FilterFunction, extraction_root: str + ) -> None: ... # undocumented def chown(self, tarinfo: TarInfo, targetpath: StrOrBytesPath, numeric_owner: bool) -> None: ... # undocumented def chmod(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ... # undocumented def utime(self, tarinfo: TarInfo, targetpath: StrOrBytesPath) -> None: ... # undocumented @@ -376,17 +699,15 @@ 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: ... def close(self) -> None: ... -if sys.version_info >= (3, 9): - def is_tarfile(name: StrOrBytesPath | IO[bytes]) -> bool: ... +open = TarFile.open -else: - def is_tarfile(name: StrOrBytesPath) -> bool: ... +def is_tarfile(name: StrOrBytesPath | IO[bytes]) -> bool: ... class TarError(Exception): ... class ReadError(TarError): ... @@ -415,11 +736,36 @@ class AbsoluteLinkError(FilterError): class LinkOutsideDestinationError(FilterError): def __init__(self, tarinfo: TarInfo, path: str) -> None: ... +class LinkFallbackError(FilterError): + def __init__(self, tarinfo: TarInfo, path: str) -> None: ... + def fully_trusted_filter(member: TarInfo, dest_path: str) -> TarInfo: ... def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ... def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ... class TarInfo: + __slots__ = ( + "name", + "mode", + "uid", + "gid", + "size", + "mtime", + "chksum", + "type", + "linkname", + "uname", + "gname", + "devmajor", + "devminor", + "offset", + "offset_data", + "pax_headers", + "sparse", + "_tarfile", + "_sparse_structs", + "_link_target", + ) name: str path: str size: int @@ -430,9 +776,8 @@ class TarInfo: offset: int offset_data: int sparse: bytes | None - tarfile: TarFile | None mode: int - type: bytes + type: bytes # usually one of the TYPE constants, but could be an arbitrary byte linkname: str uid: int gid: int @@ -440,6 +785,16 @@ class TarInfo: gname: str pax_headers: Mapping[str, str] def __init__(self, name: str = "") -> None: ... + if sys.version_info >= (3, 13): + @property + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.16.") + def tarfile(self) -> TarFile | None: ... + @tarfile.setter + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.16.") + def tarfile(self, tarfile: TarFile | None) -> None: ... + else: + tarfile: TarFile | None + @classmethod def frombuf(cls, buf: bytes | bytearray, encoding: str, errors: str) -> Self: ... @classmethod @@ -452,7 +807,7 @@ class TarInfo: self, *, name: str = ..., - mtime: int = ..., + mtime: float = ..., mode: int = ..., linkname: str = ..., uid: int = ..., @@ -462,7 +817,7 @@ class TarInfo: deep: bool = True, ) -> Self: ... def get_info(self) -> Mapping[str, str | int | bytes | Mapping[str, str]]: ... - def tobuf(self, format: int | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ... + def tobuf(self, format: _TarFormat | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ... def create_ustar_header( self, info: Mapping[str, str | int | bytes | Mapping[str, str]], encoding: str, errors: str ) -> bytes: ... diff --git a/mypy/typeshed/stdlib/telnetlib.pyi b/mypy/typeshed/stdlib/telnetlib.pyi index 294a1cb12b63e..88aa43d248996 100644 --- a/mypy/typeshed/stdlib/telnetlib.pyi +++ b/mypy/typeshed/stdlib/telnetlib.pyi @@ -1,90 +1,90 @@ import socket -from collections.abc import Callable, Sequence +from collections.abc import Callable, MutableSequence, Sequence from re import Match, Pattern from types import TracebackType -from typing import Any +from typing import Any, Final from typing_extensions import Self __all__ = ["Telnet"] -DEBUGLEVEL: int -TELNET_PORT: int +DEBUGLEVEL: Final = 0 +TELNET_PORT: Final = 23 -IAC: bytes -DONT: bytes -DO: bytes -WONT: bytes -WILL: bytes -theNULL: bytes +IAC: Final = b"\xff" +DONT: Final = b"\xfe" +DO: Final = b"\xfd" +WONT: Final = b"\xfc" +WILL: Final = b"\xfb" +theNULL: Final = b"\x00" -SE: bytes -NOP: bytes -DM: bytes -BRK: bytes -IP: bytes -AO: bytes -AYT: bytes -EC: bytes -EL: bytes -GA: bytes -SB: bytes +SE: Final = b"\xf0" +NOP: Final = b"\xf1" +DM: Final = b"\xf2" +BRK: Final = b"\xf3" +IP: Final = b"\xf4" +AO: Final = b"\xf5" +AYT: Final = b"\xf6" +EC: Final = b"\xf7" +EL: Final = b"\xf8" +GA: Final = b"\xf9" +SB: Final = b"\xfa" -BINARY: bytes -ECHO: bytes -RCP: bytes -SGA: bytes -NAMS: bytes -STATUS: bytes -TM: bytes -RCTE: bytes -NAOL: bytes -NAOP: bytes -NAOCRD: bytes -NAOHTS: bytes -NAOHTD: bytes -NAOFFD: bytes -NAOVTS: bytes -NAOVTD: bytes -NAOLFD: bytes -XASCII: bytes -LOGOUT: bytes -BM: bytes -DET: bytes -SUPDUP: bytes -SUPDUPOUTPUT: bytes -SNDLOC: bytes -TTYPE: bytes -EOR: bytes -TUID: bytes -OUTMRK: bytes -TTYLOC: bytes -VT3270REGIME: bytes -X3PAD: bytes -NAWS: bytes -TSPEED: bytes -LFLOW: bytes -LINEMODE: bytes -XDISPLOC: bytes -OLD_ENVIRON: bytes -AUTHENTICATION: bytes -ENCRYPT: bytes -NEW_ENVIRON: bytes +BINARY: Final = b"\x00" +ECHO: Final = b"\x01" +RCP: Final = b"\x02" +SGA: Final = b"\x03" +NAMS: Final = b"\x04" +STATUS: Final = b"\x05" +TM: Final = b"\x06" +RCTE: Final = b"\x07" +NAOL: Final = b"\x08" +NAOP: Final = b"\t" +NAOCRD: Final = b"\n" +NAOHTS: Final = b"\x0b" +NAOHTD: Final = b"\x0c" +NAOFFD: Final = b"\r" +NAOVTS: Final = b"\x0e" +NAOVTD: Final = b"\x0f" +NAOLFD: Final = b"\x10" +XASCII: Final = b"\x11" +LOGOUT: Final = b"\x12" +BM: Final = b"\x13" +DET: Final = b"\x14" +SUPDUP: Final = b"\x15" +SUPDUPOUTPUT: Final = b"\x16" +SNDLOC: Final = b"\x17" +TTYPE: Final = b"\x18" +EOR: Final = b"\x19" +TUID: Final = b"\x1a" +OUTMRK: Final = b"\x1b" +TTYLOC: Final = b"\x1c" +VT3270REGIME: Final = b"\x1d" +X3PAD: Final = b"\x1e" +NAWS: Final = b"\x1f" +TSPEED: Final = b" " +LFLOW: Final = b"!" +LINEMODE: Final = b'"' +XDISPLOC: Final = b"#" +OLD_ENVIRON: Final = b"$" +AUTHENTICATION: Final = b"%" +ENCRYPT: Final = b"&" +NEW_ENVIRON: Final = b"'" -TN3270E: bytes -XAUTH: bytes -CHARSET: bytes -RSP: bytes -COM_PORT_OPTION: bytes -SUPPRESS_LOCAL_ECHO: bytes -TLS: bytes -KERMIT: bytes -SEND_URL: bytes -FORWARD_X: bytes -PRAGMA_LOGON: bytes -SSPI_LOGON: bytes -PRAGMA_HEARTBEAT: bytes -EXOPL: bytes -NOOPT: bytes +TN3270E: Final = b"(" +XAUTH: Final = b")" +CHARSET: Final = b"*" +RSP: Final = b"+" +COM_PORT_OPTION: Final = b"," +SUPPRESS_LOCAL_ECHO: Final = b"-" +TLS: Final = b"." +KERMIT: Final = b"/" +SEND_URL: Final = b"0" +FORWARD_X: Final = b"1" +PRAGMA_LOGON: Final = b"\x8a" +SSPI_LOGON: Final = b"\x8b" +PRAGMA_HEARTBEAT: Final = b"\x8c" +EXOPL: Final = b"\xff" +NOOPT: Final = b"\x00" class Telnet: host: str | None # undocumented @@ -114,7 +114,7 @@ class Telnet: def mt_interact(self) -> None: ... def listener(self) -> None: ... def expect( - self, list: Sequence[Pattern[bytes] | bytes], timeout: float | None = None + self, list: MutableSequence[Pattern[bytes] | bytes] | Sequence[Pattern[bytes]], timeout: float | None = None ) -> tuple[int, Match[bytes] | None, bytes]: ... def __enter__(self) -> Self: ... def __exit__( diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 0c19d56fc7a65..26491074ff71d 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -13,12 +13,9 @@ from _typeshed import ( WriteableBuffer, ) from collections.abc import Iterable, Iterator -from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Literal, overload -from typing_extensions import Self - -if sys.version_info >= (3, 9): - from types import GenericAlias +from types import GenericAlias, TracebackType +from typing import IO, Any, AnyStr, Final, Generic, Literal, overload +from typing_extensions import Self, deprecated __all__ = [ "NamedTemporaryFile", @@ -37,7 +34,7 @@ __all__ = [ ] # global variables -TMP_MAX: int +TMP_MAX: Final[int] tempdir: str | None template: str @@ -387,7 +384,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def write(self: SpooledTemporaryFile[bytes], s: ReadableBuffer) -> int: ... @overload def write(self, s: AnyStr) -> int: ... - @overload + @overload # type: ignore[override] def writelines(self: SpooledTemporaryFile[str], iterable: Iterable[str]) -> None: ... @overload def writelines(self: SpooledTemporaryFile[bytes], iterable: Iterable[ReadableBuffer]) -> None: ... @@ -399,8 +396,7 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase): def seekable(self) -> bool: ... def writable(self) -> bool: ... def __next__(self) -> AnyStr: ... # type: ignore[override] - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class TemporaryDirectory(Generic[AnyStr]): name: AnyStr @@ -458,8 +454,7 @@ class TemporaryDirectory(Generic[AnyStr]): def cleanup(self) -> None: ... def __enter__(self) -> AnyStr: ... def __exit__(self, exc: type[BaseException] | None, value: BaseException | None, tb: TracebackType | None) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... # The overloads overlap, but they should still work fine. @overload @@ -476,6 +471,7 @@ def mkstemp( 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: ... +@deprecated("Deprecated since Python 2.3. Use `mkstemp()` or `NamedTemporaryFile(delete=False)` instead.") def mktemp(suffix: str = "", prefix: str = "tmp", dir: StrPath | None = None) -> str: ... def gettempdirb() -> bytes: ... def gettempprefixb() -> bytes: ... diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi index a5378e40fdf2c..a35be5dfe740a 100644 --- a/mypy/typeshed/stdlib/termios.pyi +++ b/mypy/typeshed/stdlib/termios.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import FileDescriptorLike -from typing import Any +from typing import Any, Final from typing_extensions import TypeAlias # Must be a list of length 7, containing 6 ints and a list of NCCS 1-character bytes or ints. @@ -9,250 +9,287 @@ _Attr: TypeAlias = list[int | list[bytes | int]] | list[int | list[bytes]] | lis _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 - CR2: int - CR3: int - CRDLY: int - CREAD: int - CRPRNT: int - CRTSCTS: int - CS5: int - CS6: int - CS7: int - CS8: int - CSIZE: int - CSTART: int - CSTOP: int - CSTOPB: int - CSUSP: int - CSWTCH: int - CWERASE: int - ECHO: int - ECHOCTL: int - ECHOE: int - ECHOK: int - ECHOKE: int - ECHONL: int - ECHOPRT: int - EXTA: int - EXTB: int - FF0: int - FF1: int - FFDLY: int - FIOASYNC: int - FIOCLEX: int - FIONBIO: int - FIONCLEX: int - FIONREAD: int - FLUSHO: int - HUPCL: int - IBSHIFT: int - ICANON: int - ICRNL: int - IEXTEN: int - IGNBRK: int - 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 - OPOST: int - PARENB: int - PARMRK: int - PARODD: int - PENDIN: int - TAB0: int - TAB1: int - TAB2: int - TAB3: int - TABDLY: int - TCFLSH: int - TCGETA: int - TCGETS: int - TCIFLUSH: int - TCIOFF: int - TCIOFLUSH: int - TCION: int - TCOFLUSH: int - TCOOFF: int - TCOON: int - 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 - TIOCM_DSR: int - TIOCM_DTR: int - TIOCM_LE: int - TIOCM_RI: int - TIOCM_RNG: int - TIOCM_RTS: int - TIOCM_SR: int - TIOCM_ST: int - TIOCMBIC: int - TIOCMBIS: int - TIOCMGET: int - TIOCMIWAIT: int - TIOCMSET: int - TIOCNOTTY: int - TIOCNXCL: int - TIOCOUTQ: int - TIOCPKT_DATA: int - TIOCPKT_DOSTOP: int - TIOCPKT_FLUSHREAD: int - TIOCPKT_FLUSHWRITE: int - TIOCPKT_NOSTOP: int - TIOCPKT_START: int - 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 - VEOL: int - VEOL2: int - VERASE: int - VINTR: int - VKILL: int - VLNEXT: int - VMIN: int - VQUIT: int - VREPRINT: int - VSTART: int - VSTOP: int - VSUSP: int - VSWTC: int - VSWTCH: int - VT0: int - VT1: int - VTDLY: int - VTIME: int - VWERASE: int - XCASE: int - XTABS: int + # Values depends on the platform + B0: Final[int] + B110: Final[int] + B115200: Final[int] + B1200: Final[int] + B134: Final[int] + B150: Final[int] + B1800: Final[int] + B19200: Final[int] + B200: Final[int] + B230400: Final[int] + B2400: Final[int] + B300: Final[int] + B38400: Final[int] + B4800: Final[int] + B50: Final[int] + B57600: Final[int] + B600: Final[int] + B75: Final[int] + B9600: Final[int] + BRKINT: Final[int] + BS0: Final[int] + BS1: Final[int] + BSDLY: Final[int] + CDSUSP: Final[int] + CEOF: Final[int] + CEOL: Final[int] + CEOT: Final[int] + CERASE: Final[int] + CFLUSH: Final[int] + CINTR: Final[int] + CKILL: Final[int] + CLNEXT: Final[int] + CLOCAL: Final[int] + CQUIT: Final[int] + CR0: Final[int] + CR1: Final[int] + CR2: Final[int] + CR3: Final[int] + CRDLY: Final[int] + CREAD: Final[int] + CRPRNT: Final[int] + CRTSCTS: Final[int] + CS5: Final[int] + CS6: Final[int] + CS7: Final[int] + CS8: Final[int] + CSIZE: Final[int] + CSTART: Final[int] + CSTOP: Final[int] + CSTOPB: Final[int] + CSUSP: Final[int] + CWERASE: Final[int] + ECHO: Final[int] + ECHOCTL: Final[int] + ECHOE: Final[int] + ECHOK: Final[int] + ECHOKE: Final[int] + ECHONL: Final[int] + ECHOPRT: Final[int] + EXTA: Final[int] + EXTB: Final[int] + FF0: Final[int] + FF1: Final[int] + FFDLY: Final[int] + FIOASYNC: Final[int] + FIOCLEX: Final[int] + FIONBIO: Final[int] + FIONCLEX: Final[int] + FIONREAD: Final[int] + FLUSHO: Final[int] + HUPCL: Final[int] + ICANON: Final[int] + ICRNL: Final[int] + IEXTEN: Final[int] + IGNBRK: Final[int] + IGNCR: Final[int] + IGNPAR: Final[int] + IMAXBEL: Final[int] + INLCR: Final[int] + INPCK: Final[int] + ISIG: Final[int] + ISTRIP: Final[int] + IXANY: Final[int] + IXOFF: Final[int] + IXON: Final[int] + NCCS: Final[int] + NL0: Final[int] + NL1: Final[int] + NLDLY: Final[int] + NOFLSH: Final[int] + OCRNL: Final[int] + OFDEL: Final[int] + OFILL: Final[int] + ONLCR: Final[int] + ONLRET: Final[int] + ONOCR: Final[int] + OPOST: Final[int] + PARENB: Final[int] + PARMRK: Final[int] + PARODD: Final[int] + PENDIN: Final[int] + TAB0: Final[int] + TAB1: Final[int] + TAB2: Final[int] + TAB3: Final[int] + TABDLY: Final[int] + TCIFLUSH: Final[int] + TCIOFF: Final[int] + TCIOFLUSH: Final[int] + TCION: Final[int] + TCOFLUSH: Final[int] + TCOOFF: Final[int] + TCOON: Final[int] + TCSADRAIN: Final[int] + TCSAFLUSH: Final[int] + TCSANOW: Final[int] + TIOCCONS: Final[int] + TIOCEXCL: Final[int] + TIOCGETD: Final[int] + TIOCGPGRP: Final[int] + TIOCGWINSZ: Final[int] + TIOCM_CAR: Final[int] + TIOCM_CD: Final[int] + TIOCM_CTS: Final[int] + TIOCM_DSR: Final[int] + TIOCM_DTR: Final[int] + TIOCM_LE: Final[int] + TIOCM_RI: Final[int] + TIOCM_RNG: Final[int] + TIOCM_RTS: Final[int] + TIOCM_SR: Final[int] + TIOCM_ST: Final[int] + TIOCMBIC: Final[int] + TIOCMBIS: Final[int] + TIOCMGET: Final[int] + TIOCMSET: Final[int] + TIOCNOTTY: Final[int] + TIOCNXCL: Final[int] + TIOCOUTQ: Final[int] + TIOCPKT_DATA: Final[int] + TIOCPKT_DOSTOP: Final[int] + TIOCPKT_FLUSHREAD: Final[int] + TIOCPKT_FLUSHWRITE: Final[int] + TIOCPKT_NOSTOP: Final[int] + TIOCPKT_START: Final[int] + TIOCPKT_STOP: Final[int] + TIOCPKT: Final[int] + TIOCSCTTY: Final[int] + TIOCSETD: Final[int] + TIOCSPGRP: Final[int] + TIOCSTI: Final[int] + TIOCSWINSZ: Final[int] + TOSTOP: Final[int] + VDISCARD: Final[int] + VEOF: Final[int] + VEOL: Final[int] + VEOL2: Final[int] + VERASE: Final[int] + VINTR: Final[int] + VKILL: Final[int] + VLNEXT: Final[int] + VMIN: Final[int] + VQUIT: Final[int] + VREPRINT: Final[int] + VSTART: Final[int] + VSTOP: Final[int] + VSUSP: Final[int] + VT0: Final[int] + VT1: Final[int] + VTDLY: Final[int] + VTIME: Final[int] + VWERASE: Final[int] + + if sys.version_info >= (3, 13): + EXTPROC: Final[int] + IUTF8: Final[int] + + if sys.platform == "darwin" and sys.version_info >= (3, 13): + ALTWERASE: Final[int] + B14400: Final[int] + B28800: Final[int] + B7200: Final[int] + B76800: Final[int] + CCAR_OFLOW: Final[int] + CCTS_OFLOW: Final[int] + CDSR_OFLOW: Final[int] + CDTR_IFLOW: Final[int] + CIGNORE: Final[int] + CRTS_IFLOW: Final[int] + MDMBUF: Final[int] + NL2: Final[int] + NL3: Final[int] + NOKERNINFO: Final[int] + ONOEOT: Final[int] + OXTABS: Final[int] + VDSUSP: Final[int] + VSTATUS: Final[int] + + if sys.platform == "darwin" and sys.version_info >= (3, 11): + TIOCGSIZE: Final[int] + TIOCSSIZE: Final[int] + + if sys.platform == "linux": + B1152000: Final[int] + B576000: Final[int] + CBAUD: Final[int] + CBAUDEX: Final[int] + CIBAUD: Final[int] + IOCSIZE_MASK: Final[int] + IOCSIZE_SHIFT: Final[int] + IUCLC: Final[int] + N_MOUSE: Final[int] + N_PPP: Final[int] + N_SLIP: Final[int] + N_STRIP: Final[int] + N_TTY: Final[int] + NCC: Final[int] + OLCUC: Final[int] + TCFLSH: Final[int] + TCGETA: Final[int] + TCGETS: Final[int] + TCSBRK: Final[int] + TCSBRKP: Final[int] + TCSETA: Final[int] + TCSETAF: Final[int] + TCSETAW: Final[int] + TCSETS: Final[int] + TCSETSF: Final[int] + TCSETSW: Final[int] + TCXONC: Final[int] + TIOCGICOUNT: Final[int] + TIOCGLCKTRMIOS: Final[int] + TIOCGSERIAL: Final[int] + TIOCGSOFTCAR: Final[int] + TIOCINQ: Final[int] + TIOCLINUX: Final[int] + TIOCMIWAIT: Final[int] + TIOCTTYGSTRUCT: Final[int] + TIOCSER_TEMT: Final[int] + TIOCSERCONFIG: Final[int] + TIOCSERGETLSR: Final[int] + TIOCSERGETMULTI: Final[int] + TIOCSERGSTRUCT: Final[int] + TIOCSERGWILD: Final[int] + TIOCSERSETMULTI: Final[int] + TIOCSERSWILD: Final[int] + TIOCSLCKTRMIOS: Final[int] + TIOCSSERIAL: Final[int] + TIOCSSOFTCAR: Final[int] + VSWTC: Final[int] + VSWTCH: Final[int] + XCASE: Final[int] + XTABS: Final[int] + + if sys.platform != "darwin": + B1000000: Final[int] + B1500000: Final[int] + B2000000: Final[int] + B2500000: Final[int] + B3000000: Final[int] + B3500000: Final[int] + B4000000: Final[int] + B460800: Final[int] + B500000: Final[int] + B921600: Final[int] + + if sys.platform != "linux": + TCSASOFT: Final[int] + + if sys.platform != "darwin" and sys.platform != "linux": + # not available on FreeBSD either. + CDEL: Final[int] + CEOL2: Final[int] + CESC: Final[int] + CNUL: Final[int] + COMMON: Final[int] + CSWTCH: Final[int] + IBSHIFT: Final[int] + INIT_C_CC: Final[int] + NSWTCH: Final[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 c441a04681e22..7b0f15bdfa2e8 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -1,10 +1,12 @@ import _thread import sys -from _thread import _excepthook, _ExceptHookArgs, get_native_id as get_native_id +from _thread import _ExceptHookArgs, get_native_id as get_native_id from _typeshed import ProfileFunction, TraceFunction from collections.abc import Callable, Iterable, Mapping +from contextvars import ContextVar from types import TracebackType -from typing import Any, TypeVar, final +from typing import Any, Final, TypeVar, final +from typing_extensions import deprecated _T = TypeVar("_T") @@ -44,9 +46,11 @@ if sys.version_info >= (3, 12): _profile_hook: ProfileFunction | None def active_count() -> int: ... -def activeCount() -> int: ... # deprecated alias for active_count() +@deprecated("Deprecated since Python 3.10. Use `active_count()` instead.") +def activeCount() -> int: ... def current_thread() -> Thread: ... -def currentThread() -> Thread: ... # deprecated alias for current_thread() +@deprecated("Deprecated since Python 3.10. Use `current_thread()` instead.") +def currentThread() -> Thread: ... def get_ident() -> int: ... def enumerate() -> list[Thread]: ... def main_thread() -> Thread: ... @@ -63,7 +67,7 @@ if sys.version_info >= (3, 10): def stack_size(size: int = 0, /) -> int: ... -TIMEOUT_MAX: float +TIMEOUT_MAX: Final[float] ThreadError = _thread.error local = _thread._local @@ -73,68 +77,79 @@ class Thread: @property def ident(self) -> int | None: ... daemon: bool - def __init__( - self, - group: None = None, - target: Callable[..., object] | None = None, - name: str | None = None, - args: Iterable[Any] = (), - kwargs: Mapping[str, Any] | None = None, - *, - daemon: bool | None = None, - ) -> None: ... + if sys.version_info >= (3, 14): + def __init__( + self, + group: None = None, + target: Callable[..., object] | None = None, + name: str | None = None, + args: Iterable[Any] = (), + kwargs: Mapping[str, Any] | None = None, + *, + daemon: bool | None = None, + context: ContextVar[Any] | None = None, + ) -> None: ... + else: + def __init__( + self, + group: None = None, + target: Callable[..., object] | None = None, + name: str | None = None, + args: Iterable[Any] = (), + kwargs: Mapping[str, Any] | None = None, + *, + daemon: bool | None = None, + ) -> None: ... + def start(self) -> None: ... def run(self) -> None: ... def join(self, timeout: float | None = None) -> None: ... @property def native_id(self) -> int | None: ... # only available on some platforms def is_alive(self) -> bool: ... - if sys.version_info < (3, 9): - def isAlive(self) -> bool: ... - # the following methods are all deprecated - def getName(self) -> str: ... - def setName(self, name: str) -> None: ... + @deprecated("Deprecated since Python 3.10. Read the `daemon` attribute instead.") def isDaemon(self) -> bool: ... + @deprecated("Deprecated since Python 3.10. Set the `daemon` attribute instead.") def setDaemon(self, daemonic: bool) -> None: ... + @deprecated("Deprecated since Python 3.10. Read the `name` attribute instead.") + def getName(self) -> str: ... + @deprecated("Deprecated since Python 3.10. Set the `name` attribute instead.") + def setName(self, name: str) -> None: ... class _DummyThread(Thread): def __init__(self) -> None: ... -@final -class Lock: - def __enter__(self) -> bool: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... - def release(self) -> None: ... - def locked(self) -> bool: ... - def acquire_lock(self, blocking: bool = ..., timeout: float = ...) -> bool: ... # undocumented - def release_lock(self) -> None: ... # undocumented - def locked_lock(self) -> bool: ... # undocumented +# This is actually the function _thread.allocate_lock for <= 3.12 +Lock = _thread.LockType +# Python implementation of RLock. @final class _RLock: + _count: int def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... __enter__ = acquire def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... -RLock = _RLock + if sys.version_info >= (3, 14): + def locked(self) -> bool: ... + +RLock = _thread.RLock # Actually a function at runtime. class Condition: - def __init__(self, lock: Lock | _RLock | None = None) -> None: ... + def __init__(self, lock: Lock | _RLock | RLock | None = None) -> None: ... def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... + def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... def wait(self, timeout: float | None = None) -> bool: ... def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ... def notify(self, n: int = 1) -> None: ... def notify_all(self) -> None: ... - def notifyAll(self) -> None: ... # deprecated alias for notify_all() + @deprecated("Deprecated since Python 3.10. Use `notify_all()` instead.") + def notifyAll(self) -> None: ... class Semaphore: _value: int @@ -142,21 +157,21 @@ class Semaphore: def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... def acquire(self, blocking: bool = True, timeout: float | None = None) -> bool: ... def __enter__(self, blocking: bool = True, timeout: float | None = None) -> bool: ... - if sys.version_info >= (3, 9): - def release(self, n: int = 1) -> None: ... - else: - def release(self) -> None: ... + def release(self, n: int = 1) -> None: ... class BoundedSemaphore(Semaphore): ... class Event: def is_set(self) -> bool: ... - def isSet(self) -> bool: ... # deprecated alias for is_set() + @deprecated("Deprecated since Python 3.10. Use `is_set()` instead.") + def isSet(self) -> bool: ... def set(self) -> None: ... def clear(self) -> None: ... def wait(self, timeout: float | None = None) -> bool: ... -excepthook = _excepthook +excepthook: Callable[[_ExceptHookArgs], object] +if sys.version_info >= (3, 10): + __excepthook__: Callable[[_ExceptHookArgs], object] ExceptHookArgs = _ExceptHookArgs class Timer(Thread): diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi index 71cdc4d78fdc4..5665efbba69d0 100644 --- a/mypy/typeshed/stdlib/time.pyi +++ b/mypy/typeshed/stdlib/time.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import structseq -from typing import Any, Final, Literal, Protocol, final +from typing import Any, Final, Literal, Protocol, final, type_check_only from typing_extensions import TypeAlias _TimeTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int] @@ -11,28 +11,28 @@ timezone: int tzname: tuple[str, str] if sys.platform == "linux": - CLOCK_BOOTTIME: int + CLOCK_BOOTTIME: Final[int] if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": - CLOCK_PROF: int # FreeBSD, NetBSD, OpenBSD - CLOCK_UPTIME: int # FreeBSD, OpenBSD + CLOCK_PROF: Final[int] # FreeBSD, NetBSD, OpenBSD + CLOCK_UPTIME: Final[int] # FreeBSD, OpenBSD if sys.platform != "win32": - CLOCK_MONOTONIC: int - CLOCK_MONOTONIC_RAW: int - CLOCK_PROCESS_CPUTIME_ID: int - CLOCK_REALTIME: int - CLOCK_THREAD_CPUTIME_ID: int + CLOCK_MONOTONIC: Final[int] + CLOCK_MONOTONIC_RAW: Final[int] + CLOCK_PROCESS_CPUTIME_ID: Final[int] + CLOCK_REALTIME: Final[int] + CLOCK_THREAD_CPUTIME_ID: Final[int] if sys.platform != "linux" and sys.platform != "darwin": - CLOCK_HIGHRES: int # Solaris only + CLOCK_HIGHRES: Final[int] # Solaris only if sys.platform == "darwin": - CLOCK_UPTIME_RAW: int + CLOCK_UPTIME_RAW: Final[int] if sys.version_info >= (3, 13): - CLOCK_UPTIME_RAW_APPROX: int - CLOCK_MONOTONIC_RAW_APPROX: int + CLOCK_UPTIME_RAW_APPROX: Final[int] + CLOCK_MONOTONIC_RAW_APPROX: Final[int] -if sys.version_info >= (3, 9) and sys.platform == "linux": - CLOCK_TAI: int +if sys.platform == "linux": + CLOCK_TAI: Final[int] # Constructor takes an iterable of any type, of length between 9 and 11 elements. # However, it always *behaves* like a tuple of 9 elements, @@ -80,6 +80,7 @@ def time() -> float: ... if sys.platform != "win32": def tzset() -> None: ... # Unix only +@type_check_only class _ClockInfo(Protocol): adjustable: bool implementation: str diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 4d25a04f8eb75..ef57faa2b0097 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,147 +1,151 @@ import _tkinter import sys -from _typeshed import Incomplete, StrEnum, StrOrBytesPath +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_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated - -if sys.version_info >= (3, 9): - __all__ = [ - "TclError", - "NO", - "FALSE", - "OFF", - "YES", - "TRUE", - "ON", - "N", - "S", - "W", - "E", - "NW", - "SW", - "NE", - "SE", - "NS", - "EW", - "NSEW", - "CENTER", - "NONE", - "X", - "Y", - "BOTH", - "LEFT", - "TOP", - "RIGHT", - "BOTTOM", - "RAISED", - "SUNKEN", - "FLAT", - "RIDGE", - "GROOVE", - "SOLID", - "HORIZONTAL", - "VERTICAL", - "NUMERIC", - "CHAR", - "WORD", - "BASELINE", - "INSIDE", - "OUTSIDE", - "SEL", - "SEL_FIRST", - "SEL_LAST", - "END", - "INSERT", - "CURRENT", - "ANCHOR", - "ALL", - "NORMAL", - "DISABLED", - "ACTIVE", - "HIDDEN", - "CASCADE", - "CHECKBUTTON", - "COMMAND", - "RADIOBUTTON", - "SEPARATOR", - "SINGLE", - "BROWSE", - "MULTIPLE", - "EXTENDED", - "DOTBOX", - "UNDERLINE", - "PIESLICE", - "CHORD", - "ARC", - "FIRST", - "LAST", - "BUTT", - "PROJECTING", - "ROUND", - "BEVEL", - "MITER", - "MOVETO", - "SCROLL", - "UNITS", - "PAGES", - "TkVersion", - "TclVersion", - "READABLE", - "WRITABLE", - "EXCEPTION", - "EventType", - "Event", - "NoDefaultRoot", - "Variable", - "StringVar", - "IntVar", - "DoubleVar", - "BooleanVar", - "mainloop", - "getint", - "getdouble", - "getboolean", - "Misc", - "CallWrapper", - "XView", - "YView", - "Wm", - "Tk", - "Tcl", - "Pack", - "Place", - "Grid", - "BaseWidget", - "Widget", - "Toplevel", - "Button", - "Canvas", - "Checkbutton", - "Entry", - "Frame", - "Label", - "Listbox", - "Menu", - "Menubutton", - "Message", - "Radiobutton", - "Scale", - "Scrollbar", - "Text", - "OptionMenu", - "Image", - "PhotoImage", - "BitmapImage", - "image_names", - "image_types", - "Spinbox", - "LabelFrame", - "PanedWindow", - ] +from types import GenericAlias, TracebackType +from typing import Any, ClassVar, Final, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only +from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated, disjoint_base + +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from enum import Enum + +__all__ = [ + "TclError", + "NO", + "FALSE", + "OFF", + "YES", + "TRUE", + "ON", + "N", + "S", + "W", + "E", + "NW", + "SW", + "NE", + "SE", + "NS", + "EW", + "NSEW", + "CENTER", + "NONE", + "X", + "Y", + "BOTH", + "LEFT", + "TOP", + "RIGHT", + "BOTTOM", + "RAISED", + "SUNKEN", + "FLAT", + "RIDGE", + "GROOVE", + "SOLID", + "HORIZONTAL", + "VERTICAL", + "NUMERIC", + "CHAR", + "WORD", + "BASELINE", + "INSIDE", + "OUTSIDE", + "SEL", + "SEL_FIRST", + "SEL_LAST", + "END", + "INSERT", + "CURRENT", + "ANCHOR", + "ALL", + "NORMAL", + "DISABLED", + "ACTIVE", + "HIDDEN", + "CASCADE", + "CHECKBUTTON", + "COMMAND", + "RADIOBUTTON", + "SEPARATOR", + "SINGLE", + "BROWSE", + "MULTIPLE", + "EXTENDED", + "DOTBOX", + "UNDERLINE", + "PIESLICE", + "CHORD", + "ARC", + "FIRST", + "LAST", + "BUTT", + "PROJECTING", + "ROUND", + "BEVEL", + "MITER", + "MOVETO", + "SCROLL", + "UNITS", + "PAGES", + "TkVersion", + "TclVersion", + "READABLE", + "WRITABLE", + "EXCEPTION", + "EventType", + "Event", + "NoDefaultRoot", + "Variable", + "StringVar", + "IntVar", + "DoubleVar", + "BooleanVar", + "mainloop", + "getint", + "getdouble", + "getboolean", + "Misc", + "CallWrapper", + "XView", + "YView", + "Wm", + "Tk", + "Tcl", + "Pack", + "Place", + "Grid", + "BaseWidget", + "Widget", + "Toplevel", + "Button", + "Canvas", + "Checkbutton", + "Entry", + "Frame", + "Label", + "Listbox", + "Menu", + "Menubutton", + "Message", + "Radiobutton", + "Scale", + "Scrollbar", + "Text", + "OptionMenu", + "Image", + "PhotoImage", + "BitmapImage", + "image_names", + "image_types", + "Spinbox", + "LabelFrame", + "PanedWindow", +] # Using anything from tkinter.font in this file means that 'import tkinter' # seems to also load tkinter.font. That's not how it actually works, but @@ -149,11 +153,11 @@ if sys.version_info >= (3, 9): TclError = _tkinter.TclError wantobjects: int -TkVersion: float -TclVersion: float -READABLE = _tkinter.READABLE -WRITABLE = _tkinter.WRITABLE -EXCEPTION = _tkinter.EXCEPTION +TkVersion: Final[float] +TclVersion: Final[float] +READABLE: Final = _tkinter.READABLE +WRITABLE: Final = _tkinter.WRITABLE +EXCEPTION: Final = _tkinter.EXCEPTION # Quick guide for figuring out which widget class to choose: # - Misc: any widget (don't use BaseWidget because Tk doesn't inherit from BaseWidget) @@ -169,74 +173,111 @@ EXCEPTION = _tkinter.EXCEPTION # # You can also read the manual pages online: https://www.tcl.tk/doc/ -# Some widgets have an option named -compound that accepts different values -# than the _Compound defined here. Many other options have similar things. -_Anchor: TypeAlias = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] # manual page: Tk_GetAnchor -_ButtonCommand: TypeAlias = str | Callable[[], Any] # accepts string of tcl code, return value is returned from Button.invoke() -_Compound: TypeAlias = Literal["top", "left", "center", "right", "bottom", "none"] # -compound in manual page named 'options' # manual page: Tk_GetCursor _Cursor: TypeAlias = str | tuple[str] | tuple[str, str] | tuple[str, str, str] | tuple[str, str, str, str] -# example when it's sequence: entry['invalidcommand'] = [entry.register(print), '%P'] -_EntryValidateCommand: TypeAlias = str | list[str] | tuple[str, ...] | Callable[[], bool] -_ImageSpec: TypeAlias = _Image | str # str can be from e.g. tkinter.image_names() -_Relief: TypeAlias = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] # manual page: Tk_GetRelief -_ScreenUnits: TypeAlias = str | float # Often the right type instead of int. Manual page: Tk_GetPixels -# -xscrollcommand and -yscrollcommand in 'options' manual page -_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" + if sys.version_info >= (3, 12): + class _VersionInfoType(_VersionInfoTypeBase): ... + else: + @disjoint_base + 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 = KeyPress + 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. -_W_co = TypeVar("_W_co", covariant=True, bound=Misc) +_W_co = TypeVar("_W_co", covariant=True, bound=Misc, default=Misc) class Event(Generic[_W_co]): serial: int @@ -258,27 +299,37 @@ class Event(Generic[_W_co]): type: EventType widget: _W_co delta: int + if sys.version_info >= (3, 14): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def NoDefaultRoot() -> None: ... class Variable: - def __init__(self, master: Misc | None = None, value: Incomplete | None = None, name: str | None = None) -> None: ... + def __init__(self, master: Misc | None = None, value=None, name: str | None = None) -> None: ... def set(self, value) -> None: ... initialize = set def get(self): ... def trace_add(self, mode: Literal["array", "read", "write", "unset"], callback: Callable[[str, str, str], object]) -> str: ... def trace_remove(self, mode: Literal["array", "read", "write", "unset"], cbname: str) -> None: ... def trace_info(self) -> list[tuple[tuple[Literal["array", "read", "write", "unset"], ...], str]]: ... - @deprecated("use trace_add() instead of trace()") - def trace(self, mode, callback): ... - @deprecated("use trace_add() instead of trace_variable()") - def trace_variable(self, mode, callback): ... - @deprecated("use trace_remove() instead of trace_vdelete()") - def trace_vdelete(self, mode, cbname) -> None: ... - @deprecated("use trace_info() instead of trace_vinfo()") - def trace_vinfo(self): ... + if sys.version_info >= (3, 14): + @deprecated("Deprecated since Python 3.14. Use `trace_add()` instead.") + def trace(self, mode, callback) -> str: ... + @deprecated("Deprecated since Python 3.14. Use `trace_add()` instead.") + def trace_variable(self, mode, callback) -> str: ... + @deprecated("Deprecated since Python 3.14. Use `trace_remove()` instead.") + def trace_vdelete(self, mode, cbname) -> None: ... + @deprecated("Deprecated since Python 3.14. Use `trace_info()` instead.") + def trace_vinfo(self) -> list[Incomplete]: ... + else: + def trace(self, mode, callback) -> str: ... + def trace_variable(self, mode, callback) -> str: ... + def trace_vdelete(self, mode, cbname) -> None: ... + def trace_vinfo(self) -> list[Incomplete]: ... + def __eq__(self, other: object) -> bool: ... def __del__(self) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] class StringVar(Variable): def __init__(self, master: Misc | None = None, value: str | None = None, name: str | None = None) -> None: ... @@ -306,26 +357,31 @@ class BooleanVar(Variable): def mainloop(n: int = 0) -> None: ... -getint: Incomplete -getdouble: Incomplete +getint = int +getdouble = float -def getboolean(s): ... +def getboolean(s) -> bool: ... _Ts = TypeVarTuple("_Ts") +@type_check_only class _GridIndexInfo(TypedDict, total=False): - minsize: _ScreenUnits - pad: _ScreenUnits + minsize: float | str + pad: float | str uniform: str | None weight: int +@type_check_only +class _BusyInfo(TypedDict): + cursor: _Cursor + class Misc: master: Misc | None tk: _tkinter.TkappType children: dict[str, Widget] def destroy(self) -> None: ... def deletecommand(self, name: str) -> None: ... - def tk_strictMotif(self, boolean: Incomplete | None = None): ... + def tk_strictMotif(self, boolean=None): ... def tk_bisque(self) -> None: ... def tk_setPalette(self, *args, **kw) -> None: ... def wait_variable(self, name: str | Variable = "PY_VAR") -> None: ... @@ -334,9 +390,9 @@ class Misc: def wait_visibility(self, window: Misc | None = None) -> None: ... def setvar(self, name: str = "PY_VAR", value: str = "1") -> None: ... def getvar(self, name: str = "PY_VAR"): ... - def getint(self, s): ... - def getdouble(self, s): ... - def getboolean(self, s): ... + def getint(self, s) -> int: ... + def getdouble(self, s) -> float: ... + def getboolean(self, s) -> bool: ... def focus_set(self) -> None: ... focus = focus_set def focus_force(self) -> None: ... @@ -352,7 +408,29 @@ class Misc: # after_idle is essentially partialmethod(after, "idle") def after_idle(self, func: Callable[[Unpack[_Ts]], object], *args: Unpack[_Ts]) -> str: ... def after_cancel(self, id: str) -> None: ... + if sys.version_info >= (3, 13): + def after_info(self, id: str | None = None) -> tuple[str, ...]: ... + def bell(self, displayof: Literal[0] | Misc | None = 0) -> None: ... + if sys.version_info >= (3, 13): + # Supports options from `_BusyInfo`` + def tk_busy_cget(self, option: Literal["cursor"]) -> _Cursor: ... + busy_cget = tk_busy_cget + def tk_busy_configure(self, cnf: Any = None, **kw: Any) -> Any: ... + tk_busy_config = tk_busy_configure + busy_configure = tk_busy_configure + busy_config = tk_busy_configure + def tk_busy_current(self, pattern: str | None = None) -> list[Misc]: ... + busy_current = tk_busy_current + def tk_busy_forget(self) -> None: ... + busy_forget = tk_busy_forget + def tk_busy_hold(self, **kw: Unpack[_BusyInfo]) -> None: ... + tk_busy = tk_busy_hold + busy_hold = tk_busy_hold + busy = tk_busy_hold + def tk_busy_status(self) -> bool: ... + busy_status = tk_busy_status + def clipboard_get(self, *, displayof: Misc = ..., type: str = ...) -> str: ... def clipboard_clear(self, *, displayof: Misc = ...) -> None: ... def clipboard_append(self, string: str, *, displayof: Misc = ..., format: str = ..., type: str = ...) -> None: ... @@ -366,15 +444,15 @@ class Misc: ) -> None: ... def option_clear(self) -> None: ... def option_get(self, name, className): ... - def option_readfile(self, fileName, priority: Incomplete | None = None) -> None: ... + def option_readfile(self, fileName, priority=None) -> None: ... def selection_clear(self, **kw) -> None: ... def selection_get(self, **kw): ... def selection_handle(self, command, **kw) -> None: ... def selection_own(self, **kw) -> None: ... def selection_own_get(self, **kw): ... def send(self, interp, cmd, *args): ... - def lower(self, belowThis: Incomplete | None = None) -> None: ... - def tkraise(self, aboveThis: Incomplete | None = None) -> None: ... + def lower(self, belowThis=None) -> None: ... + def tkraise(self, aboveThis=None) -> None: ... lift = tkraise if sys.version_info >= (3, 11): def info_patchlevel(self) -> _VersionInfoType: ... @@ -382,13 +460,13 @@ class Misc: def winfo_atom(self, name: str, displayof: Literal[0] | Misc | None = 0) -> int: ... def winfo_atomname(self, id: int, displayof: Literal[0] | Misc | None = 0) -> str: ... def winfo_cells(self) -> int: ... - def winfo_children(self) -> list[Widget]: ... # Widget because it can't be Toplevel or Tk + def winfo_children(self) -> list[Widget | Toplevel]: ... def winfo_class(self) -> str: ... def winfo_colormapfull(self) -> bool: ... def winfo_containing(self, rootX: int, rootY: int, displayof: Literal[0] | Misc | None = 0) -> Misc | None: ... def winfo_depth(self) -> int: ... def winfo_exists(self) -> bool: ... - def winfo_fpixels(self, number: _ScreenUnits) -> float: ... + def winfo_fpixels(self, number: float | str) -> float: ... def winfo_geometry(self) -> str: ... def winfo_height(self) -> int: ... def winfo_id(self) -> int: ... @@ -398,7 +476,7 @@ class Misc: def winfo_name(self) -> str: ... def winfo_parent(self) -> str: ... # return value needs nametowidget() def winfo_pathname(self, id: int, displayof: Literal[0] | Misc | None = 0): ... - def winfo_pixels(self, number: _ScreenUnits) -> int: ... + def winfo_pixels(self, number: float | str) -> int: ... def winfo_pointerx(self) -> int: ... def winfo_pointerxy(self) -> tuple[int, int]: ... def winfo_pointery(self) -> int: ... @@ -489,7 +567,7 @@ class Misc: @overload def pack_propagate(self) -> None: ... propagate = pack_propagate - def grid_anchor(self, anchor: _Anchor | None = None) -> None: ... + def grid_anchor(self, anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] | None = None) -> None: ... anchor = grid_anchor @overload def grid_bbox( @@ -505,24 +583,24 @@ class Misc: index: int | str | list[int] | tuple[int, ...], cnf: _GridIndexInfo = {}, *, - minsize: _ScreenUnits = ..., - pad: _ScreenUnits = ..., + minsize: float | str = ..., + pad: float | str = ..., 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, ...], cnf: _GridIndexInfo = {}, *, - minsize: _ScreenUnits = ..., - pad: _ScreenUnits = ..., + minsize: float | str = ..., + pad: float | str = ..., 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]: ... + def grid_location(self, x: float | str, y: float | str) -> tuple[int, int]: ... @overload def grid_propagate(self, flag: bool) -> None: ... @overload @@ -541,32 +619,32 @@ class Misc: sequence: str, *, above: Misc | int = ..., - borderwidth: _ScreenUnits = ..., + borderwidth: float | str = ..., button: int = ..., count: int = ..., data: Any = ..., # anything with usable str() value delta: int = ..., detail: str = ..., focus: bool = ..., - height: _ScreenUnits = ..., + height: float | str = ..., keycode: int = ..., keysym: str = ..., mode: str = ..., override: bool = ..., place: Literal["PlaceOnTop", "PlaceOnBottom"] = ..., root: Misc | int = ..., - rootx: _ScreenUnits = ..., - rooty: _ScreenUnits = ..., + rootx: float | str = ..., + rooty: float | str = ..., sendevent: bool = ..., serial: int = ..., state: int | str = ..., subwindow: Misc | int = ..., time: int = ..., warp: bool = ..., - width: _ScreenUnits = ..., + width: float | str = ..., when: Literal["now", "tail", "head", "mark"] = ..., - x: _ScreenUnits = ..., - y: _ScreenUnits = ..., + x: float | str = ..., + y: float | str = ..., ) -> None: ... def event_info(self, virtual: str | None = None) -> tuple[str, ...]: ... def image_names(self) -> tuple[str, ...]: ... @@ -576,7 +654,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 @@ -589,23 +668,55 @@ class XView: @overload def xview(self) -> tuple[float, float]: ... @overload - def xview(self, *args): ... + def xview(self, *args) -> None: ... def xview_moveto(self, fraction: float) -> None: ... @overload def xview_scroll(self, number: int, what: Literal["units", "pages"]) -> None: ... @overload - def xview_scroll(self, number: _ScreenUnits, what: Literal["pixels"]) -> None: ... + def xview_scroll(self, number: float | str, what: Literal["pixels"]) -> None: ... class YView: @overload def yview(self) -> tuple[float, float]: ... @overload - def yview(self, *args): ... + def yview(self, *args) -> None: ... def yview_moveto(self, fraction: float) -> None: ... @overload def yview_scroll(self, number: int, what: Literal["units", "pages"]) -> None: ... @overload - def yview_scroll(self, number: _ScreenUnits, what: Literal["pixels"]) -> None: ... + def yview_scroll(self, number: float | str, what: Literal["pixels"]) -> None: ... + +if sys.platform == "darwin": + @type_check_only + class _WmAttributes(TypedDict): + alpha: float + fullscreen: bool + modified: bool + notify: bool + titlepath: str + topmost: bool + transparent: bool + type: str # Present, but not actually used on darwin + +elif sys.platform == "win32": + @type_check_only + class _WmAttributes(TypedDict): + alpha: float + transparentcolor: str + disabled: bool + fullscreen: bool + toolwindow: bool + topmost: bool + +else: + # X11 + @type_check_only + class _WmAttributes(TypedDict): + alpha: float + topmost: bool + zoomed: bool + fullscreen: bool + type: str class Wm: @overload @@ -615,12 +726,144 @@ class Wm: self, minNumer: None = None, minDenom: None = None, maxNumer: None = None, maxDenom: None = None ) -> tuple[int, int, int, int] | None: ... aspect = wm_aspect + if sys.version_info >= (3, 13): + @overload + def wm_attributes(self, *, return_python_dict: Literal[False] = False) -> tuple[Any, ...]: ... + @overload + def wm_attributes(self, *, return_python_dict: Literal[True]) -> _WmAttributes: ... + + else: + @overload + def wm_attributes(self) -> tuple[Any, ...]: ... + + @overload + def wm_attributes(self, option: Literal["-alpha"], /) -> float: ... @overload - def wm_attributes(self) -> tuple[Any, ...]: ... + def wm_attributes(self, option: Literal["-fullscreen"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-topmost"], /) -> bool: ... + if sys.platform == "darwin": + @overload + def wm_attributes(self, option: Literal["-modified"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-notify"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-titlepath"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["-transparent"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-type"], /) -> str: ... + elif sys.platform == "win32": + @overload + def wm_attributes(self, option: Literal["-transparentcolor"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["-disabled"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-toolwindow"], /) -> bool: ... + else: + # X11 + @overload + def wm_attributes(self, option: Literal["-zoomed"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["-type"], /) -> str: ... + if sys.version_info >= (3, 13): + @overload + def wm_attributes(self, option: Literal["alpha"], /) -> float: ... + @overload + def wm_attributes(self, option: Literal["fullscreen"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["topmost"], /) -> bool: ... + if sys.platform == "darwin": + @overload + def wm_attributes(self, option: Literal["modified"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["notify"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["titlepath"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["transparent"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["type"], /) -> str: ... + elif sys.platform == "win32": + @overload + def wm_attributes(self, option: Literal["transparentcolor"], /) -> str: ... + @overload + def wm_attributes(self, option: Literal["disabled"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["toolwindow"], /) -> bool: ... + else: + # X11 + @overload + def wm_attributes(self, option: Literal["zoomed"], /) -> bool: ... + @overload + def wm_attributes(self, option: Literal["type"], /) -> str: ... + @overload def wm_attributes(self, option: str, /): ... @overload - def wm_attributes(self, option: str, value, /, *__other_option_value_pairs: Any) -> None: ... + def wm_attributes(self, option: Literal["-alpha"], value: float, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-fullscreen"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-topmost"], value: bool, /) -> Literal[""]: ... + if sys.platform == "darwin": + @overload + def wm_attributes(self, option: Literal["-modified"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-notify"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-titlepath"], value: str, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-transparent"], value: bool, /) -> Literal[""]: ... + elif sys.platform == "win32": + @overload + def wm_attributes(self, option: Literal["-transparentcolor"], value: str, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-disabled"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-toolwindow"], value: bool, /) -> Literal[""]: ... + else: + # X11 + @overload + def wm_attributes(self, option: Literal["-zoomed"], value: bool, /) -> Literal[""]: ... + @overload + def wm_attributes(self, option: Literal["-type"], value: str, /) -> Literal[""]: ... + + @overload + def wm_attributes(self, option: str, value, /, *__other_option_value_pairs: Any) -> Literal[""]: ... + if sys.version_info >= (3, 13): + if sys.platform == "darwin": + @overload + def wm_attributes( + self, + *, + alpha: float = ..., + fullscreen: bool = ..., + modified: bool = ..., + notify: bool = ..., + titlepath: str = ..., + topmost: bool = ..., + transparent: bool = ..., + ) -> None: ... + elif sys.platform == "win32": + @overload + def wm_attributes( + self, + *, + alpha: float = ..., + transparentcolor: str = ..., + disabled: bool = ..., + fullscreen: bool = ..., + toolwindow: bool = ..., + topmost: bool = ..., + ) -> None: ... + else: + # X11 + @overload + def wm_attributes( + self, *, alpha: float = ..., topmost: bool = ..., zoomed: bool = ..., fullscreen: bool = ..., type: str = ... + ) -> None: ... + attributes = wm_attributes def wm_client(self, name: str | None = None) -> str: ... client = wm_client @@ -647,29 +890,23 @@ class Wm: @overload def wm_geometry(self, newGeometry: str) -> None: ... geometry = wm_geometry - def wm_grid( - self, - baseWidth: Incomplete | None = None, - baseHeight: Incomplete | None = None, - widthInc: Incomplete | None = None, - heightInc: Incomplete | None = None, - ): ... + def wm_grid(self, baseWidth=None, baseHeight=None, widthInc=None, heightInc=None): ... grid = wm_grid - def wm_group(self, pathName: Incomplete | None = None): ... + def wm_group(self, pathName=None): ... group = wm_group - def wm_iconbitmap(self, bitmap: Incomplete | None = None, default: Incomplete | None = None): ... + def wm_iconbitmap(self, bitmap=None, default=None): ... iconbitmap = wm_iconbitmap def wm_iconify(self) -> None: ... iconify = wm_iconify - def wm_iconmask(self, bitmap: Incomplete | None = None): ... + def wm_iconmask(self, bitmap=None): ... iconmask = wm_iconmask - def wm_iconname(self, newName: Incomplete | None = None) -> str: ... + def wm_iconname(self, newName=None) -> str: ... iconname = wm_iconname def wm_iconphoto(self, default: bool, image1: _PhotoImageLike | str, /, *args: _PhotoImageLike | str) -> None: ... iconphoto = wm_iconphoto def wm_iconposition(self, x: int | None = None, y: int | None = None) -> tuple[int, int] | None: ... iconposition = wm_iconposition - def wm_iconwindow(self, pathName: Incomplete | None = None): ... + def wm_iconwindow(self, pathName=None): ... iconwindow = wm_iconwindow def wm_manage(self, widget) -> None: ... manage = wm_manage @@ -736,27 +973,28 @@ class Tk(Misc, Wm): sync: bool = False, use: str | None = None, ) -> None: ... + # Keep this in sync with ttktheme.ThemedTk. See issue #13858 @overload def configure( self, cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., menu: Menu = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., - width: _ScreenUnits = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -767,47 +1005,49 @@ class Tk(Misc, Wm): # Tk has __getattr__ so that tk_instance.foo falls back to tk_instance.tk.foo # Please keep in sync with _tkinter.TkappType. # Some methods are intentionally missing because they are inherited from Misc instead. - def adderrorinfo(self, msg, /): ... + def adderrorinfo(self, msg: str, /): ... def call(self, command: Any, /, *args: Any) -> Any: ... - def createcommand(self, name, func, /): ... + def createcommand(self, name: str, func, /): ... if sys.platform != "win32": - def createfilehandler(self, file, mask, func, /): ... - def deletefilehandler(self, file, /): ... + def createfilehandler(self, file, mask: int, func, /): ... + def deletefilehandler(self, file, /) -> None: ... - def createtimerhandler(self, milliseconds, func, /): ... - def dooneevent(self, flags: int = ..., /): ... + def createtimerhandler(self, milliseconds: int, func, /): ... + def dooneevent(self, flags: int = 0, /): ... def eval(self, script: str, /) -> str: ... - def evalfile(self, fileName, /): ... - def exprboolean(self, s, /): ... - def exprdouble(self, s, /): ... - def exprlong(self, s, /): ... - def exprstring(self, s, /): ... + def evalfile(self, fileName: str, /): ... + def exprboolean(self, s: str, /): ... + def exprdouble(self, s: str, /): ... + def exprlong(self, s: str, /): ... + def exprstring(self, s: str, /): ... def globalgetvar(self, *args, **kwargs): ... def globalsetvar(self, *args, **kwargs): ... def globalunsetvar(self, *args, **kwargs): ... - def interpaddr(self): ... + def interpaddr(self) -> int: ... def loadtk(self) -> None: ... - def record(self, script, /): ... + def record(self, script: str, /): ... if sys.version_info < (3, 11): + @deprecated("Deprecated since Python 3.9; removed in Python 3.11. Use `splitlist()` instead.") def split(self, arg, /): ... def splitlist(self, arg, /): ... def unsetvar(self, *args, **kwargs): ... def wantobjects(self, *args, **kwargs): ... - def willdispatch(self): ... + def willdispatch(self) -> None: ... def Tcl(screenName: str | None = None, baseName: str | None = None, className: str = "Tk", useTk: bool = False) -> Tk: ... _InMiscTotal = TypedDict("_InMiscTotal", {"in": Misc}) _InMiscNonTotal = TypedDict("_InMiscNonTotal", {"in": Misc}, total=False) +@type_check_only class _PackInfo(_InMiscTotal): # 'before' and 'after' never appear in _PackInfo - anchor: _Anchor + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] expand: bool fill: Literal["none", "x", "y", "both"] side: Literal["left", "right", "top", "bottom"] - # Paddings come out as int or tuple of int, even though any _ScreenUnits + # Paddings come out as int or tuple of int, even though any screen units # can be specified in pack(). ipadx: int ipady: int @@ -816,7 +1056,7 @@ class _PackInfo(_InMiscTotal): class Pack: # _PackInfo is not the valid type for cnf because pad stuff accepts any - # _ScreenUnits instead of int only. I didn't bother to create another + # screen units instead of int only. I didn't bother to create another # TypedDict for cnf because it appears to be a legacy thing that was # replaced by **kwargs. def pack_configure( @@ -824,15 +1064,15 @@ class Pack: cnf: Mapping[str, Any] | None = {}, *, after: Misc = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., before: Misc = ..., expand: bool | Literal[0, 1] = 0, fill: Literal["none", "x", "y", "both"] = ..., side: Literal["left", "right", "top", "bottom"] = ..., - ipadx: _ScreenUnits = ..., - ipady: _ScreenUnits = ..., - padx: _ScreenUnits | tuple[_ScreenUnits, _ScreenUnits] = ..., - pady: _ScreenUnits | tuple[_ScreenUnits, _ScreenUnits] = ..., + ipadx: float | str = ..., + ipady: float | str = ..., + padx: float | str | tuple[float | str, float | str] = ..., + pady: float | str | tuple[float | str, float | str] = ..., in_: Misc = ..., **kw: Any, # allow keyword argument named 'in', see #4836 ) -> None: ... @@ -842,8 +1082,9 @@ class Pack: forget = pack_forget propagate = Misc.pack_propagate +@type_check_only class _PlaceInfo(_InMiscNonTotal): # empty dict if widget hasn't been placed - anchor: _Anchor + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] bordermode: Literal["inside", "outside", "ignore"] width: str # can be int()ed (even after e.g. widget.place(height='2.3c') or similar) height: str # can be int()ed @@ -859,12 +1100,12 @@ class Place: self, cnf: Mapping[str, Any] | None = {}, *, - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., bordermode: Literal["inside", "outside", "ignore"] = ..., - width: _ScreenUnits = ..., - height: _ScreenUnits = ..., - x: _ScreenUnits = ..., - y: _ScreenUnits = ..., + width: float | str = ..., + height: float | str = ..., + x: float | str = ..., + y: float | str = ..., # str allowed for compatibility with place_info() relheight: str | float = ..., relwidth: str | float = ..., @@ -878,6 +1119,7 @@ class Place: place = place_configure info = place_info +@type_check_only class _GridInfo(_InMiscNonTotal): # empty dict if widget hasn't been gridded column: int columnspan: int @@ -898,10 +1140,10 @@ class Grid: columnspan: int = ..., row: int = ..., rowspan: int = ..., - ipadx: _ScreenUnits = ..., - ipady: _ScreenUnits = ..., - padx: _ScreenUnits | tuple[_ScreenUnits, _ScreenUnits] = ..., - pady: _ScreenUnits | tuple[_ScreenUnits, _ScreenUnits] = ..., + ipadx: float | str = ..., + ipady: float | str = ..., + padx: float | str | tuple[float | str, float | str] = ..., + pady: float | str | tuple[float | str, float | str] = ..., sticky: str = ..., # consists of letters 'n', 's', 'w', 'e', may contain repeats, may be empty in_: Misc = ..., **kw: Any, # allow keyword argument named 'in', see #4836 @@ -915,8 +1157,8 @@ class Grid: class BaseWidget(Misc): master: Misc - widgetName: Incomplete - def __init__(self, master, widgetName, cnf={}, kw={}, extra=()) -> None: ... + widgetName: str + def __init__(self, master, widgetName: str, cnf={}, kw={}, extra=()) -> None: ... def destroy(self) -> None: ... # This class represents any widget except Toplevel or Tk. @@ -946,28 +1188,28 @@ class Toplevel(BaseWidget, Wm): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = 0, + bd: float | str = 0, bg: str = ..., - border: _ScreenUnits = 0, - borderwidth: _ScreenUnits = 0, + border: float | str = 0, + borderwidth: float | str = 0, class_: str = "Toplevel", colormap: Literal["new", ""] | Misc = "", container: bool = False, cursor: _Cursor = "", - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, + highlightthickness: float | str = 0, menu: Menu = ..., name: str = ..., - padx: _ScreenUnits = 0, - pady: _ScreenUnits = 0, - relief: _Relief = "flat", + padx: float | str = 0, + pady: float | str = 0, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", screen: str = "", # can't be changed after creating widget - takefocus: _TakeFocusValue = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, use: int = ..., visual: str | tuple[str, int] = "", - width: _ScreenUnits = 0, + width: float | str = 0, ) -> None: ... @overload def configure( @@ -975,21 +1217,21 @@ class Toplevel(BaseWidget, Wm): cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., menu: Menu = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., - width: _ScreenUnits = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -1003,15 +1245,15 @@ class Button(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = "center", + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center", background: str = ..., - bd: _ScreenUnits = ..., # same as borderwidth + bd: float | str = ..., # same as borderwidth bg: str = ..., # same as background bitmap: str = "", - border: _ScreenUnits = ..., # same as borderwidth - borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = "", - compound: _Compound = "none", + border: float | str = ..., # same as borderwidth + borderwidth: float | str = ..., + command: str | Callable[[], Any] = "", + compound: Literal["top", "left", "center", "right", "bottom", "none"] = "none", cursor: _Cursor = "", default: Literal["normal", "active", "disabled"] = "disabled", disabledforeground: str = ..., @@ -1019,30 +1261,30 @@ class Button(Widget): font: _FontDescription = "TkDefaultFont", foreground: str = ..., # width and height must be int for buttons containing just text, but - # ints are also valid _ScreenUnits - height: _ScreenUnits = 0, + # buttons with an image accept any screen units. + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 1, - image: _ImageSpec = "", + highlightthickness: float | str = 1, + image: _Image | str = "", justify: Literal["left", "center", "right"] = "center", name: str = ..., - overrelief: _Relief | Literal[""] = "", - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + overrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove", ""] = "", + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., repeatdelay: int = ..., repeatinterval: int = ..., state: Literal["normal", "active", "disabled"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", text: float | str = "", # We allow the textvariable to be any Variable, not necessarily # StringVar. This is useful for e.g. a button that displays the value # of an IntVar. textvariable: Variable = ..., underline: int = -1, - width: _ScreenUnits = 0, - wraplength: _ScreenUnits = 0, + width: float | str = 0, + wraplength: float | str = 0, ) -> None: ... @overload def configure( @@ -1051,40 +1293,40 @@ class Button(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = ..., - compound: _Compound = ..., + border: float | str = ..., + borderwidth: float | str = ..., + command: str | Callable[[], Any] = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., cursor: _Cursor = ..., default: Literal["normal", "active", "disabled"] = ..., disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., + highlightthickness: float | str = ..., + image: _Image | str = ..., justify: Literal["left", "center", "right"] = ..., - overrelief: _Relief | Literal[""] = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + overrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove", ""] = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., repeatdelay: int = ..., repeatinterval: int = ..., state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: Variable = ..., underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: float | str = ..., + wraplength: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -1099,41 +1341,39 @@ class Canvas(Widget, XView, YView): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = 0, + bd: float | str = 0, bg: str = ..., - border: _ScreenUnits = 0, - borderwidth: _ScreenUnits = 0, + border: float | str = 0, + borderwidth: float | str = 0, closeenough: float = 1.0, confine: bool = True, cursor: _Cursor = "", - # canvas manual page has a section named COORDINATES, and the first - # part of it describes _ScreenUnits. - height: _ScreenUnits = ..., + height: float | str = ..., # see COORDINATES in canvas manual page highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = 0, + insertborderwidth: float | str = 0, insertofftime: int = 300, insertontime: int = 600, - insertwidth: _ScreenUnits = 2, + insertwidth: float | str = 2, name: str = ..., offset=..., # undocumented - relief: _Relief = "flat", + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", # Setting scrollregion to None doesn't reset it back to empty, # but setting it to () does. - scrollregion: tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits] | tuple[()] = (), + scrollregion: tuple[float | str, float | str, float | str, float | str] | tuple[()] = (), selectbackground: str = ..., - selectborderwidth: _ScreenUnits = 1, + selectborderwidth: float | str = 1, selectforeground: str = ..., # man page says that state can be 'hidden', but it can't state: Literal["normal", "disabled"] = "normal", - takefocus: _TakeFocusValue = "", - width: _ScreenUnits = ..., - xscrollcommand: _XYScrollCommand = "", - xscrollincrement: _ScreenUnits = 0, - yscrollcommand: _XYScrollCommand = "", - yscrollincrement: _ScreenUnits = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", + width: float | str = ..., + xscrollcommand: str | Callable[[float, float], object] = "", + xscrollincrement: float | str = 0, + yscrollcommand: str | Callable[[float, float], object] = "", + yscrollincrement: float | str = 0, ) -> None: ... @overload def configure( @@ -1141,35 +1381,35 @@ class Canvas(Widget, XView, YView): cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., closeenough: float = ..., confine: bool = ..., cursor: _Cursor = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., + insertborderwidth: float | str = ..., insertofftime: int = ..., insertontime: int = ..., - insertwidth: _ScreenUnits = ..., + insertwidth: float | str = ..., offset=..., # undocumented - relief: _Relief = ..., - scrollregion: tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits] | tuple[()] = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + scrollregion: tuple[float | str, float | str, float | str, float | str] | tuple[()] = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., state: Literal["normal", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - width: _ScreenUnits = ..., - xscrollcommand: _XYScrollCommand = ..., - xscrollincrement: _ScreenUnits = ..., - yscrollcommand: _XYScrollCommand = ..., - yscrollincrement: _ScreenUnits = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + width: float | str = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., + xscrollincrement: float | str = ..., + yscrollcommand: str | Callable[[float, float], object] = ..., + yscrollincrement: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -1179,20 +1419,20 @@ class Canvas(Widget, XView, YView): def addtag_all(self, newtag: str) -> None: ... def addtag_below(self, newtag: str, tagOrId: str | int) -> None: ... def addtag_closest( - self, newtag: str, x: _ScreenUnits, y: _ScreenUnits, halo: _ScreenUnits | None = None, start: str | int | None = None + self, newtag: str, x: float | str, y: float | str, halo: float | str | None = None, start: str | int | None = None ) -> None: ... - def addtag_enclosed(self, newtag: str, x1: _ScreenUnits, y1: _ScreenUnits, x2: _ScreenUnits, y2: _ScreenUnits) -> None: ... - def addtag_overlapping(self, newtag: str, x1: _ScreenUnits, y1: _ScreenUnits, x2: _ScreenUnits, y2: _ScreenUnits) -> None: ... + def addtag_enclosed(self, newtag: str, x1: float | str, y1: float | str, x2: float | str, y2: float | str) -> None: ... + def addtag_overlapping(self, newtag: str, x1: float | str, y1: float | str, x2: float | str, y2: float | str) -> None: ... def addtag_withtag(self, newtag: str, tagOrId: str | int) -> None: ... def find(self, *args): ... # internal method def find_above(self, tagOrId: str | int) -> tuple[int, ...]: ... def find_all(self) -> tuple[int, ...]: ... def find_below(self, tagOrId: str | int) -> tuple[int, ...]: ... def find_closest( - self, x: _ScreenUnits, y: _ScreenUnits, halo: _ScreenUnits | None = None, start: str | int | None = None + self, x: float | str, y: float | str, halo: float | str | None = None, start: str | int | None = None ) -> tuple[int, ...]: ... - def find_enclosed(self, x1: _ScreenUnits, y1: _ScreenUnits, x2: _ScreenUnits, y2: _ScreenUnits) -> tuple[int, ...]: ... - def find_overlapping(self, x1: _ScreenUnits, y1: _ScreenUnits, x2: _ScreenUnits, y2: float) -> tuple[int, ...]: ... + def find_enclosed(self, x1: float | str, y1: float | str, x2: float | str, y2: float | str) -> tuple[int, ...]: ... + def find_overlapping(self, x1: float | str, y1: float | str, x2: float | str, y2: float) -> tuple[int, ...]: ... def find_withtag(self, tagOrId: str | int) -> tuple[int, ...]: ... # Incompatible with Misc.bbox(), tkinter violates LSP def bbox(self, *args: str | int) -> tuple[int, int, int, int]: ... # type: ignore[override] @@ -1211,8 +1451,8 @@ class Canvas(Widget, XView, YView): @overload def tag_bind(self, tagOrId: str | int, *, func: str, add: Literal["", "+"] | bool | None = None) -> None: ... def tag_unbind(self, tagOrId: str | int, sequence: str, funcid: str | None = None) -> None: ... - def canvasx(self, screenx, gridspacing: Incomplete | None = None): ... - def canvasy(self, screeny, gridspacing: Incomplete | None = None): ... + def canvasx(self, screenx, gridspacing=None): ... + def canvasy(self, screeny, gridspacing=None): ... @overload def coords(self, tagOrId: str | int, /) -> list[float]: ... @overload @@ -1237,25 +1477,25 @@ class Canvas(Widget, XView, YView): activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., arrow: Literal["first", "last", "both"] = ..., arrowshape: tuple[float, float, float] = ..., capstyle: Literal["round", "projecting", "butt"] = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., joinstyle: Literal["round", "bevel", "miter"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., smooth: bool = ..., splinesteps: float = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_line( @@ -1267,25 +1507,25 @@ class Canvas(Widget, XView, YView): activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., arrow: Literal["first", "last", "both"] = ..., arrowshape: tuple[float, float, float] = ..., capstyle: Literal["round", "projecting", "butt"] = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., joinstyle: Literal["round", "bevel", "miter"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., smooth: bool = ..., splinesteps: float = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_line( @@ -1303,25 +1543,25 @@ class Canvas(Widget, XView, YView): activedash: str | int | list[int] | tuple[int, ...] = ..., activefill: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., arrow: Literal["first", "last", "both"] = ..., arrowshape: tuple[float, float, float] = ..., capstyle: Literal["round", "projecting", "butt"] = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., joinstyle: Literal["round", "bevel", "miter"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., smooth: bool = ..., splinesteps: float = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_oval( @@ -1337,24 +1577,24 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_oval( @@ -1368,24 +1608,24 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_oval( @@ -1405,24 +1645,24 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_polygon( @@ -1438,27 +1678,27 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., joinstyle: Literal["round", "bevel", "miter"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., smooth: bool = ..., splinesteps: float = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_polygon( @@ -1472,27 +1712,27 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., joinstyle: Literal["round", "bevel", "miter"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., smooth: bool = ..., splinesteps: float = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_polygon( @@ -1512,27 +1752,27 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., joinstyle: Literal["round", "bevel", "miter"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., smooth: bool = ..., splinesteps: float = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_rectangle( @@ -1548,24 +1788,24 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_rectangle( @@ -1579,24 +1819,24 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_rectangle( @@ -1616,24 +1856,24 @@ class Canvas(Widget, XView, YView): activeoutline: str = ..., activeoutlinestipple: str = ..., activestipple: str = ..., - activewidth: _ScreenUnits = ..., + activewidth: float | str = ..., dash: str | int | list[int] | tuple[int, ...] = ..., - dashoffset: _ScreenUnits = ..., + dashoffset: float | str = ..., disableddash: str | int | list[int] | tuple[int, ...] = ..., disabledfill: str = ..., disabledoutline: str = ..., disabledoutlinestipple: str = ..., disabledstipple: str = ..., - disabledwidth: _ScreenUnits = ..., + disabledwidth: float | str = ..., fill: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., outline: str = ..., - outlineoffset: _ScreenUnits = ..., + outlineoffset: float | str = ..., outlinestipple: str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_text( @@ -1644,19 +1884,19 @@ class Canvas(Widget, XView, YView): *, activefill: str = ..., activestipple: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., angle: float | str = ..., disabledfill: str = ..., disabledstipple: str = ..., fill: str = ..., font: _FontDescription = ..., justify: Literal["left", "center", "right"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., text: float | str = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_text( @@ -1666,19 +1906,19 @@ class Canvas(Widget, XView, YView): *, activefill: str = ..., activestipple: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., angle: float | str = ..., disabledfill: str = ..., disabledstipple: str = ..., fill: str = ..., font: _FontDescription = ..., justify: Literal["left", "center", "right"] = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., state: Literal["normal", "hidden", "disabled"] = ..., stipple: str = ..., tags: str | list[str] | tuple[str, ...] = ..., text: float | str = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> int: ... @overload def create_window( @@ -1687,11 +1927,11 @@ class Canvas(Widget, XView, YView): y: float, /, *, - anchor: _Anchor = ..., - height: _ScreenUnits = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., + height: float | str = ..., state: Literal["normal", "hidden", "disabled"] = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., window: Widget = ..., ) -> int: ... @overload @@ -1700,11 +1940,11 @@ class Canvas(Widget, XView, YView): coords: tuple[float, float] | list[int] | list[float], /, *, - anchor: _Anchor = ..., - height: _ScreenUnits = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., + height: float | str = ..., state: Literal["normal", "hidden", "disabled"] = ..., tags: str | list[str] | tuple[str, ...] = ..., - width: _ScreenUnits = ..., + width: float | str = ..., window: Widget = ..., ) -> int: ... def dchars(self, *args) -> None: ... @@ -1737,9 +1977,7 @@ class Canvas(Widget, XView, YView): def tag_raise(self, first: str | int, second: str | int | None = ..., /) -> None: ... def tkraise(self, first: str | int, second: str | int | None = ..., /) -> None: ... # type: ignore[override] def lift(self, first: str | int, second: str | int | None = ..., /) -> None: ... # type: ignore[override] - def scale( - self, tagOrId: str | int, xOrigin: _ScreenUnits, yOrigin: _ScreenUnits, xScale: float, yScale: float, / - ) -> None: ... + def scale(self, tagOrId: str | int, xOrigin: float | str, yOrigin: float | str, xScale: float, yScale: float, /) -> None: ... def scan_mark(self, x, y) -> None: ... def scan_dragto(self, x, y, gain: int = 10) -> None: ... def select_adjust(self, tagOrId, index) -> None: ... @@ -1757,29 +1995,29 @@ class Checkbutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = "center", + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center", background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = "", - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = "", - compound: _Compound = "none", + border: float | str = ..., + borderwidth: float | str = ..., + command: str | Callable[[], Any] = "", + compound: Literal["top", "left", "center", "right", "bottom", "none"] = "none", cursor: _Cursor = "", disabledforeground: str = ..., fg: str = ..., font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 1, - image: _ImageSpec = "", + highlightthickness: float | str = 1, + image: _Image | str = "", indicatoron: bool = True, justify: Literal["left", "center", "right"] = "center", name: str = ..., - offrelief: _Relief = ..., + offrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., # The checkbutton puts a value to its variable when it's checked or # unchecked. We don't restrict the type of that value here, so # Any-typing is fine. @@ -1792,22 +2030,22 @@ class Checkbutton(Widget): # done by setting variable to empty string (the default). offvalue: Any = 0, onvalue: Any = 1, - overrelief: _Relief | Literal[""] = "", - padx: _ScreenUnits = 1, - pady: _ScreenUnits = 1, - relief: _Relief = "flat", + overrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove", ""] = "", + padx: float | str = 1, + pady: float | str = 1, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", selectcolor: str = ..., - selectimage: _ImageSpec = "", + selectimage: _Image | str = "", state: Literal["normal", "active", "disabled"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", text: float | str = "", textvariable: Variable = ..., - tristateimage: _ImageSpec = "", + tristateimage: _Image | str = "", tristatevalue: Any = "", underline: int = -1, variable: Variable | Literal[""] = ..., - width: _ScreenUnits = 0, - wraplength: _ScreenUnits = 0, + width: float | str = 0, + wraplength: float | str = 0, ) -> None: ... @overload def configure( @@ -1816,46 +2054,46 @@ class Checkbutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = ..., - compound: _Compound = ..., + border: float | str = ..., + borderwidth: float | str = ..., + command: str | Callable[[], Any] = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., cursor: _Cursor = ..., disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., + highlightthickness: float | str = ..., + image: _Image | str = ..., indicatoron: bool = ..., justify: Literal["left", "center", "right"] = ..., - offrelief: _Relief = ..., + offrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., offvalue: Any = ..., onvalue: Any = ..., - overrelief: _Relief | Literal[""] = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + overrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove", ""] = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectcolor: str = ..., - selectimage: _ImageSpec = ..., + selectimage: _Image | str = ..., state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: Variable = ..., - tristateimage: _ImageSpec = ..., + tristateimage: _Image | str = ..., tristatevalue: Any = ..., underline: int = ..., variable: Variable | Literal[""] = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: float | str = ..., + wraplength: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -1873,10 +2111,10 @@ class Entry(Widget, XView): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = "xterm", disabledbackground: str = ..., disabledforeground: str = ..., @@ -1886,30 +2124,30 @@ class Entry(Widget, XView): foreground: str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = 0, + insertborderwidth: float | str = 0, insertofftime: int = 300, insertontime: int = 600, - insertwidth: _ScreenUnits = ..., - invalidcommand: _EntryValidateCommand = "", - invcmd: _EntryValidateCommand = "", # same as invalidcommand + insertwidth: float | str = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", + invcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", # same as invalidcommand justify: Literal["left", "center", "right"] = "left", name: str = ..., readonlybackground: str = ..., - relief: _Relief = "sunken", + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "sunken", selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., show: str = "", state: Literal["normal", "disabled", "readonly"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", textvariable: Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", - validatecommand: _EntryValidateCommand = "", - vcmd: _EntryValidateCommand = "", # same as validatecommand + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", + vcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", # same as validatecommand width: int = 20, - xscrollcommand: _XYScrollCommand = "", + xscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload def configure( @@ -1917,10 +2155,10 @@ class Entry(Widget, XView): cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., disabledbackground: str = ..., disabledforeground: str = ..., @@ -1930,29 +2168,29 @@ class Entry(Widget, XView): foreground: str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., + insertborderwidth: float | str = ..., insertofftime: int = ..., insertontime: int = ..., - insertwidth: _ScreenUnits = ..., - invalidcommand: _EntryValidateCommand = ..., - invcmd: _EntryValidateCommand = ..., + insertwidth: float | str = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., + invcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., readonlybackground: str = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., show: str = ..., state: Literal["normal", "disabled", "readonly"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: _EntryValidateCommand = ..., - vcmd: _EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., + vcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., width: int = ..., - xscrollcommand: _XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -1984,25 +2222,25 @@ class Frame(Widget): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = 0, + bd: float | str = 0, bg: str = ..., - border: _ScreenUnits = 0, - borderwidth: _ScreenUnits = 0, + border: float | str = 0, + borderwidth: float | str = 0, class_: str = "Frame", # can't be changed with configure() colormap: Literal["new", ""] | Misc = "", # can't be changed with configure() container: bool = False, # can't be changed with configure() cursor: _Cursor = "", - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, + highlightthickness: float | str = 0, name: str = ..., - padx: _ScreenUnits = 0, - pady: _ScreenUnits = 0, - relief: _Relief = "flat", - takefocus: _TakeFocusValue = 0, + padx: float | str = 0, + pady: float | str = 0, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, visual: str | tuple[str, int] = "", # can't be changed with configure() - width: _ScreenUnits = 0, + width: float | str = 0, ) -> None: ... @overload def configure( @@ -2010,20 +2248,20 @@ class Frame(Widget): cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., - width: _ScreenUnits = ..., + highlightthickness: float | str = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2037,36 +2275,36 @@ class Label(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = "center", + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center", background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = "", - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - compound: _Compound = "none", + border: float | str = ..., + borderwidth: float | str = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = "none", cursor: _Cursor = "", disabledforeground: str = ..., fg: str = ..., font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, - image: _ImageSpec = "", + highlightthickness: float | str = 0, + image: _Image | str = "", justify: Literal["left", "center", "right"] = "center", name: str = ..., - padx: _ScreenUnits = 1, - pady: _ScreenUnits = 1, - relief: _Relief = "flat", + padx: float | str = 1, + pady: float | str = 1, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", state: Literal["normal", "active", "disabled"] = "normal", - takefocus: _TakeFocusValue = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, text: float | str = "", textvariable: Variable = ..., underline: int = -1, - width: _ScreenUnits = 0, - wraplength: _ScreenUnits = 0, + width: float | str = 0, + wraplength: float | str = 0, ) -> None: ... @overload def configure( @@ -2075,35 +2313,35 @@ class Label(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - compound: _Compound = ..., + border: float | str = ..., + borderwidth: float | str = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., cursor: _Cursor = ..., disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., + highlightthickness: float | str = ..., + image: _Image | str = ..., justify: Literal["left", "center", "right"] = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: Variable = ..., underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: float | str = ..., + wraplength: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2117,10 +2355,10 @@ class Listbox(Widget, XView, YView): *, activestyle: Literal["dotbox", "none", "underline"] = ..., background: str = ..., - bd: _ScreenUnits = 1, + bd: float | str = 1, bg: str = ..., - border: _ScreenUnits = 1, - borderwidth: _ScreenUnits = 1, + border: float | str = 1, + borderwidth: float | str = 1, cursor: _Cursor = "", disabledforeground: str = ..., exportselection: bool | Literal[0, 1] = 1, @@ -2130,7 +2368,7 @@ class Listbox(Widget, XView, YView): height: int = 10, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., justify: Literal["left", "center", "right"] = "left", # There's no tkinter.ListVar, but seems like bare tkinter.Variable # actually works for this: @@ -2143,9 +2381,9 @@ class Listbox(Widget, XView, YView): # ('foo', 'bar', 'baz') listvariable: Variable = ..., name: str = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = 0, + selectborderwidth: float | str = 0, selectforeground: str = ..., # from listbox man page: "The value of the [selectmode] option may be # arbitrary, but the default bindings expect it to be either single, @@ -2156,10 +2394,10 @@ class Listbox(Widget, XView, YView): selectmode: str | Literal["single", "browse", "multiple", "extended"] = "browse", # noqa: Y051 setgrid: bool = False, state: Literal["normal", "disabled"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", width: int = 20, - xscrollcommand: _XYScrollCommand = "", - yscrollcommand: _XYScrollCommand = "", + xscrollcommand: str | Callable[[float, float], object] = "", + yscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload def configure( @@ -2168,10 +2406,10 @@ class Listbox(Widget, XView, YView): *, activestyle: Literal["dotbox", "none", "underline"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., disabledforeground: str = ..., exportselection: bool = ..., @@ -2181,20 +2419,20 @@ class Listbox(Widget, XView, YView): height: int = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., justify: Literal["left", "center", "right"] = ..., listvariable: Variable = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., selectmode: str | Literal["single", "browse", "multiple", "extended"] = ..., # noqa: Y051 setgrid: bool = ..., state: Literal["normal", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., width: int = ..., - xscrollcommand: _XYScrollCommand = ..., - yscrollcommand: _XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., + yscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2220,7 +2458,7 @@ class Listbox(Widget, XView, YView): select_set = selection_set def size(self) -> int: ... # type: ignore[override] def itemcget(self, index: str | int, option): ... - def itemconfigure(self, index: str | int, cnf: Incomplete | None = None, **kw): ... + def itemconfigure(self, index: str | int, cnf=None, **kw): ... itemconfig = itemconfigure class Menu(Widget): @@ -2230,13 +2468,13 @@ class Menu(Widget): cnf: dict[str, Any] | None = {}, *, activebackground: str = ..., - activeborderwidth: _ScreenUnits = ..., + activeborderwidth: float | str = ..., activeforeground: str = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = "arrow", disabledforeground: str = ..., fg: str = ..., @@ -2244,9 +2482,9 @@ class Menu(Widget): foreground: str = ..., name: str = ..., postcommand: Callable[[], object] | str = "", - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectcolor: str = ..., - takefocus: _TakeFocusValue = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, tearoff: bool | Literal[0, 1] = 1, # I guess tearoffcommand arguments are supposed to be widget objects, # but they are widget name strings. Use nametowidget() to handle the @@ -2261,22 +2499,22 @@ class Menu(Widget): cnf: dict[str, Any] | None = None, *, activebackground: str = ..., - activeborderwidth: _ScreenUnits = ..., + activeborderwidth: float | str = ..., activeforeground: str = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., postcommand: Callable[[], object] | str = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectcolor: str = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., tearoff: bool = ..., tearoffcommand: Callable[[str, str], object] | str = ..., title: str = ..., @@ -2300,11 +2538,11 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., label: str = ..., menu: Menu = ..., state: Literal["normal", "active", "disabled"] = ..., @@ -2321,17 +2559,17 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., indicatoron: bool = ..., label: str = ..., offvalue: Any = ..., onvalue: Any = ..., selectcolor: str = ..., - selectimage: _ImageSpec = ..., + selectimage: _Image | str = ..., state: Literal["normal", "active", "disabled"] = ..., underline: int = ..., variable: Variable = ..., @@ -2347,11 +2585,11 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., label: str = ..., state: Literal["normal", "active", "disabled"] = ..., underline: int = ..., @@ -2367,15 +2605,15 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., indicatoron: bool = ..., label: str = ..., selectcolor: str = ..., - selectimage: _ImageSpec = ..., + selectimage: _Image | str = ..., state: Literal["normal", "active", "disabled"] = ..., underline: int = ..., value: Any = ..., @@ -2394,11 +2632,11 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., label: str = ..., menu: Menu = ..., state: Literal["normal", "active", "disabled"] = ..., @@ -2416,17 +2654,17 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., indicatoron: bool = ..., label: str = ..., offvalue: Any = ..., onvalue: Any = ..., selectcolor: str = ..., - selectimage: _ImageSpec = ..., + selectimage: _Image | str = ..., state: Literal["normal", "active", "disabled"] = ..., underline: int = ..., variable: Variable = ..., @@ -2443,11 +2681,11 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., label: str = ..., state: Literal["normal", "active", "disabled"] = ..., underline: int = ..., @@ -2464,15 +2702,15 @@ class Menu(Widget): bitmap: str = ..., columnbreak: int = ..., command: Callable[[], object] | str = ..., - compound: _Compound = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., font: _FontDescription = ..., foreground: str = ..., hidemargin: bool = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., indicatoron: bool = ..., label: str = ..., selectcolor: str = ..., - selectimage: _ImageSpec = ..., + selectimage: _Image | str = ..., state: Literal["normal", "active", "disabled"] = ..., underline: int = ..., value: Any = ..., @@ -2501,39 +2739,39 @@ class Menubutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = "", - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - compound: _Compound = "none", + border: float | str = ..., + borderwidth: float | str = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = "none", cursor: _Cursor = "", direction: Literal["above", "below", "left", "right", "flush"] = "below", disabledforeground: str = ..., fg: str = ..., font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, - image: _ImageSpec = "", + highlightthickness: float | str = 0, + image: _Image | str = "", indicatoron: bool = ..., justify: Literal["left", "center", "right"] = ..., menu: Menu = ..., name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = "flat", + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", state: Literal["normal", "active", "disabled"] = "normal", - takefocus: _TakeFocusValue = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, text: float | str = "", textvariable: Variable = ..., underline: int = -1, - width: _ScreenUnits = 0, - wraplength: _ScreenUnits = 0, + width: float | str = 0, + wraplength: float | str = 0, ) -> None: ... @overload def configure( @@ -2542,38 +2780,38 @@ class Menubutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - compound: _Compound = ..., + border: float | str = ..., + borderwidth: float | str = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., cursor: _Cursor = ..., direction: Literal["above", "below", "left", "right", "flush"] = ..., disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., + highlightthickness: float | str = ..., + image: _Image | str = ..., indicatoron: bool = ..., justify: Literal["left", "center", "right"] = ..., menu: Menu = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: Variable = ..., underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: float | str = ..., + wraplength: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2585,58 +2823,58 @@ class Message(Widget): master: Misc | None = None, cnf: dict[str, Any] | None = {}, *, - anchor: _Anchor = "center", + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center", aspect: int = 150, background: str = ..., - bd: _ScreenUnits = 1, + bd: float | str = 1, bg: str = ..., - border: _ScreenUnits = 1, - borderwidth: _ScreenUnits = 1, + border: float | str = 1, + borderwidth: float | str = 1, cursor: _Cursor = "", fg: str = ..., font: _FontDescription = "TkDefaultFont", foreground: str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, + highlightthickness: float | str = 0, justify: Literal["left", "center", "right"] = "left", name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = "flat", - takefocus: _TakeFocusValue = 0, + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, text: float | str = "", textvariable: Variable = ..., # there's width but no height - width: _ScreenUnits = 0, + width: float | str = 0, ) -> None: ... @overload def configure( self, cnf: dict[str, Any] | None = None, *, - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., aspect: int = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., justify: Literal["left", "center", "right"] = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: Variable = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2650,46 +2888,46 @@ class Radiobutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = "center", + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center", background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = "", - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = "", - compound: _Compound = "none", + border: float | str = ..., + borderwidth: float | str = ..., + command: str | Callable[[], Any] = "", + compound: Literal["top", "left", "center", "right", "bottom", "none"] = "none", cursor: _Cursor = "", disabledforeground: str = ..., fg: str = ..., font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 1, - image: _ImageSpec = "", + highlightthickness: float | str = 1, + image: _Image | str = "", indicatoron: bool = True, justify: Literal["left", "center", "right"] = "center", name: str = ..., - offrelief: _Relief = ..., - overrelief: _Relief | Literal[""] = "", - padx: _ScreenUnits = 1, - pady: _ScreenUnits = 1, - relief: _Relief = "flat", + offrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + overrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove", ""] = "", + padx: float | str = 1, + pady: float | str = 1, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", selectcolor: str = ..., - selectimage: _ImageSpec = "", + selectimage: _Image | str = "", state: Literal["normal", "active", "disabled"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", text: float | str = "", textvariable: Variable = ..., - tristateimage: _ImageSpec = "", + tristateimage: _Image | str = "", tristatevalue: Any = "", underline: int = -1, value: Any = "", variable: Variable | Literal[""] = ..., - width: _ScreenUnits = 0, - wraplength: _ScreenUnits = 0, + width: float | str = 0, + wraplength: float | str = 0, ) -> None: ... @overload def configure( @@ -2698,45 +2936,45 @@ class Radiobutton(Widget): *, activebackground: str = ..., activeforeground: str = ..., - anchor: _Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bitmap: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., - command: _ButtonCommand = ..., - compound: _Compound = ..., + border: float | str = ..., + borderwidth: float | str = ..., + command: str | Callable[[], Any] = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., cursor: _Cursor = ..., disabledforeground: str = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., + highlightthickness: float | str = ..., + image: _Image | str = ..., indicatoron: bool = ..., justify: Literal["left", "center", "right"] = ..., - offrelief: _Relief = ..., - overrelief: _Relief | Literal[""] = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + offrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + overrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove", ""] = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectcolor: str = ..., - selectimage: _ImageSpec = ..., + selectimage: _Image | str = ..., state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: Variable = ..., - tristateimage: _ImageSpec = ..., + tristateimage: _Image | str = ..., tristatevalue: Any = ..., underline: int = ..., value: Any = ..., variable: Variable | Literal[""] = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., + width: float | str = ..., + wraplength: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2754,11 +2992,11 @@ class Scale(Widget): *, activebackground: str = ..., background: str = ..., - bd: _ScreenUnits = 1, + bd: float | str = 1, bg: str = ..., bigincrement: float = 0.0, - border: _ScreenUnits = 1, - borderwidth: _ScreenUnits = 1, + border: float | str = 1, + borderwidth: float | str = 1, # don't know why the callback gets string instead of float command: str | Callable[[str], object] = "", cursor: _Cursor = "", @@ -2769,25 +3007,25 @@ class Scale(Widget): from_: float = 0.0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., label: str = "", - length: _ScreenUnits = 100, + length: float | str = 100, name: str = ..., orient: Literal["horizontal", "vertical"] = "vertical", - relief: _Relief = "flat", + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", repeatdelay: int = 300, repeatinterval: int = 100, resolution: float = 1.0, showvalue: bool = True, - sliderlength: _ScreenUnits = 30, - sliderrelief: _Relief = "raised", + sliderlength: float | str = 30, + sliderrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "raised", state: Literal["normal", "active", "disabled"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", tickinterval: float = 0.0, to: float = 100.0, troughcolor: str = ..., variable: IntVar | DoubleVar = ..., - width: _ScreenUnits = 15, + width: float | str = 15, ) -> None: ... @overload def configure( @@ -2796,11 +3034,11 @@ class Scale(Widget): *, activebackground: str = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., bigincrement: float = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., command: str | Callable[[str], object] = ..., cursor: _Cursor = ..., digits: int = ..., @@ -2810,24 +3048,24 @@ class Scale(Widget): from_: float = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., label: str = ..., - length: _ScreenUnits = ..., + length: float | str = ..., orient: Literal["horizontal", "vertical"] = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., repeatdelay: int = ..., repeatinterval: int = ..., resolution: float = ..., showvalue: bool = ..., - sliderlength: _ScreenUnits = ..., - sliderrelief: _Relief = ..., + sliderlength: float | str = ..., + sliderrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., tickinterval: float = ..., to: float = ..., troughcolor: str = ..., variable: IntVar | DoubleVar = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -2844,31 +3082,31 @@ class Scrollbar(Widget): cnf: dict[str, Any] | None = {}, *, activebackground: str = ..., - activerelief: _Relief = "raised", + activerelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "raised", background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., # There are many ways how the command may get called. Search for # 'SCROLLING COMMANDS' in scrollbar man page. There doesn't seem to # be any way to specify an overloaded callback function, so we say # that it can take any args while it can't in reality. command: Callable[..., tuple[float, float] | None] | str = "", cursor: _Cursor = "", - elementborderwidth: _ScreenUnits = -1, + elementborderwidth: float | str = -1, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, + highlightthickness: float | str = 0, jump: bool = False, name: str = ..., orient: Literal["horizontal", "vertical"] = "vertical", - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., repeatdelay: int = 300, repeatinterval: int = 100, - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", troughcolor: str = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> None: ... @overload def configure( @@ -2876,38 +3114,37 @@ class Scrollbar(Widget): cnf: dict[str, Any] | None = None, *, activebackground: str = ..., - activerelief: _Relief = ..., + activerelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., command: Callable[..., tuple[float, float] | None] | str = ..., cursor: _Cursor = ..., - elementborderwidth: _ScreenUnits = ..., + elementborderwidth: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., jump: bool = ..., orient: Literal["horizontal", "vertical"] = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., repeatdelay: int = ..., repeatinterval: int = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., troughcolor: str = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... config = configure - def activate(self, index: Incomplete | None = None): ... + def activate(self, index=None): ... def delta(self, deltax: int, deltay: int) -> float: ... def fraction(self, x: int, y: int) -> float: ... def identify(self, x: int, y: int) -> Literal["arrow1", "arrow2", "slider", "trough1", "trough2", ""]: ... def get(self) -> tuple[float, float, float, float] | tuple[float, float]: ... 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" ] @@ -2920,54 +3157,54 @@ class Text(Widget, XView, YView): *, autoseparators: bool = True, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., blockcursor: bool = False, - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = "xterm", endline: int | Literal[""] = "", exportselection: bool = True, fg: str = ..., font: _FontDescription = "TkFixedFont", foreground: str = ..., - # width is always int, but height is allowed to be ScreenUnits. + # width is always int, but height is allowed to be screen units. # This doesn't make any sense to me, and this isn't documented. # The docs seem to say that both should be integers. - height: _ScreenUnits = 24, + height: float | str = 24, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., inactiveselectbackground: str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = 0, + insertborderwidth: float | str = 0, insertofftime: int = 300, insertontime: int = 600, insertunfocussed: Literal["none", "hollow", "solid"] = "none", - insertwidth: _ScreenUnits = ..., + insertwidth: float | str = ..., maxundo: int = 0, name: str = ..., - padx: _ScreenUnits = 1, - pady: _ScreenUnits = 1, - relief: _Relief = ..., + padx: float | str = 1, + pady: float | str = 1, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., setgrid: bool = False, - spacing1: _ScreenUnits = 0, - spacing2: _ScreenUnits = 0, - spacing3: _ScreenUnits = 0, + spacing1: float | str = 0, + spacing2: float | str = 0, + spacing3: float | str = 0, startline: int | Literal[""] = "", state: Literal["normal", "disabled"] = "normal", # Literal inside Tuple doesn't actually work - tabs: _ScreenUnits | str | tuple[_ScreenUnits | str, ...] = "", + tabs: float | str | tuple[float | str, ...] = "", tabstyle: Literal["tabular", "wordprocessor"] = "tabular", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", undo: bool = False, width: int = 80, wrap: Literal["none", "char", "word"] = "char", - xscrollcommand: _XYScrollCommand = "", - yscrollcommand: _XYScrollCommand = "", + xscrollcommand: str | Callable[[float, float], object] = "", + yscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload def configure( @@ -2976,67 +3213,84 @@ class Text(Widget, XView, YView): *, autoseparators: bool = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., blockcursor: bool = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., endline: int | Literal[""] = ..., exportselection: bool = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., inactiveselectbackground: str = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., + insertborderwidth: float | str = ..., insertofftime: int = ..., insertontime: int = ..., insertunfocussed: Literal["none", "hollow", "solid"] = ..., - insertwidth: _ScreenUnits = ..., + insertwidth: float | str = ..., maxundo: int = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., setgrid: bool = ..., - spacing1: _ScreenUnits = ..., - spacing2: _ScreenUnits = ..., - spacing3: _ScreenUnits = ..., + spacing1: float | str = ..., + spacing2: float | str = ..., + spacing3: float | str = ..., startline: int | Literal[""] = ..., state: Literal["normal", "disabled"] = ..., - tabs: _ScreenUnits | str | tuple[_ScreenUnits | str, ...] = ..., + tabs: float | str | tuple[float | str, ...] = ..., tabstyle: Literal["tabular", "wordprocessor"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., undo: bool = ..., width: int = ..., wrap: Literal["none", "char", "word"] = ..., - xscrollcommand: _XYScrollCommand = ..., - yscrollcommand: _XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., + yscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... 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 bbox(self, index: str | float | _tkinter.Tcl_Obj | Widget) -> tuple[int, int, int, int] | None: ... # type: ignore[override] + def compare( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + op: Literal["<", "<=", "==", ">=", ">", "!="], + index2: str | float | _tkinter.Tcl_Obj | Widget, + ) -> bool: ... if sys.version_info >= (3, 13): @overload - def count(self, index1: _TextIndex, index2: _TextIndex, *, return_ints: Literal[True]) -> int: ... + def count( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + *, + return_ints: Literal[True], + ) -> int: ... @overload def count( - self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], /, *, return_ints: Literal[True] + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + arg: _WhatToCount | Literal["update"], + /, + *, + return_ints: Literal[True], ) -> int: ... @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: Literal["update"], arg2: _WhatToCount, /, @@ -3046,8 +3300,8 @@ class Text(Widget, XView, YView): @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: _WhatToCount, arg2: Literal["update"], /, @@ -3056,13 +3310,20 @@ class Text(Widget, XView, YView): ) -> int: ... @overload def count( - self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /, *, return_ints: Literal[True] + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + arg1: _WhatToCount, + arg2: _WhatToCount, + /, + *, + return_ints: Literal[True], ) -> tuple[int, int]: ... @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: _WhatToCount | Literal["update"], arg2: _WhatToCount | Literal["update"], arg3: _WhatToCount | Literal["update"], @@ -3071,12 +3332,18 @@ class Text(Widget, XView, YView): return_ints: Literal[True], ) -> tuple[int, ...]: ... @overload - def count(self, index1: _TextIndex, index2: _TextIndex, *, return_ints: Literal[False] = False) -> tuple[int] | None: ... + def count( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + *, + return_ints: Literal[False] = False, + ) -> tuple[int] | None: ... @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg: _WhatToCount | Literal["update"], /, *, @@ -3085,8 +3352,8 @@ class Text(Widget, XView, YView): @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: Literal["update"], arg2: _WhatToCount, /, @@ -3096,8 +3363,8 @@ class Text(Widget, XView, YView): @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: _WhatToCount, arg2: Literal["update"], /, @@ -3107,8 +3374,8 @@ class Text(Widget, XView, YView): @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: _WhatToCount, arg2: _WhatToCount, /, @@ -3118,8 +3385,8 @@ class Text(Widget, XView, YView): @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: _WhatToCount | Literal["update"], arg2: _WhatToCount | Literal["update"], arg3: _WhatToCount | Literal["update"], @@ -3129,22 +3396,49 @@ class Text(Widget, XView, YView): ) -> tuple[int, ...]: ... else: @overload - def count(self, index1: _TextIndex, index2: _TextIndex) -> tuple[int] | None: ... + def count( + self, index1: str | float | _tkinter.Tcl_Obj | Widget, index2: str | float | _tkinter.Tcl_Obj | Widget + ) -> tuple[int] | None: ... @overload def count( - self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], / + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + arg: _WhatToCount | Literal["update"], + /, ) -> tuple[int] | None: ... @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg1: Literal["update"], arg2: _WhatToCount, /) -> int | None: ... + def count( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + arg1: Literal["update"], + arg2: _WhatToCount, + /, + ) -> int | None: ... @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: Literal["update"], /) -> int | None: ... + def count( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + arg1: _WhatToCount, + arg2: Literal["update"], + /, + ) -> int | None: ... @overload - def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /) -> tuple[int, int]: ... + def count( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + arg1: _WhatToCount, + arg2: _WhatToCount, + /, + ) -> tuple[int, int]: ... @overload def count( self, - index1: _TextIndex, - index2: _TextIndex, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, arg1: _WhatToCount | Literal["update"], arg2: _WhatToCount | Literal["update"], arg3: _WhatToCount | Literal["update"], @@ -3156,13 +3450,15 @@ class Text(Widget, XView, YView): def debug(self, boolean: None = None) -> bool: ... @overload def debug(self, boolean: bool) -> None: ... - def delete(self, index1: _TextIndex, index2: _TextIndex | None = None) -> None: ... - def dlineinfo(self, index: _TextIndex) -> tuple[int, int, int, int, int] | None: ... + def delete( + self, index1: str | float | _tkinter.Tcl_Obj | Widget, index2: str | float | _tkinter.Tcl_Obj | Widget | None = None + ) -> None: ... + def dlineinfo(self, index: str | float | _tkinter.Tcl_Obj | Widget) -> tuple[int, int, int, int, int] | None: ... @overload def dump( self, - index1: _TextIndex, - index2: _TextIndex | None = None, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget | None = None, command: None = None, *, all: bool = ..., @@ -3175,8 +3471,8 @@ class Text(Widget, XView, YView): @overload def dump( self, - index1: _TextIndex, - index2: _TextIndex | None, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget | None, command: Callable[[str, str, str], object] | str, *, all: bool = ..., @@ -3189,8 +3485,8 @@ class Text(Widget, XView, YView): @overload def dump( self, - index1: _TextIndex, - index2: _TextIndex | None = None, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget | None = None, *, command: Callable[[str, str, str], object] | str, all: bool = ..., @@ -3209,63 +3505,77 @@ class Text(Widget, XView, YView): def edit_reset(self) -> None: ... # actually returns empty string def edit_separator(self) -> None: ... # actually returns empty string def edit_undo(self) -> None: ... # actually returns empty string - def get(self, index1: _TextIndex, index2: _TextIndex | None = None) -> str: ... + def get( + self, index1: str | float | _tkinter.Tcl_Obj | Widget, index2: str | float | _tkinter.Tcl_Obj | Widget | None = None + ) -> str: ... @overload - def image_cget(self, index: _TextIndex, option: Literal["image", "name"]) -> str: ... + def image_cget(self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["image", "name"]) -> str: ... @overload - def image_cget(self, index: _TextIndex, option: Literal["padx", "pady"]) -> int: ... + def image_cget(self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["padx", "pady"]) -> int: ... @overload - def image_cget(self, index: _TextIndex, option: Literal["align"]) -> Literal["baseline", "bottom", "center", "top"]: ... + def image_cget( + self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["align"] + ) -> Literal["baseline", "bottom", "center", "top"]: ... @overload - def image_cget(self, index: _TextIndex, option: str) -> Any: ... + def image_cget(self, index: str | float | _tkinter.Tcl_Obj | Widget, option: str) -> Any: ... @overload - def image_configure(self, index: _TextIndex, cnf: str) -> tuple[str, str, str, str, str | int]: ... + def image_configure( + self, index: str | float | _tkinter.Tcl_Obj | Widget, cnf: str + ) -> tuple[str, str, str, str, str | int]: ... @overload def image_configure( self, - index: _TextIndex, - cnf: dict[str, Any] | None = {}, + index: str | float | _tkinter.Tcl_Obj | Widget, + cnf: dict[str, Any] | None = None, *, align: Literal["baseline", "bottom", "center", "top"] = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., + padx: float | str = ..., + pady: float | str = ..., ) -> dict[str, tuple[str, str, str, str, str | int]] | None: ... def image_create( self, - index: _TextIndex, + index: str | float | _tkinter.Tcl_Obj | Widget, cnf: dict[str, Any] | None = {}, *, align: Literal["baseline", "bottom", "center", "top"] = ..., - image: _ImageSpec = ..., + image: _Image | str = ..., name: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., + padx: float | str = ..., + pady: float | str = ..., ) -> str: ... def image_names(self) -> tuple[str, ...]: ... - def index(self, index: _TextIndex) -> str: ... - def insert(self, index: _TextIndex, chars: str, *args: str | list[str] | tuple[str, ...]) -> None: ... + def index(self, index: str | float | _tkinter.Tcl_Obj | Widget) -> str: ... + def insert( + self, index: str | float | _tkinter.Tcl_Obj | Widget, chars: str, *args: str | list[str] | tuple[str, ...] + ) -> None: ... @overload def mark_gravity(self, markName: str, direction: None = None) -> Literal["left", "right"]: ... @overload def mark_gravity(self, markName: str, direction: Literal["left", "right"]) -> None: ... # actually returns empty string def mark_names(self) -> tuple[str, ...]: ... - def mark_set(self, markName: str, index: _TextIndex) -> None: ... + def mark_set(self, markName: str, index: str | float | _tkinter.Tcl_Obj | Widget) -> None: ... def mark_unset(self, *markNames: str) -> None: ... - def mark_next(self, index: _TextIndex) -> str | None: ... - def mark_previous(self, index: _TextIndex) -> str | None: ... + def mark_next(self, index: str | float | _tkinter.Tcl_Obj | Widget) -> str | None: ... + def mark_previous(self, index: str | float | _tkinter.Tcl_Obj | Widget) -> str | None: ... # **kw of peer_create is same as the kwargs of Text.__init__ def peer_create(self, newPathName: str | Text, cnf: dict[str, Any] = {}, **kw) -> None: ... def peer_names(self) -> tuple[_tkinter.Tcl_Obj, ...]: ... - def replace(self, index1: _TextIndex, index2: _TextIndex, chars: str, *args: str | list[str] | tuple[str, ...]) -> None: ... + def replace( + self, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget, + chars: str, + *args: str | list[str] | tuple[str, ...], + ) -> None: ... def scan_mark(self, x: int, y: int) -> None: ... def scan_dragto(self, x: int, y: int) -> None: ... def search( self, pattern: str, - index: _TextIndex, - stopindex: _TextIndex | None = None, + index: str | float | _tkinter.Tcl_Obj | Widget, + stopindex: str | float | _tkinter.Tcl_Obj | Widget | None = None, forwards: bool | None = None, backwards: bool | None = None, exact: bool | None = None, @@ -3274,8 +3584,10 @@ class Text(Widget, XView, YView): count: Variable | None = None, elide: bool | None = None, ) -> str: ... # returns empty string for not found - def see(self, index: _TextIndex) -> None: ... - def tag_add(self, tagName: str, index1: _TextIndex, *args: _TextIndex) -> None: ... + def see(self, index: str | float | _tkinter.Tcl_Obj | Widget) -> None: ... + def tag_add( + self, tagName: str, index1: str | float | _tkinter.Tcl_Obj | Widget, *args: str | float | _tkinter.Tcl_Obj | Widget + ) -> None: ... # tag_bind stuff is very similar to Canvas @overload def tag_bind( @@ -3298,27 +3610,27 @@ class Text(Widget, XView, YView): *, background: str = ..., bgstipple: str = ..., - borderwidth: _ScreenUnits = ..., - border: _ScreenUnits = ..., # alias for borderwidth + borderwidth: float | str = ..., + border: float | str = ..., # alias for borderwidth elide: bool = ..., fgstipple: str = ..., font: _FontDescription = ..., foreground: str = ..., justify: Literal["left", "right", "center"] = ..., - lmargin1: _ScreenUnits = ..., - lmargin2: _ScreenUnits = ..., + lmargin1: float | str = ..., + lmargin2: float | str = ..., lmargincolor: str = ..., - offset: _ScreenUnits = ..., + offset: float | str = ..., overstrike: bool = ..., overstrikefg: str = ..., - relief: _Relief = ..., - rmargin: _ScreenUnits = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + rmargin: float | str = ..., rmargincolor: str = ..., selectbackground: str = ..., selectforeground: str = ..., - spacing1: _ScreenUnits = ..., - spacing2: _ScreenUnits = ..., - spacing3: _ScreenUnits = ..., + spacing1: float | str = ..., + spacing2: float | str = ..., + spacing3: float | str = ..., tabs: Any = ..., # the exact type is kind of complicated, see manual page tabstyle: Literal["tabular", "wordprocessor"] = ..., underline: bool = ..., @@ -3330,52 +3642,69 @@ class Text(Widget, XView, YView): tag_config = tag_configure def tag_delete(self, first_tag_name: str, /, *tagNames: str) -> None: ... # error if no tag names given def tag_lower(self, tagName: str, belowThis: str | None = None) -> None: ... - def tag_names(self, index: _TextIndex | None = None) -> tuple[str, ...]: ... + def tag_names(self, index: str | float | _tkinter.Tcl_Obj | Widget | None = None) -> tuple[str, ...]: ... def tag_nextrange( - self, tagName: str, index1: _TextIndex, index2: _TextIndex | None = None + self, + tagName: str, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget | None = None, ) -> tuple[str, str] | tuple[()]: ... def tag_prevrange( - self, tagName: str, index1: _TextIndex, index2: _TextIndex | None = None + self, + tagName: str, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget | None = None, ) -> tuple[str, str] | tuple[()]: ... def tag_raise(self, tagName: str, aboveThis: str | None = None) -> None: ... def tag_ranges(self, tagName: str) -> tuple[_tkinter.Tcl_Obj, ...]: ... # tag_remove and tag_delete are different - def tag_remove(self, tagName: str, index1: _TextIndex, index2: _TextIndex | None = None) -> None: ... + def tag_remove( + self, + tagName: str, + index1: str | float | _tkinter.Tcl_Obj | Widget, + index2: str | float | _tkinter.Tcl_Obj | Widget | None = None, + ) -> None: ... @overload - def window_cget(self, index: _TextIndex, option: Literal["padx", "pady"]) -> int: ... + def window_cget(self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["padx", "pady"]) -> int: ... @overload - def window_cget(self, index: _TextIndex, option: Literal["stretch"]) -> bool: ... # actually returns Literal[0, 1] + def window_cget( + self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["stretch"] + ) -> bool: ... # actually returns Literal[0, 1] @overload - def window_cget(self, index: _TextIndex, option: Literal["align"]) -> Literal["baseline", "bottom", "center", "top"]: ... + def window_cget( + self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["align"] + ) -> Literal["baseline", "bottom", "center", "top"]: ... @overload # window is set to a widget, but read as the string name. - def window_cget(self, index: _TextIndex, option: Literal["create", "window"]) -> str: ... + def window_cget(self, index: str | float | _tkinter.Tcl_Obj | Widget, option: Literal["create", "window"]) -> str: ... @overload - def window_cget(self, index: _TextIndex, option: str) -> Any: ... + def window_cget(self, index: str | float | _tkinter.Tcl_Obj | Widget, option: str) -> Any: ... @overload - def window_configure(self, index: _TextIndex, cnf: str) -> tuple[str, str, str, str, str | int]: ... + def window_configure( + self, index: str | float | _tkinter.Tcl_Obj | Widget, cnf: str + ) -> tuple[str, str, str, str, str | int]: ... @overload def window_configure( self, - index: _TextIndex, + index: str | float | _tkinter.Tcl_Obj | Widget, cnf: dict[str, Any] | None = None, *, align: Literal["baseline", "bottom", "center", "top"] = ..., create: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., + padx: float | str = ..., + pady: float | str = ..., stretch: bool | Literal[0, 1] = ..., window: Misc | str = ..., ) -> dict[str, tuple[str, str, str, str, str | int]] | None: ... window_config = window_configure def window_create( self, - index: _TextIndex, + index: str | float | _tkinter.Tcl_Obj | Widget, cnf: dict[str, Any] | None = {}, *, align: Literal["baseline", "bottom", "center", "top"] = ..., create: str = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., + padx: float | str = ..., + pady: float | str = ..., stretch: bool | Literal[0, 1] = ..., window: Misc | str = ..., ) -> None: ... @@ -3383,12 +3712,11 @@ class Text(Widget, XView, YView): def yview_pickplace(self, *what): ... # deprecated class _setit: - def __init__(self, var, value, callback: Incomplete | None = None) -> None: ... + def __init__(self, var, value, callback=None) -> None: ... def __call__(self, *args) -> None: ... # manual page: tk_optionMenu class OptionMenu(Menubutton): - widgetName: Incomplete menuname: Incomplete def __init__( # differs from other widgets @@ -3403,11 +3731,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): ... @@ -3418,17 +3749,13 @@ class _PhotoImageLike(_Image): ... class Image(_Image): name: Incomplete tk: _tkinter.TkappType - def __init__( - self, imgtype, name: Incomplete | None = None, cnf={}, master: Misc | _tkinter.TkappType | None = None, **kw - ) -> None: ... + def __init__(self, imgtype, name=None, cnf={}, master: Misc | _tkinter.TkappType | None = None, **kw) -> None: ... def __del__(self) -> None: ... def __setitem__(self, key, value) -> None: ... 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__() @@ -3493,6 +3820,7 @@ class PhotoImage(Image, _PhotoImageLike): self, data: ( str + | bytes | list[str] | list[list[str]] | list[tuple[str, ...]] @@ -3500,7 +3828,7 @@ class PhotoImage(Image, _PhotoImageLike): | tuple[list[str], ...] | tuple[tuple[str, ...], ...] ), - to: tuple[int, int] | None = None, + to: tuple[int, int] | tuple[int, int, int, int] | None = None, ) -> None: ... if sys.version_info >= (3, 13): def read( @@ -3547,7 +3875,7 @@ class BitmapImage(Image, _BitmapImageLike): # This should be kept in sync with PIL.ImageTK.BitmapImage.__init__() def __init__( self, - name: Incomplete | None = None, + name=None, cnf: dict[str, Any] = {}, master: Misc | _tkinter.TkappType | None = None, *, @@ -3570,14 +3898,14 @@ class Spinbox(Widget, XView): *, activebackground: str = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., buttonbackground: str = ..., buttoncursor: _Cursor = "", - buttondownrelief: _Relief = ..., - buttonuprelief: _Relief = ..., + buttondownrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + buttonuprelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., # percent substitutions don't seem to be supported, it's similar to Entry's validation stuff command: Callable[[], object] | str | list[str] | tuple[str, ...] = "", cursor: _Cursor = "xterm", @@ -3591,35 +3919,35 @@ class Spinbox(Widget, XView): from_: float = 0.0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., increment: float = 1.0, insertbackground: str = ..., - insertborderwidth: _ScreenUnits = 0, + insertborderwidth: float | str = 0, insertofftime: int = 300, insertontime: int = 600, - insertwidth: _ScreenUnits = ..., - invalidcommand: _EntryValidateCommand = "", - invcmd: _EntryValidateCommand = "", + insertwidth: float | str = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", + invcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", justify: Literal["left", "center", "right"] = "left", name: str = ..., readonlybackground: str = ..., - relief: _Relief = "sunken", + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "sunken", repeatdelay: int = 400, repeatinterval: int = 100, selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., state: Literal["normal", "disabled", "readonly"] = "normal", - takefocus: _TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", textvariable: Variable = ..., to: float = 0.0, validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", - validatecommand: _EntryValidateCommand = "", - vcmd: _EntryValidateCommand = "", + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", + vcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", values: list[str] | tuple[str, ...] = ..., width: int = 20, wrap: bool = False, - xscrollcommand: _XYScrollCommand = "", + xscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload def configure( @@ -3628,14 +3956,14 @@ class Spinbox(Widget, XView): *, activebackground: str = ..., background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., buttonbackground: str = ..., buttoncursor: _Cursor = ..., - buttondownrelief: _Relief = ..., - buttonuprelief: _Relief = ..., + buttondownrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + buttonuprelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., command: Callable[[], object] | str | list[str] | tuple[str, ...] = ..., cursor: _Cursor = ..., disabledbackground: str = ..., @@ -3648,40 +3976,40 @@ class Spinbox(Widget, XView): from_: float = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., increment: float = ..., insertbackground: str = ..., - insertborderwidth: _ScreenUnits = ..., + insertborderwidth: float | str = ..., insertofftime: int = ..., insertontime: int = ..., - insertwidth: _ScreenUnits = ..., - invalidcommand: _EntryValidateCommand = ..., - invcmd: _EntryValidateCommand = ..., + insertwidth: float | str = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., + invcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., readonlybackground: str = ..., - relief: _Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., repeatdelay: int = ..., repeatinterval: int = ..., selectbackground: str = ..., - selectborderwidth: _ScreenUnits = ..., + selectborderwidth: float | str = ..., selectforeground: str = ..., state: Literal["normal", "disabled", "readonly"] = ..., - takefocus: _TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: Variable = ..., to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: _EntryValidateCommand = ..., - vcmd: _EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., + vcmd: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., values: list[str] | tuple[str, ...] = ..., width: int = ..., wrap: bool = ..., - xscrollcommand: _XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... config = configure def bbox(self, index) -> tuple[int, int, int, int] | None: ... # type: ignore[override] - def delete(self, first, last: Incomplete | None = None) -> Literal[""]: ... + def delete(self, first, last=None) -> Literal[""]: ... def get(self) -> str: ... def icursor(self, index): ... def identify(self, x: int, y: int) -> Literal["", "buttondown", "buttonup", "entry"]: ... @@ -3695,7 +4023,7 @@ class Spinbox(Widget, XView): def selection(self, *args) -> tuple[int, ...]: ... def selection_adjust(self, index): ... def selection_clear(self): ... # type: ignore[override] - def selection_element(self, element: Incomplete | None = None): ... + def selection_element(self, element=None): ... def selection_from(self, index: int) -> None: ... def selection_present(self) -> None: ... def selection_range(self, start: int, end: int) -> None: ... @@ -3708,10 +4036,10 @@ class LabelFrame(Widget): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = 2, + bd: float | str = 2, bg: str = ..., - border: _ScreenUnits = 2, - borderwidth: _ScreenUnits = 2, + border: float | str = 2, + borderwidth: float | str = 2, class_: str = "Labelframe", # can't be changed with configure() colormap: Literal["new", ""] | Misc = "", # can't be changed with configure() container: bool = False, # undocumented, can't be changed with configure() @@ -3719,21 +4047,21 @@ class LabelFrame(Widget): fg: str = ..., font: _FontDescription = "TkDefaultFont", foreground: str = ..., - height: _ScreenUnits = 0, + height: float | str = 0, highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = 0, + highlightthickness: float | str = 0, # 'ne' and 'en' are valid labelanchors, but only 'ne' is a valid _Anchor. labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = "nw", labelwidget: Misc = ..., name: str = ..., - padx: _ScreenUnits = 0, - pady: _ScreenUnits = 0, - relief: _Relief = "groove", - takefocus: _TakeFocusValue = 0, + padx: float | str = 0, + pady: float | str = 0, + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "groove", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = 0, text: float | str = "", visual: str | tuple[str, int] = "", # can't be changed with configure() - width: _ScreenUnits = 0, + width: float | str = 0, ) -> None: ... @overload def configure( @@ -3741,26 +4069,26 @@ class LabelFrame(Widget): cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., fg: str = ..., font: _FontDescription = ..., foreground: str = ..., - height: _ScreenUnits = ..., + height: float | str = ..., highlightbackground: str = ..., highlightcolor: str = ..., - highlightthickness: _ScreenUnits = ..., + highlightthickness: float | str = ..., labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., labelwidget: Misc = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - takefocus: _TakeFocusValue = ..., + padx: float | str = ..., + pady: float | str = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -3773,27 +4101,27 @@ class PanedWindow(Widget): cnf: dict[str, Any] | None = {}, *, background: str = ..., - bd: _ScreenUnits = 1, + bd: float | str = 1, bg: str = ..., - border: _ScreenUnits = 1, - borderwidth: _ScreenUnits = 1, + border: float | str = 1, + borderwidth: float | str = 1, cursor: _Cursor = "", - handlepad: _ScreenUnits = 8, - handlesize: _ScreenUnits = 8, - height: _ScreenUnits = "", + handlepad: float | str = 8, + handlesize: float | str = 8, + height: float | str = "", name: str = ..., opaqueresize: bool = True, orient: Literal["horizontal", "vertical"] = "horizontal", proxybackground: str = "", - proxyborderwidth: _ScreenUnits = 2, - proxyrelief: _Relief = "flat", - relief: _Relief = "flat", + proxyborderwidth: float | str = 2, + proxyrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", sashcursor: _Cursor = "", - sashpad: _ScreenUnits = 0, - sashrelief: _Relief = "flat", - sashwidth: _ScreenUnits = 3, + sashpad: float | str = 0, + sashrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = "flat", + sashwidth: float | str = 3, showhandle: bool = False, - width: _ScreenUnits = "", + width: float | str = "", ) -> None: ... @overload def configure( @@ -3801,45 +4129,45 @@ class PanedWindow(Widget): cnf: dict[str, Any] | None = None, *, background: str = ..., - bd: _ScreenUnits = ..., + bd: float | str = ..., bg: str = ..., - border: _ScreenUnits = ..., - borderwidth: _ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: _Cursor = ..., - handlepad: _ScreenUnits = ..., - handlesize: _ScreenUnits = ..., - height: _ScreenUnits = ..., + handlepad: float | str = ..., + handlesize: float | str = ..., + height: float | str = ..., opaqueresize: bool = ..., orient: Literal["horizontal", "vertical"] = ..., proxybackground: str = ..., - proxyborderwidth: _ScreenUnits = ..., - proxyrelief: _Relief = ..., - relief: _Relief = ..., + proxyborderwidth: float | str = ..., + proxyrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., sashcursor: _Cursor = ..., - sashpad: _ScreenUnits = ..., - sashrelief: _Relief = ..., - sashwidth: _ScreenUnits = ..., + sashpad: float | str = ..., + sashrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., + sashwidth: float | str = ..., showhandle: bool = ..., - width: _ScreenUnits = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... config = configure def add(self, child: Widget, **kw) -> None: ... def remove(self, child) -> None: ... - forget: Incomplete + forget = remove # type: ignore[assignment] def identify(self, x: int, y: int): ... - def proxy(self, *args): ... - def proxy_coord(self): ... - def proxy_forget(self): ... - def proxy_place(self, x, y): ... - def sash(self, *args): ... - def sash_coord(self, index): ... - def sash_mark(self, index): ... - def sash_place(self, index, x, y): ... + def proxy(self, *args) -> tuple[Incomplete, ...]: ... + def proxy_coord(self) -> tuple[Incomplete, ...]: ... + def proxy_forget(self) -> tuple[Incomplete, ...]: ... + def proxy_place(self, x, y) -> tuple[Incomplete, ...]: ... + def sash(self, *args) -> tuple[Incomplete, ...]: ... + def sash_coord(self, index) -> tuple[Incomplete, ...]: ... + def sash_mark(self, index) -> tuple[Incomplete, ...]: ... + def sash_place(self, index, x, y) -> tuple[Incomplete, ...]: ... def panecget(self, child, option): ... - def paneconfigure(self, tagOrId, cnf: Incomplete | None = None, **kw): ... - paneconfig: Incomplete + def paneconfigure(self, tagOrId, cnf=None, **kw): ... + paneconfig = paneconfigure def panes(self): ... def _test() -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/colorchooser.pyi b/mypy/typeshed/stdlib/tkinter/colorchooser.pyi index 09bc8cbb4f1ee..d0d6de8426562 100644 --- a/mypy/typeshed/stdlib/tkinter/colorchooser.pyi +++ b/mypy/typeshed/stdlib/tkinter/colorchooser.pyi @@ -1,20 +1,12 @@ -import sys from tkinter import Misc from tkinter.commondialog import Dialog from typing import ClassVar -if sys.version_info >= (3, 9): - __all__ = ["Chooser", "askcolor"] +__all__ = ["Chooser", "askcolor"] class Chooser(Dialog): command: ClassVar[str] -if sys.version_info >= (3, 9): - def askcolor( - color: str | bytes | None = None, *, initialcolor: str = ..., parent: Misc = ..., title: str = ... - ) -> tuple[None, None] | tuple[tuple[int, int, int], str]: ... - -else: - def askcolor( - color: str | bytes | None = None, *, initialcolor: str = ..., parent: Misc = ..., title: str = ... - ) -> tuple[None, None] | tuple[tuple[float, float, float], str]: ... +def askcolor( + color: str | bytes | None = None, *, initialcolor: str = ..., parent: Misc = ..., title: str = ... +) -> tuple[None, None] | tuple[tuple[int, int, int], str]: ... diff --git a/mypy/typeshed/stdlib/tkinter/commondialog.pyi b/mypy/typeshed/stdlib/tkinter/commondialog.pyi index d06c08df5b768..6dba6bd609284 100644 --- a/mypy/typeshed/stdlib/tkinter/commondialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/commondialog.pyi @@ -1,14 +1,14 @@ -import sys -from _typeshed import Incomplete from collections.abc import Mapping -from typing import ClassVar +from tkinter import Misc +from typing import Any, ClassVar -if sys.version_info >= (3, 9): - __all__ = ["Dialog"] +__all__ = ["Dialog"] class Dialog: command: ClassVar[str | None] - master: Incomplete | None - options: Mapping[str, Incomplete] - def __init__(self, master: Incomplete | None = None, **options) -> None: ... - def show(self, **options): ... + master: Misc | None + # Types of options are very dynamic. They depend on the command and are + # sometimes changed to a different type. + options: Mapping[str, Any] + def __init__(self, master: Misc | None = None, **options: Any) -> None: ... + def show(self, **options: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/tkinter/dialog.pyi b/mypy/typeshed/stdlib/tkinter/dialog.pyi index b7d74c0fa71e5..971b64f091253 100644 --- a/mypy/typeshed/stdlib/tkinter/dialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/dialog.pyi @@ -1,16 +1,13 @@ -import sys -from _typeshed import Incomplete from collections.abc import Mapping from tkinter import Widget from typing import Any, Final -if sys.version_info >= (3, 9): - __all__ = ["Dialog"] +__all__ = ["Dialog"] DIALOG_ICON: Final = "questhead" class Dialog(Widget): widgetName: str num: int - def __init__(self, master: Incomplete | None = None, cnf: Mapping[str, Any] = {}, **kw) -> None: ... + def __init__(self, master=None, cnf: Mapping[str, Any] = {}, **kw) -> None: ... def destroy(self) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/dnd.pyi b/mypy/typeshed/stdlib/tkinter/dnd.pyi index d806be74068e3..521f451a9b2c5 100644 --- a/mypy/typeshed/stdlib/tkinter/dnd.pyi +++ b/mypy/typeshed/stdlib/tkinter/dnd.pyi @@ -1,10 +1,9 @@ -import sys from tkinter import Event, Misc, Tk, Widget -from typing import ClassVar, Protocol +from typing import ClassVar, Protocol, type_check_only -if sys.version_info >= (3, 9): - __all__ = ["dnd_start", "DndHandler"] +__all__ = ["dnd_start", "DndHandler"] +@type_check_only class _DndSource(Protocol): def dnd_end(self, target: Widget | None, event: Event[Misc] | None, /) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/filedialog.pyi b/mypy/typeshed/stdlib/tkinter/filedialog.pyi index 3d62f079178ef..b6ef8f45d0350 100644 --- a/mypy/typeshed/stdlib/tkinter/filedialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/filedialog.pyi @@ -1,32 +1,30 @@ -import sys -from _typeshed import Incomplete, StrOrBytesPath -from collections.abc import Iterable -from tkinter import Button, Entry, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog +from _typeshed import Incomplete, StrOrBytesPath, StrPath +from collections.abc import Hashable, Iterable +from tkinter import Button, Entry, Event, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog from typing import IO, ClassVar, Literal -if sys.version_info >= (3, 9): - __all__ = [ - "FileDialog", - "LoadFileDialog", - "SaveFileDialog", - "Open", - "SaveAs", - "Directory", - "askopenfilename", - "asksaveasfilename", - "askopenfilenames", - "askopenfile", - "askopenfiles", - "asksaveasfile", - "askdirectory", - ] +__all__ = [ + "FileDialog", + "LoadFileDialog", + "SaveFileDialog", + "Open", + "SaveAs", + "Directory", + "askopenfilename", + "asksaveasfilename", + "askopenfilenames", + "askopenfile", + "askopenfiles", + "asksaveasfile", + "askdirectory", +] -dialogstates: dict[Incomplete, tuple[Incomplete, Incomplete]] +dialogstates: dict[Hashable, tuple[str, str]] class FileDialog: title: str - master: Incomplete - directory: Incomplete | None + master: Misc + directory: str | None top: Toplevel botframe: Frame selection: Entry @@ -40,23 +38,23 @@ class FileDialog: filter_button: Button cancel_button: Button def __init__( - self, master, title: Incomplete | None = None + self, master: Misc, title: str | None = None ) -> None: ... # title is usually a str or None, but e.g. int doesn't raise en exception either - how: Incomplete | None - def go(self, dir_or_file=".", pattern: str = "*", default: str = "", key: Incomplete | None = None): ... - def quit(self, how: Incomplete | None = None) -> None: ... - def dirs_double_event(self, event) -> None: ... - def dirs_select_event(self, event) -> None: ... - def files_double_event(self, event) -> None: ... - def files_select_event(self, event) -> None: ... - def ok_event(self, event) -> None: ... + how: str | None + def go(self, dir_or_file: StrPath = ".", pattern: StrPath = "*", default: StrPath = "", key: Hashable | None = None): ... + def quit(self, how: str | None = None) -> None: ... + def dirs_double_event(self, event: Event) -> None: ... + def dirs_select_event(self, event: Event) -> None: ... + def files_double_event(self, event: Event) -> None: ... + def files_select_event(self, event: Event) -> None: ... + def ok_event(self, event: Event) -> None: ... def ok_command(self) -> None: ... - def filter_command(self, event: Incomplete | None = None) -> None: ... - def get_filter(self): ... - def get_selection(self): ... - def cancel_command(self, event: Incomplete | None = None) -> None: ... - def set_filter(self, dir, pat) -> None: ... - def set_selection(self, file) -> None: ... + def filter_command(self, event: Event | None = None) -> None: ... + def get_filter(self) -> tuple[str, str]: ... + def get_selection(self) -> str: ... + def cancel_command(self, event: Event | None = None) -> None: ... + def set_filter(self, dir: StrPath, pat: StrPath) -> None: ... + def set_selection(self, file: StrPath) -> None: ... class LoadFileDialog(FileDialog): title: str @@ -80,8 +78,8 @@ class Directory(commondialog.Dialog): # TODO: command kwarg available on macos def asksaveasfilename( *, - confirmoverwrite: bool | None = ..., - defaultextension: str | None = ..., + confirmoverwrite: bool | None = True, + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -91,7 +89,7 @@ def asksaveasfilename( ) -> str: ... # can be empty string def askopenfilename( *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -101,7 +99,7 @@ def askopenfilename( ) -> str: ... # can be empty string def askopenfilenames( *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -110,15 +108,15 @@ def askopenfilenames( typevariable: StringVar | str | None = ..., ) -> Literal[""] | tuple[str, ...]: ... def askdirectory( - *, initialdir: StrOrBytesPath | None = ..., mustexist: bool | None = ..., parent: Misc | None = ..., title: str | None = ... + *, initialdir: StrOrBytesPath | None = ..., mustexist: bool | None = False, parent: Misc | None = ..., title: str | None = ... ) -> str: ... # can be empty string # TODO: If someone actually uses these, overload to have the actual return type of open(..., mode) def asksaveasfile( mode: str = "w", *, - confirmoverwrite: bool | None = ..., - defaultextension: str | None = ..., + confirmoverwrite: bool | None = True, + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -129,7 +127,7 @@ def asksaveasfile( def askopenfile( mode: str = "r", *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., @@ -140,7 +138,7 @@ def askopenfile( def askopenfiles( mode: str = "r", *, - defaultextension: str | None = ..., + defaultextension: str | None = "", filetypes: Iterable[tuple[str, str | list[str] | tuple[str, ...]]] | None = ..., initialdir: StrOrBytesPath | None = ..., initialfile: StrOrBytesPath | None = ..., diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 317f3068be63a..327ba7a2432e0 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -1,11 +1,11 @@ import _tkinter +import itertools import sys import tkinter -from typing import Any, Final, Literal, TypedDict, overload -from typing_extensions import TypeAlias +from typing import Any, ClassVar, Final, Literal, TypedDict, overload, type_check_only +from typing_extensions import TypeAlias, Unpack -if sys.version_info >= (3, 9): - __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] +__all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] NORMAL: Final = "normal" ROMAN: Final = "roman" @@ -17,12 +17,13 @@ _FontDescription: TypeAlias = ( | Font # A font object constructed in Python | list[Any] # ["Helvetica", 12, BOLD] | tuple[str] # ("Liberation Sans",) needs wrapping in tuple/list to handle spaces - | tuple[str, int] # ("Liberation Sans", 12) - | tuple[str, int, str] # ("Liberation Sans", 12, "bold") - | tuple[str, int, list[str] | tuple[str, ...]] # e.g. bold and italic + # ("Liberation Sans", 12) or ("Liberation Sans", 12, "bold", "italic", "underline") + | tuple[str, int, Unpack[tuple[str, ...]]] # Any number of trailing options is permitted + | tuple[str, int, list[str] | tuple[str, ...]] # Options can also be passed as list/tuple | _tkinter.Tcl_Obj # A font object constructed in Tcl ) +@type_check_only class _FontDict(TypedDict): family: str size: int @@ -31,6 +32,7 @@ class _FontDict(TypedDict): underline: bool overstrike: bool +@type_check_only class _MetricsDict(TypedDict): ascent: int descent: int @@ -40,6 +42,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 @@ -56,6 +59,7 @@ class Font: underline: bool = ..., overstrike: bool = ..., ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __setitem__(self, key: str, value: Any) -> None: ... @overload def cget(self, option: Literal["family"]) -> str: ... diff --git a/mypy/typeshed/stdlib/tkinter/messagebox.pyi b/mypy/typeshed/stdlib/tkinter/messagebox.pyi index 5cdfe512f9b76..cd95f0de5f803 100644 --- a/mypy/typeshed/stdlib/tkinter/messagebox.pyi +++ b/mypy/typeshed/stdlib/tkinter/messagebox.pyi @@ -1,18 +1,8 @@ -import sys +from tkinter import Misc from tkinter.commondialog import Dialog -from typing import ClassVar, Final +from typing import ClassVar, Final, Literal -if sys.version_info >= (3, 9): - __all__ = [ - "showinfo", - "showwarning", - "showerror", - "askquestion", - "askokcancel", - "askyesno", - "askyesnocancel", - "askretrycancel", - ] +__all__ = ["showinfo", "showwarning", "showerror", "askquestion", "askokcancel", "askyesno", "askyesnocancel", "askretrycancel"] ERROR: Final = "error" INFO: Final = "info" @@ -34,11 +24,75 @@ NO: Final = "no" class Message(Dialog): command: ClassVar[str] -def showinfo(title: str | None = None, message: str | None = None, **options) -> str: ... -def showwarning(title: str | None = None, message: str | None = None, **options) -> str: ... -def showerror(title: str | None = None, message: str | None = None, **options) -> str: ... -def askquestion(title: str | None = None, message: str | None = None, **options) -> str: ... -def askokcancel(title: str | None = None, message: str | None = None, **options) -> bool: ... -def askyesno(title: str | None = None, message: str | None = None, **options) -> bool: ... -def askyesnocancel(title: str | None = None, message: str | None = None, **options) -> bool | None: ... -def askretrycancel(title: str | None = None, message: str | None = None, **options) -> bool: ... +def showinfo( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["ok"] = "ok", + parent: Misc = ..., +) -> str: ... +def showwarning( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["ok"] = "ok", + parent: Misc = ..., +) -> str: ... +def showerror( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["ok"] = "ok", + parent: Misc = ..., +) -> str: ... +def askquestion( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["yes", "no"] = ..., + parent: Misc = ..., +) -> str: ... +def askokcancel( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["ok", "cancel"] = ..., + parent: Misc = ..., +) -> bool: ... +def askyesno( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["yes", "no"] = ..., + parent: Misc = ..., +) -> bool: ... +def askyesnocancel( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["cancel", "yes", "no"] = ..., + parent: Misc = ..., +) -> bool | None: ... +def askretrycancel( + title: str | None = None, + message: str | None = None, + *, + detail: str = ..., + icon: Literal["error", "info", "question", "warning"] = ..., + default: Literal["retry", "cancel"] = ..., + parent: Misc = ..., +) -> bool: ... diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index b851f478140af..1d72acd995126 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1,10 +1,11 @@ import _tkinter +import sys import tkinter -from _typeshed import Incomplete -from collections.abc import Callable +from _typeshed import MaybeNone +from collections.abc import Callable, Iterable from tkinter.font import _FontDescription -from typing import Any, Literal, TypedDict, overload -from typing_extensions import TypeAlias +from typing import Any, Literal, TypedDict, overload, type_check_only +from typing_extensions import Never, TypeAlias, Unpack __all__ = [ "Button", @@ -35,32 +36,164 @@ __all__ = [ ] def tclobjs_to_py(adict: dict[Any, Any]) -> dict[Any, Any]: ... -def setup_master(master: Incomplete | None = None): ... +def setup_master(master: tkinter.Misc | None = None): ... _Padding: TypeAlias = ( - tkinter._ScreenUnits - | tuple[tkinter._ScreenUnits] - | tuple[tkinter._ScreenUnits, tkinter._ScreenUnits] - | tuple[tkinter._ScreenUnits, tkinter._ScreenUnits, tkinter._ScreenUnits] - | tuple[tkinter._ScreenUnits, tkinter._ScreenUnits, tkinter._ScreenUnits, tkinter._ScreenUnits] + float + | str + | tuple[float | str] + | tuple[float | str, float | str] + | tuple[float | str, float | str, float | str] + | tuple[float | str, float | str, float | str, float | str] ) -# from ttk_widget (aka ttk::widget) manual page, differs from tkinter._Compound -_TtkCompound: TypeAlias = Literal["", "text", "image", tkinter._Compound] +# Last item (option value to apply) varies between different options so use Any. +# It could also be any iterable with items matching the tuple, but that case +# hasn't been added here for consistency with _Padding above. +_Statespec: TypeAlias = tuple[Unpack[tuple[str, ...]], Any] +_ImageStatespec: TypeAlias = tuple[Unpack[tuple[str, ...]], tkinter._Image | str] +_VsapiStatespec: TypeAlias = tuple[Unpack[tuple[str, ...]], int] + +class _Layout(TypedDict, total=False): + side: Literal["left", "right", "top", "bottom"] + sticky: str # consists of letters 'n', 's', 'w', 'e', may contain repeats, may be empty + unit: Literal[0, 1] | bool + children: _LayoutSpec + # Note: there seem to be some other undocumented keys sometimes + +# This could be any sequence when passed as a parameter but will always be a list when returned. +_LayoutSpec: TypeAlias = list[tuple[str, _Layout | None]] + +# Keep these in sync with the appropriate methods in Style +class _ElementCreateImageKwargs(TypedDict, total=False): + border: _Padding + height: float | str + padding: _Padding + sticky: str + width: float | str + +_ElementCreateArgsCrossPlatform: TypeAlias = ( + # Could be any sequence here but types are not homogenous so just type it as tuple + tuple[Literal["image"], tkinter._Image | str, Unpack[tuple[_ImageStatespec, ...]], _ElementCreateImageKwargs] + | tuple[Literal["from"], str, str] + | tuple[Literal["from"], str] # (fromelement is optional) +) +if sys.platform == "win32" and sys.version_info >= (3, 13): + class _ElementCreateVsapiKwargsPadding(TypedDict, total=False): + padding: _Padding + + class _ElementCreateVsapiKwargsMargin(TypedDict, total=False): + padding: _Padding + + class _ElementCreateVsapiKwargsSize(TypedDict): + width: float | str + height: float | str + + _ElementCreateVsapiKwargsDict: TypeAlias = ( + _ElementCreateVsapiKwargsPadding | _ElementCreateVsapiKwargsMargin | _ElementCreateVsapiKwargsSize + ) + _ElementCreateArgs: TypeAlias = ( # noqa: Y047 # It doesn't recognise the usage below for whatever reason + _ElementCreateArgsCrossPlatform + | tuple[Literal["vsapi"], str, int, _ElementCreateVsapiKwargsDict] + | tuple[Literal["vsapi"], str, int, _VsapiStatespec, _ElementCreateVsapiKwargsDict] + ) +else: + _ElementCreateArgs: TypeAlias = _ElementCreateArgsCrossPlatform +_ThemeSettingsValue = TypedDict( + "_ThemeSettingsValue", + { + "configure": dict[str, Any], + "map": dict[str, Iterable[_Statespec]], + "layout": _LayoutSpec, + "element create": _ElementCreateArgs, + }, + total=False, +) +_ThemeSettings: TypeAlias = dict[str, _ThemeSettingsValue] class Style: - master: Incomplete + master: tkinter.Misc tk: _tkinter.TkappType def __init__(self, master: tkinter.Misc | None = None) -> None: ... - def configure(self, style, query_opt: Incomplete | None = None, **kw): ... - def map(self, style, query_opt: Incomplete | None = None, **kw): ... - def lookup(self, style, option, state: Incomplete | None = None, default: Incomplete | None = None): ... - def layout(self, style, layoutspec: Incomplete | None = None): ... - def element_create(self, elementname, etype, *args, **kw) -> None: ... - def element_names(self): ... - def element_options(self, elementname): ... - def theme_create(self, themename, parent: Incomplete | None = None, settings: Incomplete | None = None) -> None: ... - def theme_settings(self, themename, settings) -> None: ... + # For these methods, values given vary between options. Returned values + # seem to be str, but this might not always be the case. + @overload + def configure(self, style: str) -> dict[str, Any] | None: ... # Returns None if no configuration. + @overload + def configure(self, style: str, query_opt: str, **kw: Any) -> Any: ... + @overload + def configure(self, style: str, query_opt: None = None, **kw: Any) -> None: ... + @overload + def map(self, style: str, query_opt: str) -> _Statespec: ... + @overload + def map(self, style: str, query_opt: None = None, **kw: Iterable[_Statespec]) -> dict[str, _Statespec]: ... + def lookup(self, style: str, option: str, state: Iterable[str] | None = None, default: Any | None = None) -> Any: ... + @overload + def layout(self, style: str, layoutspec: _LayoutSpec) -> list[Never]: ... # Always seems to return an empty list + @overload + def layout(self, style: str, layoutspec: None = None) -> _LayoutSpec: ... + @overload + def element_create( + self, + elementname: str, + etype: Literal["image"], + default_image: tkinter._Image | str, + /, + *imagespec: _ImageStatespec, + border: _Padding = ..., + height: float | str = ..., + padding: _Padding = ..., + sticky: str = ..., + width: float | str = ..., + ) -> None: ... + @overload + def element_create(self, elementname: str, etype: Literal["from"], themename: str, fromelement: str = ..., /) -> None: ... + if sys.platform == "win32" and sys.version_info >= (3, 13): # and tk version >= 8.6 + # margin, padding, and (width + height) are mutually exclusive. width + # and height must either both be present or not present at all. Note: + # There are other undocumented options if you look at ttk's source code. + @overload + def element_create( + self, + elementname: str, + etype: Literal["vsapi"], + class_: str, + part: int, + vs_statespec: _VsapiStatespec = ..., + /, + *, + padding: _Padding = ..., + ) -> None: ... + @overload + def element_create( + self, + elementname: str, + etype: Literal["vsapi"], + class_: str, + part: int, + vs_statespec: _VsapiStatespec = ..., + /, + *, + margin: _Padding = ..., + ) -> None: ... + @overload + def element_create( + self, + elementname: str, + etype: Literal["vsapi"], + class_: str, + part: int, + vs_statespec: _VsapiStatespec = ..., + /, + *, + width: float | str, + height: float | str, + ) -> None: ... + + def element_names(self) -> tuple[str, ...]: ... + def element_options(self, elementname: str) -> tuple[str, ...]: ... + def theme_create(self, themename: str, parent: str | None = None, settings: _ThemeSettings | None = None) -> None: ... + def theme_settings(self, themename: str, settings: _ThemeSettings) -> None: ... def theme_names(self) -> tuple[str, ...]: ... @overload def theme_use(self, themename: str) -> None: ... @@ -68,10 +201,10 @@ class Style: def theme_use(self, themename: None = None) -> str: ... class Widget(tkinter.Widget): - def __init__(self, master: tkinter.Misc | None, widgetname, kw: Incomplete | None = None) -> None: ... + def __init__(self, master: tkinter.Misc | None, widgetname, kw=None) -> None: ... def identify(self, x: int, y: int) -> str: ... - def instate(self, statespec, callback: Incomplete | None = None, *args, **kw): ... - def state(self, statespec: Incomplete | None = None): ... + def instate(self, statespec, callback=None, *args, **kw): ... + def state(self, statespec=None): ... class Button(Widget): def __init__( @@ -79,16 +212,16 @@ class Button(Widget): master: tkinter.Misc | None = None, *, class_: str = "", - command: tkinter._ButtonCommand = "", - compound: _TtkCompound = "", + command: str | Callable[[], Any] = "", + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = "", cursor: tkinter._Cursor = "", default: Literal["normal", "active", "disabled"] = "normal", - image: tkinter._ImageSpec = "", + image: tkinter._Image | str = "", name: str = ..., padding=..., # undocumented state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = "", textvariable: tkinter.Variable = ..., underline: int = -1, @@ -99,15 +232,15 @@ class Button(Widget): self, cnf: dict[str, Any] | None = None, *, - command: tkinter._ButtonCommand = ..., - compound: _TtkCompound = ..., + command: str | Callable[[], Any] = ..., + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = ..., cursor: tkinter._Cursor = ..., default: Literal["normal", "active", "disabled"] = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., padding=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: tkinter.Variable = ..., underline: int = ..., @@ -124,17 +257,17 @@ class Checkbutton(Widget): master: tkinter.Misc | None = None, *, class_: str = "", - command: tkinter._ButtonCommand = "", - compound: _TtkCompound = "", + command: str | Callable[[], Any] = "", + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = "", cursor: tkinter._Cursor = "", - image: tkinter._ImageSpec = "", + image: tkinter._Image | str = "", name: str = ..., offvalue: Any = 0, onvalue: Any = 1, padding=..., # undocumented state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = "", textvariable: tkinter.Variable = ..., underline: int = -1, @@ -149,16 +282,16 @@ class Checkbutton(Widget): self, cnf: dict[str, Any] | None = None, *, - command: tkinter._ButtonCommand = ..., - compound: _TtkCompound = ..., + command: str | Callable[[], Any] = ..., + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = ..., cursor: tkinter._Cursor = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., offvalue: Any = ..., onvalue: Any = ..., padding=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: tkinter.Variable = ..., underline: int = ..., @@ -182,18 +315,18 @@ class Entry(Widget, tkinter.Entry): exportselection: bool = True, font: _FontDescription = "TkTextFont", foreground: str = "", - invalidcommand: tkinter._EntryValidateCommand = "", + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", justify: Literal["left", "center", "right"] = "left", name: str = ..., show: str = "", state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", - validatecommand: tkinter._EntryValidateCommand = "", + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", width: int = 20, - xscrollcommand: tkinter._XYScrollCommand = "", + xscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload # type: ignore[override] def configure( @@ -205,17 +338,17 @@ class Entry(Widget, tkinter.Entry): exportselection: bool = ..., font: _FontDescription = ..., foreground: str = ..., - invalidcommand: tkinter._EntryValidateCommand = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., show: str = ..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., width: int = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -230,17 +363,17 @@ class Entry(Widget, tkinter.Entry): exportselection: bool = ..., font: _FontDescription = ..., foreground: str = ..., - invalidcommand: tkinter._EntryValidateCommand = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., show: str = ..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., width: int = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -260,20 +393,20 @@ class Combobox(Entry): font: _FontDescription = ..., # undocumented foreground: str = ..., # undocumented height: int = 10, - invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., # undocumented justify: Literal["left", "center", "right"] = "left", name: str = ..., postcommand: Callable[[], object] | str = "", show=..., # undocumented state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., # undocumented - validatecommand: tkinter._EntryValidateCommand = ..., # undocumented + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., # undocumented values: list[str] | tuple[str, ...] = ..., width: int = 20, - xscrollcommand: tkinter._XYScrollCommand = ..., # undocumented + xscrollcommand: str | Callable[[float, float], object] = ..., # undocumented ) -> None: ... @overload # type: ignore[override] def configure( @@ -286,19 +419,19 @@ class Combobox(Entry): font: _FontDescription = ..., foreground: str = ..., height: int = ..., - invalidcommand: tkinter._EntryValidateCommand = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., postcommand: Callable[[], object] | str = ..., show=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., values: list[str] | tuple[str, ...] = ..., width: int = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -314,19 +447,19 @@ class Combobox(Entry): font: _FontDescription = ..., foreground: str = ..., height: int = ..., - invalidcommand: tkinter._EntryValidateCommand = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., postcommand: Callable[[], object] | str = ..., show=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., values: list[str] | tuple[str, ...] = ..., width: int = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -340,32 +473,32 @@ class Frame(Widget): self, master: tkinter.Misc | None = None, *, - border: tkinter._ScreenUnits = ..., - borderwidth: tkinter._ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., class_: str = "", cursor: tkinter._Cursor = "", - height: tkinter._ScreenUnits = 0, + height: float | str = 0, name: str = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., style: str = "", - takefocus: tkinter._TakeFocusValue = "", - width: tkinter._ScreenUnits = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", + width: float | str = 0, ) -> None: ... @overload def configure( self, cnf: dict[str, Any] | None = None, *, - border: tkinter._ScreenUnits = ..., - borderwidth: tkinter._ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: tkinter._Cursor = ..., - height: tkinter._ScreenUnits = ..., + height: float | str = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - width: tkinter._ScreenUnits = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -376,54 +509,54 @@ class Label(Widget): self, master: tkinter.Misc | None = None, *, - anchor: tkinter._Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = "", - border: tkinter._ScreenUnits = ..., # alias for borderwidth - borderwidth: tkinter._ScreenUnits = ..., # undocumented + border: float | str = ..., # alias for borderwidth + borderwidth: float | str = ..., # undocumented class_: str = "", - compound: _TtkCompound = "", + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = "", cursor: tkinter._Cursor = "", font: _FontDescription = ..., foreground: str = "", - image: tkinter._ImageSpec = "", + image: tkinter._Image | str = "", justify: Literal["left", "center", "right"] = ..., name: str = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", text: float | str = "", textvariable: tkinter.Variable = ..., underline: int = -1, width: int | Literal[""] = "", - wraplength: tkinter._ScreenUnits = ..., + wraplength: float | str = ..., ) -> None: ... @overload def configure( self, cnf: dict[str, Any] | None = None, *, - anchor: tkinter._Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., background: str = ..., - border: tkinter._ScreenUnits = ..., - borderwidth: tkinter._ScreenUnits = ..., - compound: _TtkCompound = ..., + border: float | str = ..., + borderwidth: float | str = ..., + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = ..., cursor: tkinter._Cursor = ..., font: _FontDescription = ..., foreground: str = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., justify: Literal["left", "center", "right"] = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: tkinter.Variable = ..., underline: int = ..., width: int | Literal[""] = ..., - wraplength: tkinter._ScreenUnits = ..., + wraplength: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -434,40 +567,40 @@ class Labelframe(Widget): self, master: tkinter.Misc | None = None, *, - border: tkinter._ScreenUnits = ..., - borderwidth: tkinter._ScreenUnits = ..., # undocumented + border: float | str = ..., + borderwidth: float | str = ..., # undocumented class_: str = "", cursor: tkinter._Cursor = "", - height: tkinter._ScreenUnits = 0, + height: float | str = 0, labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., labelwidget: tkinter.Misc = ..., name: str = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., # undocumented + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., # undocumented style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", text: float | str = "", underline: int = -1, - width: tkinter._ScreenUnits = 0, + width: float | str = 0, ) -> None: ... @overload def configure( self, cnf: dict[str, Any] | None = None, *, - border: tkinter._ScreenUnits = ..., - borderwidth: tkinter._ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., cursor: tkinter._Cursor = ..., - height: tkinter._ScreenUnits = ..., + height: float | str = ..., labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., labelwidget: tkinter.Misc = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., underline: int = ..., - width: tkinter._ScreenUnits = ..., + width: float | str = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -481,16 +614,16 @@ class Menubutton(Widget): master: tkinter.Misc | None = None, *, class_: str = "", - compound: _TtkCompound = "", + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = "", cursor: tkinter._Cursor = "", direction: Literal["above", "below", "left", "right", "flush"] = "below", - image: tkinter._ImageSpec = "", + image: tkinter._Image | str = "", menu: tkinter.Menu = ..., name: str = ..., padding=..., # undocumented state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = "", textvariable: tkinter.Variable = ..., underline: int = -1, @@ -501,15 +634,15 @@ class Menubutton(Widget): self, cnf: dict[str, Any] | None = None, *, - compound: _TtkCompound = ..., + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = ..., cursor: tkinter._Cursor = ..., direction: Literal["above", "below", "left", "right", "flush"] = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., menu: tkinter.Menu = ..., padding=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: tkinter.Variable = ..., underline: int = ..., @@ -530,7 +663,7 @@ class Notebook(Widget): name: str = ..., padding: _Padding = ..., style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., width: int = 0, ) -> None: ... @overload @@ -542,7 +675,7 @@ class Notebook(Widget): height: int = ..., padding: _Padding = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., width: int = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload @@ -559,16 +692,16 @@ class Notebook(Widget): # `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 = ..., + compound: Literal["top", "left", "center", "right", "bottom", "none"] = ..., underline: int = ..., ) -> None: ... - def forget(self, tab_id) -> None: ... + def forget(self, tab_id) -> None: ... # type: ignore[override] def hide(self, tab_id) -> None: ... def identify(self, x: int, y: int) -> str: ... def index(self, tab_id): ... def insert(self, pos, child, **kw) -> None: ... - def select(self, tab_id: Incomplete | None = None): ... - def tab(self, tab_id, option: Incomplete | None = None, **kw): ... + def select(self, tab_id=None): ... + def tab(self, tab_id, option=None, **kw): ... def tabs(self): ... def enable_traversal(self) -> None: ... @@ -584,7 +717,7 @@ class Panedwindow(Widget, tkinter.PanedWindow): name: str = ..., orient: Literal["vertical", "horizontal"] = "vertical", # can't be changed with configure() style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", width: int = 0, ) -> None: ... def add(self, child: tkinter.Widget, *, weight: int = ..., **kw) -> None: ... @@ -596,7 +729,7 @@ class Panedwindow(Widget, tkinter.PanedWindow): cursor: tkinter._Cursor = ..., height: int = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., width: int = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload @@ -610,15 +743,15 @@ class Panedwindow(Widget, tkinter.PanedWindow): cursor: tkinter._Cursor = ..., height: int = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., width: int = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... - forget: Incomplete + forget = tkinter.PanedWindow.forget def insert(self, pos, child, **kw) -> None: ... - def pane(self, pane, option: Incomplete | None = None, **kw): ... - def sashpos(self, index, newpos: Incomplete | None = None): ... + def pane(self, pane, option=None, **kw): ... + def sashpos(self, index, newpos=None): ... PanedWindow = Panedwindow @@ -629,14 +762,14 @@ class Progressbar(Widget): *, class_: str = "", cursor: tkinter._Cursor = "", - length: tkinter._ScreenUnits = 100, + length: float | str = 100, maximum: float = 100, mode: Literal["determinate", "indeterminate"] = "determinate", name: str = ..., orient: Literal["horizontal", "vertical"] = "horizontal", phase: int = 0, # docs say read-only but assigning int to this works style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", value: float = 0.0, variable: tkinter.IntVar | tkinter.DoubleVar = ..., ) -> None: ... @@ -646,13 +779,13 @@ class Progressbar(Widget): cnf: dict[str, Any] | None = None, *, cursor: tkinter._Cursor = ..., - length: tkinter._ScreenUnits = ..., + length: float | str = ..., maximum: float = ..., mode: Literal["determinate", "indeterminate"] = ..., orient: Literal["horizontal", "vertical"] = ..., phase: int = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., value: float = ..., variable: tkinter.IntVar | tkinter.DoubleVar = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @@ -669,15 +802,15 @@ class Radiobutton(Widget): master: tkinter.Misc | None = None, *, class_: str = "", - command: tkinter._ButtonCommand = "", - compound: _TtkCompound = "", + command: str | Callable[[], Any] = "", + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = "", cursor: tkinter._Cursor = "", - image: tkinter._ImageSpec = "", + image: tkinter._Image | str = "", name: str = ..., padding=..., # undocumented state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = "", textvariable: tkinter.Variable = ..., underline: int = -1, @@ -690,14 +823,14 @@ class Radiobutton(Widget): self, cnf: dict[str, Any] | None = None, *, - command: tkinter._ButtonCommand = ..., - compound: _TtkCompound = ..., + command: str | Callable[[], Any] = ..., + compound: Literal["", "text", "image", "top", "left", "center", "right", "bottom", "none"] = ..., cursor: tkinter._Cursor = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., padding=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., text: float | str = ..., textvariable: tkinter.Variable = ..., underline: int = ..., @@ -720,12 +853,12 @@ class Scale(Widget, tkinter.Scale): # type: ignore[misc] command: str | Callable[[str], object] = "", cursor: tkinter._Cursor = "", from_: float = 0, - length: tkinter._ScreenUnits = 100, + length: float | str = 100, name: str = ..., orient: Literal["horizontal", "vertical"] = "horizontal", state: str = ..., # undocumented style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., to: float = 1.0, value: float = 0, variable: tkinter.IntVar | tkinter.DoubleVar = ..., @@ -738,11 +871,11 @@ class Scale(Widget, tkinter.Scale): # type: ignore[misc] command: str | Callable[[str], object] = ..., cursor: tkinter._Cursor = ..., from_: float = ..., - length: tkinter._ScreenUnits = ..., + length: float | str = ..., orient: Literal["horizontal", "vertical"] = ..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., to: float = ..., value: float = ..., variable: tkinter.IntVar | tkinter.DoubleVar = ..., @@ -758,11 +891,11 @@ class Scale(Widget, tkinter.Scale): # type: ignore[misc] command: str | Callable[[str], object] = ..., cursor: tkinter._Cursor = ..., from_: float = ..., - length: tkinter._ScreenUnits = ..., + length: float | str = ..., orient: Literal["horizontal", "vertical"] = ..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., to: float = ..., value: float = ..., variable: tkinter.IntVar | tkinter.DoubleVar = ..., @@ -783,7 +916,7 @@ class Scrollbar(Widget, tkinter.Scrollbar): # type: ignore[misc] name: str = ..., orient: Literal["horizontal", "vertical"] = "vertical", style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", ) -> None: ... @overload # type: ignore[override] def configure( @@ -794,7 +927,7 @@ class Scrollbar(Widget, tkinter.Scrollbar): # type: ignore[misc] cursor: tkinter._Cursor = ..., orient: Literal["horizontal", "vertical"] = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -808,7 +941,7 @@ class Scrollbar(Widget, tkinter.Scrollbar): # type: ignore[misc] cursor: tkinter._Cursor = ..., orient: Literal["horizontal", "vertical"] = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def config(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -823,7 +956,7 @@ class Separator(Widget): name: str = ..., orient: Literal["horizontal", "vertical"] = "horizontal", style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", ) -> None: ... @overload def configure( @@ -833,7 +966,7 @@ class Separator(Widget): cursor: tkinter._Cursor = ..., orient: Literal["horizontal", "vertical"] = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -848,7 +981,7 @@ class Sizegrip(Widget): cursor: tkinter._Cursor = ..., name: str = ..., style: str = "", - takefocus: tkinter._TakeFocusValue = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", ) -> None: ... @overload def configure( @@ -857,7 +990,7 @@ class Sizegrip(Widget): *, cursor: tkinter._Cursor = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -878,21 +1011,21 @@ class Spinbox(Entry): format: str = "", from_: float = 0, increment: float = 1, - invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., # undocumented justify: Literal["left", "center", "right"] = ..., # undocumented name: str = ..., show=..., # undocumented state: str = "normal", style: str = "", - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., # undocumented to: float = 0, validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = "none", - validatecommand: tkinter._EntryValidateCommand = "", + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = "", values: list[str] | tuple[str, ...] = ..., width: int = ..., # undocumented wrap: bool = False, - xscrollcommand: tkinter._XYScrollCommand = "", + xscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload # type: ignore[override] def configure( @@ -908,26 +1041,27 @@ class Spinbox(Entry): format: str = ..., from_: float = ..., increment: float = ..., - invalidcommand: tkinter._EntryValidateCommand = ..., + invalidcommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., justify: Literal["left", "center", "right"] = ..., show=..., state: str = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., textvariable: tkinter.Variable = ..., to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., - validatecommand: tkinter._EntryValidateCommand = ..., + validatecommand: str | list[str] | tuple[str, ...] | Callable[[], bool] = ..., values: list[str] | tuple[str, ...] = ..., width: int = ..., wrap: bool = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... config = configure # type: ignore[assignment] def set(self, value: Any) -> None: ... +@type_check_only class _TreeviewItemDict(TypedDict): text: str image: list[str] | Literal[""] # no idea why it's wrapped in list @@ -935,6 +1069,7 @@ class _TreeviewItemDict(TypedDict): open: bool # actually 0 or 1 tags: list[str] | Literal[""] +@type_check_only class _TreeviewTagDict(TypedDict): # There is also 'text' and 'anchor', but they don't seem to do anything, using them is likely a bug foreground: str @@ -942,18 +1077,20 @@ class _TreeviewTagDict(TypedDict): font: _FontDescription image: str # not wrapped in list :D +@type_check_only class _TreeviewHeaderDict(TypedDict): text: str image: list[str] | Literal[""] - anchor: tkinter._Anchor + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] command: str state: str # Doesn't seem to appear anywhere else than in these dicts +@type_check_only class _TreeviewColumnDict(TypedDict): width: int minwidth: int stretch: bool # actually 0 or 1 - anchor: tkinter._Anchor + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] id: str class Treeview(Widget, tkinter.XView, tkinter.YView): @@ -975,9 +1112,9 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): # surprised if someone is using it. show: Literal["tree", "headings", "tree headings", ""] | list[str] | tuple[str, ...] = ("tree", "headings"), style: str = "", - takefocus: tkinter._TakeFocusValue = ..., - xscrollcommand: tkinter._XYScrollCommand = "", - yscrollcommand: tkinter._XYScrollCommand = "", + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + xscrollcommand: str | Callable[[float, float], object] = "", + yscrollcommand: str | Callable[[float, float], object] = "", ) -> None: ... @overload def configure( @@ -992,9 +1129,9 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): selectmode: Literal["extended", "browse", "none"] = ..., show: Literal["tree", "headings", "tree headings", ""] | list[str] | tuple[str, ...] = ..., style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - xscrollcommand: tkinter._XYScrollCommand = ..., - yscrollcommand: tkinter._XYScrollCommand = ..., + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = ..., + xscrollcommand: str | Callable[[float, float], object] = ..., + yscrollcommand: str | Callable[[float, float], object] = ..., ) -> dict[str, tuple[str, str, str, Any, Any]] | None: ... @overload def configure(self, cnf: str) -> tuple[str, str, str, Any, Any]: ... @@ -1021,7 +1158,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): width: int = ..., minwidth: int = ..., stretch: bool = ..., - anchor: tkinter._Anchor = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., # id is read-only ) -> _TreeviewColumnDict | None: ... def delete(self, *items: str | int) -> None: ... @@ -1050,8 +1187,8 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): option: None = None, *, text: str = ..., - image: tkinter._ImageSpec = ..., - anchor: tkinter._Anchor = ..., + image: tkinter._Image | str = ..., + anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = ..., command: str | Callable[[], object] = ..., ) -> None: ... # Internal Method. Leave untyped: @@ -1069,7 +1206,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): *, id: str | int = ..., # same as iid text: str = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., values: list[Any] | tuple[Any, ...] = ..., open: bool = ..., tags: str | list[str] | tuple[str, ...] = ..., @@ -1095,12 +1232,12 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): option: None = None, *, text: str = ..., - image: tkinter._ImageSpec = ..., + image: tkinter._Image | str = ..., values: list[Any] | tuple[Any, ...] | Literal[""] = ..., open: bool = ..., tags: str | list[str] | tuple[str, ...] = ..., ) -> None: ... - def move(self, item: str | int, parent: str, index: int) -> None: ... + def move(self, item: str | int, parent: str, index: int | Literal["end"]) -> None: ... reattach = move def next(self, item: str | int) -> str: ... # returning empty string means last item def parent(self, item: str | int) -> str: ... @@ -1155,8 +1292,8 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): foreground: str = ..., background: str = ..., font: _FontDescription = ..., - image: tkinter._ImageSpec = ..., - ) -> _TreeviewTagDict | Any: ... # can be None but annoying to check + image: tkinter._Image | str = ..., + ) -> _TreeviewTagDict | MaybeNone: ... # can be None but annoying to check @overload def tag_has(self, tagname: str, item: None = None) -> tuple[str, ...]: ... @overload @@ -1174,18 +1311,18 @@ class LabeledScale(Frame): from_: float = 0, to: float = 10, *, - border: tkinter._ScreenUnits = ..., - borderwidth: tkinter._ScreenUnits = ..., + border: float | str = ..., + borderwidth: float | str = ..., class_: str = "", compound: Literal["top", "bottom"] = "top", cursor: tkinter._Cursor = "", - height: tkinter._ScreenUnits = 0, + height: float | str = 0, name: str = ..., padding: _Padding = ..., - relief: tkinter._Relief = ..., + relief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = ..., style: str = "", - takefocus: tkinter._TakeFocusValue = "", - width: tkinter._ScreenUnits = 0, + takefocus: bool | Literal[0, 1, ""] | Callable[[str], bool | None] = "", + width: float | str = 0, ) -> None: ... # destroy is overridden, signature does not change value: Any diff --git a/mypy/typeshed/stdlib/token.pyi b/mypy/typeshed/stdlib/token.pyi index 668987d7c2bfd..fd1b10da1d12e 100644 --- a/mypy/typeshed/stdlib/token.pyi +++ b/mypy/typeshed/stdlib/token.pyi @@ -1,4 +1,5 @@ import sys +from typing import Final __all__ = [ "AMPER", @@ -76,84 +77,92 @@ if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] if sys.version_info >= (3, 12): - __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] + __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START", "EXACT_TOKEN_TYPES"] -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 -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 -AT: int -RARROW: int -ELLIPSIS: int -ATEQUAL: int +if sys.version_info >= (3, 14): + __all__ += ["TSTRING_START", "TSTRING_MIDDLE", "TSTRING_END"] + +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] +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] +AT: Final[int] +RARROW: Final[int] +ELLIPSIS: Final[int] +ATEQUAL: Final[int] if sys.version_info < (3, 13): - AWAIT: int - ASYNC: int -OP: int -ERRORTOKEN: int -N_TOKENS: int -NT_OFFSET: int -tok_name: dict[int, str] -COMMENT: int -NL: int -ENCODING: int -TYPE_COMMENT: int -TYPE_IGNORE: int -COLONEQUAL: int -EXACT_TOKEN_TYPES: dict[str, int] + AWAIT: Final[int] + ASYNC: Final[int] +OP: Final[int] +ERRORTOKEN: Final[int] +N_TOKENS: Final[int] +NT_OFFSET: Final[int] +tok_name: Final[dict[int, str]] +COMMENT: Final[int] +NL: Final[int] +ENCODING: Final[int] +TYPE_COMMENT: Final[int] +TYPE_IGNORE: Final[int] +COLONEQUAL: Final[int] +EXACT_TOKEN_TYPES: Final[dict[str, int]] if sys.version_info >= (3, 10): - SOFT_KEYWORD: int + SOFT_KEYWORD: Final[int] if sys.version_info >= (3, 12): - EXCLAMATION: int - FSTRING_END: int - FSTRING_MIDDLE: int - FSTRING_START: int + EXCLAMATION: Final[int] + FSTRING_END: Final[int] + FSTRING_MIDDLE: Final[int] + FSTRING_START: Final[int] + +if sys.version_info >= (3, 14): + TSTRING_START: Final[int] + TSTRING_MIDDLE: Final[int] + TSTRING_END: Final[int] def ISTERMINAL(x: int) -> bool: ... def ISNONTERMINAL(x: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 3d2a93865df8e..00a24b4eea07d 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -3,9 +3,14 @@ from _typeshed import FileDescriptorOrPath 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_extensions import TypeAlias +from typing import Any, Final, NamedTuple, TextIO, type_check_only +from typing_extensions import TypeAlias, disjoint_base + +if sys.version_info < (3, 12): + # Avoid double assignment to Final name by imports, which pyright objects to. + # EXACT_TOKEN_TYPES is already defined by 'from token import *' above + # in Python 3.12+. + from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES __all__ = [ "AMPER", @@ -88,16 +93,21 @@ if sys.version_info >= (3, 10): __all__ += ["SOFT_KEYWORD"] if sys.version_info >= (3, 12): - __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START"] + __all__ += ["EXCLAMATION", "FSTRING_END", "FSTRING_MIDDLE", "FSTRING_START", "EXACT_TOKEN_TYPES"] if sys.version_info >= (3, 13): __all__ += ["TokenError", "open"] -cookie_re: Pattern[str] -blank_re: Pattern[bytes] +if sys.version_info >= (3, 14): + __all__ += ["TSTRING_START", "TSTRING_MIDDLE", "TSTRING_END"] + +cookie_re: Final[Pattern[str]] +blank_re: Final[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 @@ -105,9 +115,16 @@ class _TokenInfo(NamedTuple): end: _Position line: str -class TokenInfo(_TokenInfo): - @property - def exact_type(self) -> int: ... +if sys.version_info >= (3, 12): + class TokenInfo(_TokenInfo): + @property + def exact_type(self) -> int: ... + +else: + @disjoint_base + class TokenInfo(_TokenInfo): + @property + def exact_type(self) -> int: ... # Backwards compatible tokens can be sequences of a shorter length too _Token: TypeAlias = TokenInfo | Sequence[int | str | _Position] @@ -123,62 +140,64 @@ class Untokenizer: prev_col: int encoding: str | None def add_whitespace(self, start: _Position) -> None: ... + if sys.version_info >= (3, 12): + def add_backslash_continuation(self, start: _Position) -> None: ... + def untokenize(self, iterable: Iterable[_Token]) -> str: ... def compat(self, token: Sequence[int | str], iterable: Iterable[_Token]) -> None: ... if sys.version_info >= (3, 12): def escape_brackets(self, token: str) -> str: ... -# the docstring says "returns bytes" but is incorrect -- -# if the ENCODING token is missing, it skips the encode -def untokenize(iterable: Iterable[_Token]) -> Any: ... +# Returns str, unless the ENCODING token is present, in which case it returns bytes. +def untokenize(iterable: Iterable[_Token]) -> str | 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 def maybe(*choices: str) -> str: ... # undocumented -Whitespace: str # undocumented -Comment: str # undocumented -Ignore: str # undocumented -Name: str # undocumented - -Hexnumber: str # undocumented -Binnumber: str # undocumented -Octnumber: str # undocumented -Decnumber: str # undocumented -Intnumber: str # undocumented -Exponent: str # undocumented -Pointfloat: str # undocumented -Expfloat: str # undocumented -Floatnumber: str # undocumented -Imagnumber: str # undocumented -Number: str # undocumented +Whitespace: Final[str] # undocumented +Comment: Final[str] # undocumented +Ignore: Final[str] # undocumented +Name: Final[str] # undocumented + +Hexnumber: Final[str] # undocumented +Binnumber: Final[str] # undocumented +Octnumber: Final[str] # undocumented +Decnumber: Final[str] # undocumented +Intnumber: Final[str] # undocumented +Exponent: Final[str] # undocumented +Pointfloat: Final[str] # undocumented +Expfloat: Final[str] # undocumented +Floatnumber: Final[str] # undocumented +Imagnumber: Final[str] # undocumented +Number: Final[str] # undocumented def _all_string_prefixes() -> set[str]: ... # undocumented -StringPrefix: str # undocumented +StringPrefix: Final[str] # undocumented -Single: str # undocumented -Double: str # undocumented -Single3: str # undocumented -Double3: str # undocumented -Triple: str # undocumented -String: str # undocumented +Single: Final[str] # undocumented +Double: Final[str] # undocumented +Single3: Final[str] # undocumented +Double3: Final[str] # undocumented +Triple: Final[str] # undocumented +String: Final[str] # undocumented -Special: str # undocumented -Funny: str # undocumented +Special: Final[str] # undocumented +Funny: Final[str] # undocumented -PlainToken: str # undocumented -Token: str # undocumented +PlainToken: Final[str] # undocumented +Token: Final[str] # undocumented -ContStr: str # undocumented -PseudoExtras: str # undocumented -PseudoToken: str # undocumented +ContStr: Final[str] # undocumented +PseudoExtras: Final[str] # undocumented +PseudoToken: Final[str] # undocumented -endpats: dict[str, str] # undocumented -single_quoted: set[str] # undocumented -triple_quoted: set[str] # undocumented +endpats: Final[dict[str, str]] # undocumented +single_quoted: Final[set[str]] # undocumented +triple_quoted: Final[set[str]] # undocumented -tabsize: int # undocumented +tabsize: Final = 8 # undocumented diff --git a/mypy/typeshed/stdlib/tomllib.pyi b/mypy/typeshed/stdlib/tomllib.pyi index d559568b912b5..4ff4097f8313a 100644 --- a/mypy/typeshed/stdlib/tomllib.pyi +++ b/mypy/typeshed/stdlib/tomllib.pyi @@ -1,10 +1,26 @@ +import sys from _typeshed import SupportsRead from collections.abc import Callable -from typing import Any +from typing import Any, overload +from typing_extensions import deprecated __all__ = ("loads", "load", "TOMLDecodeError") -class TOMLDecodeError(ValueError): ... +if sys.version_info >= (3, 14): + class TOMLDecodeError(ValueError): + msg: str + doc: str + pos: int + lineno: int + colno: int + @overload + def __init__(self, msg: str, doc: str, pos: int) -> None: ... + @overload + @deprecated("Deprecated since Python 3.14. Set the 'msg', 'doc' and 'pos' arguments only.") + def __init__(self, msg: str | type = ..., doc: str | type = ..., pos: int | type = ..., *args: Any) -> None: ... + +else: + class TOMLDecodeError(ValueError): ... def load(fp: SupportsRead[bytes], /, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ... def loads(s: str, /, *, parse_float: Callable[[str], Any] = ...) -> dict[str, Any]: ... diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi index 04390f1191951..7e7cc1e9ac54a 100644 --- a/mypy/typeshed/stdlib/trace.pyi +++ b/mypy/typeshed/stdlib/trace.pyi @@ -75,11 +75,7 @@ class Trace: def runctx( self, cmd: str | types.CodeType, globals: Mapping[str, Any] | None = None, locals: Mapping[str, Any] | None = None ) -> None: ... - if sys.version_info >= (3, 9): - def runfunc(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... - else: - def runfunc(self, func: Callable[_P, _T], *args: _P.args, **kw: _P.kwargs) -> _T: ... - + def runfunc(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def file_module_function_of(self, frame: types.FrameType) -> _FileModuleFunction: ... def globaltrace_trackcallers(self, frame: types.FrameType, why: str, arg: Any) -> None: ... def globaltrace_countfuncs(self, frame: types.FrameType, why: str, arg: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 075c0f4b9de8f..d587295cd1cf7 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import SupportsWrite, Unused from collections.abc import Generator, Iterable, Iterator, Mapping from types import FrameType, TracebackType -from typing import Any, Literal, overload +from typing import Any, ClassVar, Literal, overload from typing_extensions import Self, TypeAlias, deprecated __all__ = [ @@ -27,6 +27,9 @@ __all__ = [ "walk_tb", ] +if sys.version_info >= (3, 14): + __all__ += ["print_list"] + _FrameSummaryTuple: TypeAlias = tuple[str, int, str, str | None] def print_tb(tb: TracebackType | None, limit: int | None = None, file: SupportsWrite[str] | None = None) -> None: ... @@ -81,8 +84,6 @@ def print_stack(f: FrameType | None = None, limit: int | None = None, file: Supp def extract_tb(tb: TracebackType | None, limit: int | None = None) -> StackSummary: ... def extract_stack(f: FrameType | None = None, limit: int | None = None) -> StackSummary: ... def format_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple]) -> list[str]: ... - -# undocumented def print_list(extracted_list: Iterable[FrameSummary | _FrameSummaryTuple], file: SupportsWrite[str] | None = None) -> None: ... if sys.version_info >= (3, 13): @@ -113,20 +114,31 @@ if sys.version_info >= (3, 11): def emit(self, text_gen: str | Iterable[str], margin_char: str | None = None) -> Generator[str, None, None]: ... class TracebackException: - __cause__: TracebackException - __context__: TracebackException + __cause__: TracebackException | None + __context__: TracebackException | None + if sys.version_info >= (3, 11): + exceptions: list[TracebackException] | None __suppress_context__: bool + if sys.version_info >= (3, 11): + __notes__: list[str] | None stack: StackSummary + + # These fields only exist for `SyntaxError`s, but there is no way to express that in the type system. filename: str - lineno: int + lineno: str | None + if sys.version_info >= (3, 10): + end_lineno: str | None text: str offset: int + if sys.version_info >= (3, 10): + end_offset: int | None msg: str + if sys.version_info >= (3, 13): @property def exc_type_str(self) -> str: ... @property - @deprecated("Deprecated in 3.13. Use exc_type_str instead.") + @deprecated("Deprecated since Python 3.13. Use `exc_type_str` instead.") def exc_type(self) -> type[BaseException] | None: ... else: exc_type: type[BaseException] @@ -218,6 +230,7 @@ class TracebackException: ) -> Self: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 11): def format(self, *, chain: bool = True, _ctx: _ExceptionPrintContext | None = None) -> Generator[str, None, None]: ... else: @@ -231,7 +244,24 @@ 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, 13): + __slots__ = ( + "filename", + "lineno", + "end_lineno", + "colno", + "end_colno", + "name", + "_lines", + "_lines_dedented", + "locals", + "_code", + ) + elif sys.version_info >= (3, 11): + __slots__ = ("filename", "lineno", "end_lineno", "colno", "end_colno", "name", "_line", "locals") + else: + __slots__ = ("filename", "lineno", "name", "_line", "locals") if sys.version_info >= (3, 11): def __init__( self, @@ -276,9 +306,12 @@ 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]: ... + __hash__: ClassVar[None] # type: ignore[assignment] class StackSummary(list[FrameSummary]): @classmethod diff --git a/mypy/typeshed/stdlib/tracemalloc.pyi b/mypy/typeshed/stdlib/tracemalloc.pyi index e721e414138ba..31d8f74456395 100644 --- a/mypy/typeshed/stdlib/tracemalloc.pyi +++ b/mypy/typeshed/stdlib/tracemalloc.pyi @@ -32,6 +32,7 @@ class Filter(BaseFilter): ) -> None: ... class Statistic: + __slots__ = ("traceback", "size", "count") count: int size: int traceback: Traceback @@ -40,6 +41,7 @@ class Statistic: def __hash__(self) -> int: ... class StatisticDiff: + __slots__ = ("traceback", "size", "size_diff", "count", "count_diff") count: int count_diff: int size: int @@ -52,6 +54,7 @@ class StatisticDiff: _FrameTuple: TypeAlias = tuple[str, int] class Frame: + __slots__ = ("_frame",) @property def filename(self) -> str: ... @property @@ -69,12 +72,10 @@ class Frame: def __ge__(self, other: Frame, NotImplemented: Any = ...) -> bool: ... def __le__(self, other: Frame, NotImplemented: Any = ...) -> bool: ... -if sys.version_info >= (3, 9): - _TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple], int | None] | tuple[int, int, Sequence[_FrameTuple]] -else: - _TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple]] +_TraceTuple: TypeAlias = tuple[int, int, Sequence[_FrameTuple], int | None] | tuple[int, int, Sequence[_FrameTuple]] class Trace: + __slots__ = ("_trace",) @property def domain(self) -> int: ... @property @@ -86,13 +87,10 @@ class Trace: def __hash__(self) -> int: ... class Traceback(Sequence[Frame]): - if sys.version_info >= (3, 9): - @property - def total_nframe(self) -> int | None: ... - def __init__(self, frames: Sequence[_FrameTuple], total_nframe: int | None = None) -> None: ... - else: - def __init__(self, frames: Sequence[_FrameTuple]) -> None: ... - + __slots__ = ("_frames", "_total_nframe") + @property + def total_nframe(self) -> int | None: ... + def __init__(self, frames: Sequence[_FrameTuple], total_nframe: int | None = None) -> None: ... def format(self, limit: int | None = None, most_recent_first: bool = False) -> list[str]: ... @overload def __getitem__(self, index: SupportsIndex) -> Frame: ... diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi index 0611879cf1b29..ca3f0013b20ec 100644 --- a/mypy/typeshed/stdlib/tty.pyi +++ b/mypy/typeshed/stdlib/tty.pyi @@ -15,13 +15,13 @@ if sys.platform != "win32": _FD: TypeAlias = int | IO[str] # XXX: Undocumented integer constants - IFLAG: Final[int] - OFLAG: Final[int] - CFLAG: Final[int] - LFLAG: Final[int] - ISPEED: Final[int] - OSPEED: Final[int] - CC: Final[int] + IFLAG: Final = 0 + OFLAG: Final = 1 + CFLAG: Final = 2 + LFLAG: Final = 3 + ISPEED: Final = 4 + OSPEED: Final = 5 + CC: Final = 6 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 29d2893039278..b5f536d0e28e5 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -1,8 +1,10 @@ import sys -from collections.abc import Callable, Sequence +from _typeshed import StrPath +from collections.abc import Callable, Generator, Sequence +from contextlib import contextmanager from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar -from typing import Any, ClassVar, overload -from typing_extensions import Self, TypeAlias +from typing import Any, ClassVar, Literal, TypedDict, overload, type_check_only +from typing_extensions import Self, TypeAlias, deprecated, disjoint_base __all__ = [ "ScrolledCanvas", @@ -128,6 +130,9 @@ __all__ = [ "Terminator", ] +if sys.version_info >= (3, 14): + __all__ += ["fill", "no_animation", "poly", "save"] + if sys.version_info >= (3, 12): __all__ += ["teleport"] @@ -141,24 +146,51 @@ if sys.version_info < (3, 13): _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] +@type_check_only +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]] -class Vec2D(tuple[float, float]): - def __new__(cls, x: float, y: float) -> Self: ... - def __add__(self, other: tuple[float, float]) -> Vec2D: ... # type: ignore[override] - @overload # type: ignore[override] - def __mul__(self, other: Vec2D) -> float: ... - @overload - def __mul__(self, other: float) -> Vec2D: ... - def __rmul__(self, other: float) -> Vec2D: ... # type: ignore[override] - def __sub__(self, other: tuple[float, float]) -> Vec2D: ... - def __neg__(self) -> Vec2D: ... - def __abs__(self) -> float: ... - def rotate(self, angle: float) -> Vec2D: ... +if sys.version_info >= (3, 12): + class Vec2D(tuple[float, float]): + def __new__(cls, x: float, y: float) -> Self: ... + def __add__(self, other: tuple[float, float]) -> Vec2D: ... # type: ignore[override] + @overload # type: ignore[override] + def __mul__(self, other: Vec2D) -> float: ... + @overload + def __mul__(self, other: float) -> Vec2D: ... + def __rmul__(self, other: float) -> Vec2D: ... # type: ignore[override] + def __sub__(self, other: tuple[float, float]) -> Vec2D: ... + def __neg__(self) -> Vec2D: ... + def __abs__(self) -> float: ... + def rotate(self, angle: float) -> Vec2D: ... + +else: + @disjoint_base + class Vec2D(tuple[float, float]): + def __new__(cls, x: float, y: float) -> Self: ... + def __add__(self, other: tuple[float, float]) -> Vec2D: ... # type: ignore[override] + @overload # type: ignore[override] + def __mul__(self, other: Vec2D) -> float: ... + @overload + def __mul__(self, other: float) -> Vec2D: ... + def __rmul__(self, other: float) -> Vec2D: ... # type: ignore[override] + def __sub__(self, other: tuple[float, float]) -> Vec2D: ... + def __neg__(self) -> Vec2D: ... + def __abs__(self) -> float: ... + def rotate(self, angle: float) -> Vec2D: ... # Does not actually inherit from Canvas, but dynamically gets all methods of Canvas class ScrolledCanvas(Canvas, Frame): # type: ignore[misc] @@ -189,16 +221,20 @@ class Terminator(Exception): ... class TurtleGraphicsError(Exception): ... class Shape: - def __init__(self, type_: str, data: _PolygonCoords | PhotoImage | None = None) -> None: ... + def __init__( + self, type_: Literal["polygon", "image", "compound"], data: _PolygonCoords | PhotoImage | None = None + ) -> None: ... def addcomponent(self, poly: _PolygonCoords, fill: _Color, outline: _Color | None = None) -> None: ... class TurtleScreen(TurtleScreenBase): - def __init__(self, cv: Canvas, mode: str = "standard", colormode: float = 1.0, delay: int = 10) -> None: ... + def __init__( + self, cv: Canvas, mode: Literal["standard", "logo", "world"] = "standard", colormode: float = 1.0, delay: int = 10 + ) -> None: ... def clear(self) -> None: ... @overload def mode(self, mode: None = None) -> str: ... @overload - def mode(self, mode: str) -> None: ... + def mode(self, mode: Literal["standard", "logo", "world"]) -> None: ... def setworldcoordinates(self, llx: float, lly: float, urx: float, ury: float) -> None: ... def register_shape(self, name: str, shape: _PolygonCoords | Shape | None = None) -> None: ... @overload @@ -221,12 +257,16 @@ class TurtleScreen(TurtleScreenBase): def delay(self, delay: None = None) -> int: ... @overload def delay(self, delay: int) -> None: ... + if sys.version_info >= (3, 14): + @contextmanager + def no_animation(self) -> Generator[None]: ... + def update(self) -> None: ... def window_width(self) -> int: ... def window_height(self) -> int: ... def getcanvas(self) -> Canvas: ... def getshapes(self) -> list[str]: ... - def onclick(self, fun: Callable[[float, float], object], btn: int = 1, add: Any | None = None) -> None: ... + def onclick(self, fun: Callable[[float, float], object], btn: int = 1, add: bool | None = None) -> None: ... def onkey(self, fun: Callable[[], object], key: str) -> None: ... def listen(self, xdummy: float | None = None, ydummy: float | None = None) -> None: ... def ontimer(self, fun: Callable[[], object], t: int = 0) -> None: ... @@ -239,6 +279,8 @@ class TurtleScreen(TurtleScreenBase): # Looks like if self.cv is not a ScrolledCanvas, this could return a tuple as well @overload def screensize(self, canvwidth: int, canvheight: int, bg: _Color | None = None) -> None: ... + if sys.version_info >= (3, 14): + def save(self, filename: StrPath, *, overwrite: bool = False) -> None: ... onscreenclick = onclick resetscreen = reset clearscreen = clear @@ -251,7 +293,7 @@ class TNavigator: DEFAULT_MODE: str DEFAULT_ANGLEOFFSET: int DEFAULT_ANGLEORIENT: int - def __init__(self, mode: str = "standard") -> None: ... + def __init__(self, mode: Literal["standard", "logo", "world"] = "standard") -> None: ... def reset(self) -> None: ... def degrees(self, fullcircle: float = 360.0) -> None: ... def radians(self) -> None: ... @@ -283,6 +325,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 @@ -294,11 +337,11 @@ class TNavigator: seth = setheading class TPen: - def __init__(self, resizemode: str = "noresize") -> None: ... + def __init__(self, resizemode: Literal["auto", "user", "noresize"] = "noresize") -> None: ... @overload def resizemode(self, rmode: None = None) -> str: ... @overload - def resizemode(self, rmode: str) -> None: ... + def resizemode(self, rmode: Literal["auto", "user", "noresize"]) -> None: ... @overload def pensize(self, width: None = None) -> int: ... @overload @@ -350,7 +393,7 @@ class TPen: fillcolor: _Color = ..., pensize: int = ..., speed: int = ..., - resizemode: str = ..., + resizemode: Literal["auto", "user", "noresize"] = ..., stretchfactor: tuple[float, float] = ..., outline: int = ..., tilt: float = ..., @@ -363,7 +406,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__( @@ -403,6 +446,7 @@ class RawTurtle(TPen, TNavigator): def get_shapepoly(self) -> _PolygonCoords | None: ... if sys.version_info < (3, 13): + @deprecated("Deprecated since Python 3.1; removed in Python 3.13. Use `tiltangle()` instead.") def settiltangle(self, angle: float) -> None: ... @overload @@ -417,12 +461,25 @@ class RawTurtle(TPen, TNavigator): def clearstamp(self, stampid: int | tuple[int, ...]) -> None: ... def clearstamps(self, n: int | None = None) -> None: ... def filling(self) -> bool: ... + if sys.version_info >= (3, 14): + @contextmanager + def fill(self) -> Generator[None]: ... + def begin_fill(self) -> None: ... def end_fill(self) -> None: ... - def dot(self, size: int | None = None, *color: _Color) -> None: ... + @overload + def dot(self, size: int | _Color | None = None) -> None: ... + @overload + def dot(self, size: int | None, color: _Color, /) -> None: ... + @overload + def dot(self, size: int | None, r: float, g: float, b: float, /) -> None: ... def write( self, arg: object, move: bool = False, align: str = "left", font: tuple[str, int, str] = ("Arial", 8, "normal") ) -> None: ... + if sys.version_info >= (3, 14): + @contextmanager + def poly(self) -> Generator[None]: ... + def begin_poly(self) -> None: ... def end_poly(self) -> None: ... def get_poly(self) -> _PolygonCoords | None: ... @@ -457,19 +514,8 @@ Pen = Turtle def write_docstringdict(filename: str = "turtle_docstringdict") -> None: ... -# Note: it's somewhat unfortunate that we have to copy the function signatures. -# It would be nice if we could partially reduce the redundancy by doing something -# like the following: -# -# _screen: Screen -# clear = _screen.clear -# -# However, it seems pytype does not support this type of syntax in pyi files. - # Functions copied from TurtleScreenBase: -# Note: mainloop() was always present in the global scope, but was added to -# TurtleScreenBase in Python 3.0 def mainloop() -> None: ... def textinput(title: str, prompt: str) -> str | None: ... def numinput( @@ -482,7 +528,7 @@ def clear() -> None: ... @overload def mode(mode: None = None) -> str: ... @overload -def mode(mode: str) -> None: ... +def mode(mode: Literal["standard", "logo", "world"]) -> None: ... def setworldcoordinates(llx: float, lly: float, urx: float, ury: float) -> None: ... def register_shape(name: str, shape: _PolygonCoords | Shape | None = None) -> None: ... @overload @@ -505,12 +551,17 @@ def tracer(n: int, delay: int | None = None) -> None: ... def delay(delay: None = None) -> int: ... @overload def delay(delay: int) -> None: ... + +if sys.version_info >= (3, 14): + @contextmanager + def no_animation() -> Generator[None]: ... + def update() -> None: ... def window_width() -> int: ... def window_height() -> int: ... def getcanvas() -> Canvas: ... def getshapes() -> list[str]: ... -def onclick(fun: Callable[[float, float], object], btn: int = 1, add: Any | None = None) -> None: ... +def onclick(fun: Callable[[float, float], object], btn: int = 1, add: bool | None = None) -> None: ... def onkey(fun: Callable[[], object], key: str) -> None: ... def listen(xdummy: float | None = None, ydummy: float | None = None) -> None: ... def ontimer(fun: Callable[[], object], t: int = 0) -> None: ... @@ -523,6 +574,9 @@ def screensize(canvwidth: None = None, canvheight: None = None, bg: None = None) @overload def screensize(canvwidth: int, canvheight: int, bg: _Color | None = None) -> None: ... +if sys.version_info >= (3, 14): + def save(filename: StrPath, *, overwrite: bool = False) -> None: ... + onscreenclick = onclick resetscreen = reset clearscreen = clear @@ -584,7 +638,7 @@ seth = setheading @overload def resizemode(rmode: None = None) -> str: ... @overload -def resizemode(rmode: str) -> None: ... +def resizemode(rmode: Literal["auto", "user", "noresize"]) -> None: ... @overload def pensize(width: None = None) -> int: ... @overload @@ -633,7 +687,7 @@ def pen( fillcolor: _Color = ..., pensize: int = ..., speed: int = ..., - resizemode: str = ..., + resizemode: Literal["auto", "user", "noresize"] = ..., stretchfactor: tuple[float, float] = ..., outline: int = ..., tilt: float = ..., @@ -679,6 +733,7 @@ def shapetransform( def get_shapepoly() -> _PolygonCoords | None: ... if sys.version_info < (3, 13): + @deprecated("Deprecated since Python 3.1; removed in Python 3.13. Use `tiltangle()` instead.") def settiltangle(angle: float) -> None: ... @overload @@ -694,10 +749,25 @@ def stamp() -> Any: ... def clearstamp(stampid: int | tuple[int, ...]) -> None: ... def clearstamps(n: int | None = None) -> None: ... def filling() -> bool: ... + +if sys.version_info >= (3, 14): + @contextmanager + def fill() -> Generator[None]: ... + def begin_fill() -> None: ... def end_fill() -> None: ... -def dot(size: int | None = None, *color: _Color) -> None: ... +@overload +def dot(size: int | _Color | None = None) -> None: ... +@overload +def dot(size: int | None, color: _Color, /) -> None: ... +@overload +def dot(size: int | None, r: float, g: float, b: float, /) -> None: ... def write(arg: object, move: bool = False, align: str = "left", font: tuple[str, int, str] = ("Arial", 8, "normal")) -> None: ... + +if sys.version_info >= (3, 14): + @contextmanager + def poly() -> Generator[None]: ... + def begin_poly() -> None: ... def end_poly() -> None: ... def get_poly() -> _PolygonCoords | None: ... @@ -706,8 +776,8 @@ def getturtle() -> Turtle: ... getpen = getturtle -def onrelease(fun: Callable[[float, float], object], btn: int = 1, add: Any | None = None) -> None: ... -def ondrag(fun: Callable[[float, float], object], btn: int = 1, add: Any | None = None) -> None: ... +def onrelease(fun: Callable[[float, float], object], btn: int = 1, add: bool | None = None) -> None: ... +def ondrag(fun: Callable[[float, float], object], btn: int = 1, add: bool | None = None) -> None: ... def undo() -> None: ... turtlesize = shapesize diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 0f6592a9883e7..0293e5cb0b4bb 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 AnnotationForm, MaybeNone, SupportsKeysAndGetItem from _typeshed.importlib import LoaderProtocol from collections.abc import ( AsyncGenerator, @@ -11,14 +11,16 @@ from collections.abc import ( Iterable, Iterator, KeysView, + Mapping, MutableSequence, ValuesView, ) from importlib.machinery import ModuleSpec +from typing import Any, ClassVar, Literal, TypeVar, final, overload +from typing_extensions import ParamSpec, Self, TypeAliasType, TypeVarTuple, deprecated, disjoint_base -# pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping -from typing import Any, ClassVar, Literal, Mapping, TypeVar, final, overload # noqa: Y022 -from typing_extensions import ParamSpec, Self, TypeVarTuple, deprecated +if sys.version_info >= (3, 14): + from _typeshed import AnnotateFunc __all__ = [ "FunctionType", @@ -47,11 +49,9 @@ __all__ = [ "WrapperDescriptorType", "resolve_bases", "CellType", + "GenericAlias", ] -if sys.version_info >= (3, 9): - __all__ += ["GenericAlias"] - if sys.version_info >= (3, 10): __all__ += ["EllipsisType", "NoneType", "NotImplementedType", "UnionType"] @@ -65,7 +65,7 @@ if sys.version_info >= (3, 13): _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") -_KT = TypeVar("_KT") +_KT_co = TypeVar("_KT_co", covariant=True) _VT_co = TypeVar("_VT_co", covariant=True) # Make sure this class definition stays roughly in line with `builtins.function` @@ -80,7 +80,9 @@ class FunctionType: def __globals__(self) -> dict[str, Any]: ... __name__: str __qualname__: str - __annotations__: dict[str, Any] + __annotations__: dict[str, AnnotationForm] + if sys.version_info >= (3, 14): + __annotate__: AnnotateFunc | None __kwdefaults__: dict[str, Any] | None if sys.version_info >= (3, 10): @property @@ -89,14 +91,26 @@ class FunctionType: __type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] __module__: str - def __new__( - cls, - code: CodeType, - globals: dict[str, Any], - name: str | None = ..., - argdefs: tuple[object, ...] | None = ..., - closure: tuple[CellType, ...] | None = ..., - ) -> Self: ... + if sys.version_info >= (3, 13): + def __new__( + cls, + code: CodeType, + globals: dict[str, Any], + name: str | None = None, + argdefs: tuple[object, ...] | None = None, + closure: tuple[CellType, ...] | None = None, + kwdefaults: dict[str, object] | None = None, + ) -> Self: ... + else: + def __new__( + cls, + code: CodeType, + globals: dict[str, Any], + name: str | None = None, + argdefs: tuple[object, ...] | None = None, + closure: tuple[CellType, ...] | None = None, + ) -> Self: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @overload def __get__(self, instance: None, owner: type, /) -> FunctionType: ... @@ -137,7 +151,7 @@ class CodeType: def co_firstlineno(self) -> int: ... if sys.version_info >= (3, 10): @property - @deprecated("Will be removed in Python 3.14. Use the co_lines() method instead.") + @deprecated("Deprecated since Python 3.10; will be removed in Python 3.15. Use `CodeType.co_lines()` instead.") def co_lnotab(self) -> bytes: ... else: @property @@ -157,6 +171,8 @@ class CodeType: @property def co_qualname(self) -> str: ... def co_positions(self) -> Iterable[tuple[int | None, int | None, int | None, int | None]]: ... + if sys.version_info >= (3, 14): + def co_branches(self) -> Iterator[tuple[int, int, int]]: ... if sys.version_info >= (3, 11): def __new__( @@ -293,41 +309,56 @@ class CodeType: __replace__ = replace @final -class MappingProxyType(Mapping[_KT, _VT_co]): +class MappingProxyType(Mapping[_KT_co, _VT_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] __hash__: ClassVar[None] # type: ignore[assignment] - def __new__(cls, mapping: SupportsKeysAndGetItem[_KT, _VT_co]) -> Self: ... - def __getitem__(self, key: _KT, /) -> _VT_co: ... - def __iter__(self) -> Iterator[_KT]: ... + def __new__(cls, mapping: SupportsKeysAndGetItem[_KT_co, _VT_co]) -> Self: ... + def __getitem__(self, key: _KT_co, /) -> _VT_co: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __iter__(self) -> Iterator[_KT_co]: ... def __len__(self) -> int: ... def __eq__(self, value: object, /) -> bool: ... - def copy(self) -> dict[_KT, _VT_co]: ... - def keys(self) -> KeysView[_KT]: ... + def copy(self) -> dict[_KT_co, _VT_co]: ... + def keys(self) -> KeysView[_KT_co]: ... def values(self) -> ValuesView[_VT_co]: ... - def items(self) -> ItemsView[_KT, _VT_co]: ... + def items(self) -> ItemsView[_KT_co, _VT_co]: ... @overload - def get(self, key: _KT, /) -> _VT_co | None: ... + def get(self, key: _KT_co, /) -> _VT_co | None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter @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]: ... - def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ... - def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT | _T1, _VT_co | _T2]: ... - -class SimpleNamespace: - __hash__: ClassVar[None] # type: ignore[assignment] - 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 get(self, key: _KT_co, default: _VT_co, /) -> _VT_co: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter + @overload + def get(self, key: _KT_co, default: _T2, /) -> _VT_co | _T2: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __reversed__(self) -> Iterator[_KT_co]: ... + def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT_co | _T1, _VT_co | _T2]: ... + def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT_co | _T1, _VT_co | _T2]: ... - 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: ... +if sys.version_info >= (3, 12): + @disjoint_base + class SimpleNamespace: + __hash__: ClassVar[None] # type: ignore[assignment] + 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: ... + +else: + class SimpleNamespace: + __hash__: ClassVar[None] # type: ignore[assignment] + 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: ... +@disjoint_base class ModuleType: __name__: str __file__: str | None @@ -337,6 +368,17 @@ 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 + __annotations__: dict[str, AnnotationForm] + if sys.version_info >= (3, 14): + __annotate__: AnnotateFunc | 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 @@ -350,13 +392,19 @@ class CellType: cell_contents: Any _YieldT_co = TypeVar("_YieldT_co", covariant=True) -_SendT_contra = TypeVar("_SendT_contra", contravariant=True) -_ReturnT_co = TypeVar("_ReturnT_co", covariant=True) +_SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) +_ReturnT_co = TypeVar("_ReturnT_co", covariant=True, default=None) @final class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): @property - def gi_yieldfrom(self) -> GeneratorType[_YieldT_co, _SendT_contra, Any] | None: ... + def gi_code(self) -> CodeType: ... + @property + def gi_frame(self) -> FrameType: ... + @property + def gi_running(self) -> bool: ... + @property + def gi_yieldfrom(self) -> Iterator[_YieldT_co] | None: ... if sys.version_info >= (3, 11): @property def gi_suspended(self) -> bool: ... @@ -378,6 +426,12 @@ class GeneratorType(Generator[_YieldT_co, _SendT_contra, _ReturnT_co]): class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): @property def ag_await(self) -> Awaitable[Any] | None: ... + @property + def ag_code(self) -> CodeType: ... + @property + def ag_frame(self) -> FrameType: ... + @property + def ag_running(self) -> bool: ... __name__: str __qualname__: str if sys.version_info >= (3, 12): @@ -394,22 +448,38 @@ class AsyncGeneratorType(AsyncGenerator[_YieldT_co, _SendT_contra]): @overload async def athrow(self, typ: BaseException, val: None = None, tb: TracebackType | None = ..., /) -> _YieldT_co: ... def aclose(self) -> Coroutine[Any, Any, None]: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +# Non-default variations to accommodate coroutines +_SendT_nd_contra = TypeVar("_SendT_nd_contra", contravariant=True) +_ReturnT_nd_co = TypeVar("_ReturnT_nd_co", covariant=True) @final -class CoroutineType(Coroutine[_YieldT_co, _SendT_contra, _ReturnT_co]): +class CoroutineType(Coroutine[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co]): __name__: str __qualname__: str @property + def cr_await(self) -> Any | None: ... + @property + def cr_code(self) -> CodeType: ... + if sys.version_info >= (3, 12): + @property + def cr_frame(self) -> FrameType | None: ... + else: + @property + def cr_frame(self) -> FrameType: ... + + @property + def cr_running(self) -> bool: ... + @property def cr_origin(self) -> tuple[tuple[str, int, str], ...] | None: ... if sys.version_info >= (3, 11): @property def cr_suspended(self) -> bool: ... def close(self) -> None: ... - def __await__(self) -> Generator[Any, None, _ReturnT_co]: ... - def send(self, arg: _SendT_contra, /) -> _YieldT_co: ... + def __await__(self) -> Generator[Any, None, _ReturnT_nd_co]: ... + def send(self, arg: _SendT_nd_contra, /) -> _YieldT_co: ... @overload def throw( self, typ: type[BaseException], val: BaseException | object = ..., tb: TracebackType | None = ..., / @@ -424,6 +494,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]: ... @@ -433,8 +505,12 @@ class MethodType: def __name__(self) -> str: ... # inherited from the added function @property def __qualname__(self) -> str: ... # inherited from the added function - def __new__(cls, func: Callable[..., Any], obj: object, /) -> Self: ... + def __new__(cls, func: Callable[..., Any], instance: object, /) -> Self: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + + if sys.version_info >= (3, 13): + def __get__(self, instance: object, owner: type | None = None, /) -> Self: ... + def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @@ -526,15 +602,18 @@ 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 f_trace_lines: bool f_trace_opcodes: bool def clear(self) -> None: ... + if sys.version_info >= (3, 14): + @property + def f_generator(self) -> GeneratorType[Any, Any, Any] | CoroutineType[Any, Any, Any] | None: ... @final class GetSetDescriptorType: @@ -574,8 +653,27 @@ def prepare_class( if sys.version_info >= (3, 12): def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... -# Actually a different type, but `property` is special and we want that too. -DynamicClassAttribute = property +# Does not actually inherit from property, but saying it does makes sure that +# pyright handles this class correctly. +class DynamicClassAttribute(property): + fget: Callable[[Any], Any] | None + fset: Callable[[Any, Any], object] | None # type: ignore[assignment] + fdel: Callable[[Any], object] | None # type: ignore[assignment] + overwrite_doc: bool + __isabstractmethod__: bool + def __init__( + self, + fget: Callable[[Any], Any] | None = None, + fset: Callable[[Any, Any], object] | None = None, + fdel: Callable[[Any], object] | None = None, + doc: str | None = None, + ) -> None: ... + def __get__(self, instance: Any, ownerclass: type | None = None) -> Any: ... + def __set__(self, instance: Any, value: Any) -> None: ... + def __delete__(self, instance: Any) -> None: ... + def getter(self, fget: Callable[[Any], Any]) -> DynamicClassAttribute: ... + def setter(self, fset: Callable[[Any, Any], object]) -> DynamicClassAttribute: ... + def deleter(self, fdel: Callable[[Any], object]) -> DynamicClassAttribute: ... _Fn = TypeVar("_Fn", bound=Callable[..., object]) _R = TypeVar("_R") @@ -586,30 +684,30 @@ _P = ParamSpec("_P") def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... @overload def coroutine(func: _Fn) -> _Fn: ... - -if sys.version_info >= (3, 9): - class GenericAlias: - @property - def __origin__(self) -> type: ... +@disjoint_base +class GenericAlias: + @property + def __origin__(self) -> type | TypeAliasType: ... + @property + def __args__(self) -> tuple[Any, ...]: ... + @property + def __parameters__(self) -> tuple[Any, ...]: ... + def __new__(cls, origin: type, args: Any, /) -> Self: ... + def __getitem__(self, typeargs: Any, /) -> GenericAlias: ... + def __eq__(self, value: object, /) -> bool: ... + def __hash__(self) -> int: ... + def __mro_entries__(self, bases: Iterable[object], /) -> tuple[type, ...]: ... + if sys.version_info >= (3, 11): @property - def __args__(self) -> tuple[Any, ...]: ... + def __unpacked__(self) -> bool: ... @property - def __parameters__(self) -> tuple[Any, ...]: ... - def __new__(cls, origin: type, args: Any) -> Self: ... - def __getitem__(self, typeargs: Any, /) -> GenericAlias: ... - def __eq__(self, value: object, /) -> bool: ... - def __hash__(self) -> int: ... - if sys.version_info >= (3, 11): - @property - def __unpacked__(self) -> bool: ... - @property - def __typing_unpacked_tuple_args__(self) -> tuple[Any, ...] | None: ... - if sys.version_info >= (3, 10): - def __or__(self, value: Any, /) -> UnionType: ... - def __ror__(self, value: Any, /) -> UnionType: ... - - # GenericAlias delegates attr access to `__origin__` - def __getattr__(self, name: str) -> Any: ... + def __typing_unpacked_tuple_args__(self) -> tuple[Any, ...] | None: ... + if sys.version_info >= (3, 10): + def __or__(self, value: Any, /) -> UnionType: ... + def __ror__(self, value: Any, /) -> UnionType: ... + + # GenericAlias delegates attr access to `__origin__` + def __getattr__(self, name: str) -> Any: ... if sys.version_info >= (3, 10): @final @@ -619,17 +717,28 @@ if sys.version_info >= (3, 10): @final class EllipsisType: ... - from builtins import _NotImplementedType + @final + class NotImplementedType(Any): ... - NotImplementedType = _NotImplementedType @final class UnionType: @property def __args__(self) -> tuple[Any, ...]: ... - def __or__(self, value: Any, /) -> UnionType: ... - def __ror__(self, value: Any, /) -> UnionType: ... + @property + def __parameters__(self) -> tuple[Any, ...]: ... + # `(int | str) | Literal["foo"]` returns a generic alias to an instance of `_SpecialForm` (`Union`). + # Normally we'd express this using the return type of `_SpecialForm.__ror__`, + # but because `UnionType.__or__` accepts `Any`, type checkers will use + # the return type of `UnionType.__or__` to infer the result of this operation + # rather than `_SpecialForm.__ror__`. To mitigate this, we use `| Any` + # in the return type of `UnionType.__(r)or__`. + def __or__(self, value: Any, /) -> UnionType | Any: ... + def __ror__(self, value: Any, /) -> UnionType | Any: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... + # you can only subscript a `UnionType` instance if at least one of the elements + # in the union is a generic alias instance that has a non-empty `__parameters__` + def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 13): @final diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index ce16d9adff998..e3e5d1ff28422 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -1,19 +1,18 @@ # Since this module defines "overload" it is not recognized by Ruff as typing.overload -# 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[reportUnusedImport] import sys import typing_extensions from _collections_abc import dict_items, dict_keys, dict_values -from _typeshed import IdentityFunction, ReadableBuffer, SupportsKeysAndGetItem +from _typeshed import IdentityFunction, ReadableBuffer, SupportsGetItem, SupportsGetItemViewable, SupportsKeysAndGetItem, Viewable from abc import ABCMeta, abstractmethod from re import Match as Match, Pattern as Pattern from types import ( BuiltinFunctionType, CodeType, - FrameType, FunctionType, + GenericAlias, MethodDescriptorType, MethodType, MethodWrapperType, @@ -23,13 +22,17 @@ from types import ( ) from typing_extensions import Never as _Never, ParamSpec as _ParamSpec, deprecated -if sys.version_info >= (3, 9): - from types import GenericAlias +if sys.version_info >= (3, 14): + from _typeshed import EvaluateFunc + + from annotationlib import Format + if sys.version_info >= (3, 10): from types import UnionType __all__ = [ "AbstractSet", + "Annotated", "Any", "AnyStr", "AsyncContextManager", @@ -37,6 +40,7 @@ __all__ = [ "AsyncIterable", "AsyncIterator", "Awaitable", + "BinaryIO", "ByteString", "Callable", "ChainMap", @@ -50,10 +54,12 @@ __all__ = [ "Deque", "Dict", "Final", + "ForwardRef", "FrozenSet", "Generator", "Generic", "Hashable", + "IO", "ItemsView", "Iterable", "Iterator", @@ -62,12 +68,16 @@ __all__ = [ "Literal", "Mapping", "MappingView", + "Match", "MutableMapping", "MutableSequence", "MutableSet", "NamedTuple", "NewType", + "NoReturn", "Optional", + "OrderedDict", + "Pattern", "Protocol", "Reversible", "Sequence", @@ -81,6 +91,7 @@ __all__ = [ "SupportsInt", "SupportsRound", "Text", + "TextIO", "Tuple", "Type", "TypeVar", @@ -97,13 +108,10 @@ __all__ = [ "no_type_check_decorator", "overload", "runtime_checkable", - "ForwardRef", - "NoReturn", - "OrderedDict", ] -if sys.version_info >= (3, 9): - __all__ += ["Annotated", "BinaryIO", "IO", "Match", "Pattern", "TextIO"] +if sys.version_info >= (3, 14): + __all__ += ["evaluate_forward_ref"] if sys.version_info >= (3, 10): __all__ += ["Concatenate", "ParamSpec", "ParamSpecArgs", "ParamSpecKwargs", "TypeAlias", "TypeGuard", "is_typeddict"] @@ -131,7 +139,14 @@ if sys.version_info >= (3, 12): if sys.version_info >= (3, 13): __all__ += ["get_protocol_members", "is_protocol", "NoDefault", "TypeIs", "ReadOnly"] -Any = object() +# We can't use this name here because it leads to issues with mypy, likely +# due to an import cycle. Below instead we use Any with a comment. +# from _typeshed import AnnotationForm + +class Any: ... + +class _Final: + __slots__ = ("__weakref__",) def final(f: _T) -> _T: ... @final @@ -139,9 +154,9 @@ class TypeVar: @property def __name__(self) -> str: ... @property - def __bound__(self) -> Any | None: ... + def __bound__(self) -> Any | None: ... # AnnotationForm @property - def __constraints__(self) -> tuple[Any, ...]: ... + def __constraints__(self) -> tuple[Any, ...]: ... # AnnotationForm @property def __covariant__(self) -> bool: ... @property @@ -151,55 +166,72 @@ class TypeVar: def __infer_variance__(self) -> bool: ... if sys.version_info >= (3, 13): @property - def __default__(self) -> Any: ... + def __default__(self) -> Any: ... # AnnotationForm if sys.version_info >= (3, 13): - def __init__( - self, + def __new__( + cls, name: str, - *constraints: Any, - bound: Any | None = None, + *constraints: Any, # AnnotationForm + bound: Any | None = None, # AnnotationForm contravariant: bool = False, covariant: bool = False, infer_variance: bool = False, - default: Any = ..., - ) -> None: ... + default: Any = ..., # AnnotationForm + ) -> Self: ... elif sys.version_info >= (3, 12): - def __init__( - self, + def __new__( + cls, name: str, - *constraints: Any, - bound: Any | None = None, + *constraints: Any, # AnnotationForm + bound: Any | None = None, # AnnotationForm covariant: bool = False, contravariant: bool = False, infer_variance: bool = False, - ) -> None: ... + ) -> Self: ... + elif sys.version_info >= (3, 11): + def __new__( + cls, + name: str, + *constraints: Any, # AnnotationForm + bound: Any | None = None, # AnnotationForm + covariant: bool = False, + contravariant: bool = False, + ) -> Self: ... else: def __init__( - self, name: str, *constraints: Any, bound: Any | None = None, covariant: bool = False, contravariant: bool = False + self, + name: str, + *constraints: Any, # AnnotationForm + bound: Any | None = None, # AnnotationForm + covariant: bool = False, + contravariant: bool = False, ) -> None: ... if sys.version_info >= (3, 10): - def __or__(self, right: Any) -> _SpecialForm: ... - def __ror__(self, left: Any) -> _SpecialForm: ... + def __or__(self, right: Any, /) -> _SpecialForm: ... # AnnotationForm + def __ror__(self, left: Any, /) -> _SpecialForm: ... # AnnotationForm if sys.version_info >= (3, 11): - def __typing_subst__(self, arg: Any) -> Any: ... + def __typing_subst__(self, arg: Any, /) -> Any: ... if sys.version_info >= (3, 13): - def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... + def __typing_prepare_subst__(self, alias: Any, args: Any, /) -> tuple[Any, ...]: ... def has_default(self) -> bool: ... - -# Used for an undocumented mypy feature. Does not exist at runtime. -_promote = object() + if sys.version_info >= (3, 14): + @property + def evaluate_bound(self) -> EvaluateFunc | None: ... + @property + def evaluate_constraints(self) -> EvaluateFunc | None: ... + @property + def evaluate_default(self) -> EvaluateFunc | None: ... # N.B. Keep this definition in sync with typing_extensions._SpecialForm @final -class _SpecialForm: +class _SpecialForm(_Final): + __slots__ = ("_name", "__doc__", "_getitem") def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... def __ror__(self, other: Any) -> _SpecialForm: ... Union: _SpecialForm -Generic: _SpecialForm -# Protocol is only present in 3.8 and later, but mypy needs it unconditionally Protocol: _SpecialForm Callable: _SpecialForm Type: _SpecialForm @@ -211,8 +243,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 @@ -228,38 +259,53 @@ if sys.version_info >= (3, 11): def __name__(self) -> str: ... if sys.version_info >= (3, 13): @property - def __default__(self) -> Any: ... + def __default__(self) -> Any: ... # AnnotationForm def has_default(self) -> bool: ... if sys.version_info >= (3, 13): - def __init__(self, name: str, *, default: Any = ...) -> None: ... + def __new__(cls, name: str, *, default: Any = ...) -> Self: ... # AnnotationForm + elif sys.version_info >= (3, 12): + def __new__(cls, name: str) -> Self: ... else: def __init__(self, name: str) -> None: ... def __iter__(self) -> Any: ... - def __typing_subst__(self, arg: Never) -> Never: ... - def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... + def __typing_subst__(self, arg: Never, /) -> Never: ... + def __typing_prepare_subst__(self, alias: Any, args: Any, /) -> tuple[Any, ...]: ... + if sys.version_info >= (3, 14): + @property + def evaluate_default(self) -> EvaluateFunc | None: ... if sys.version_info >= (3, 10): @final class ParamSpecArgs: @property def __origin__(self) -> ParamSpec: ... - def __init__(self, origin: ParamSpec) -> None: ... - def __eq__(self, other: object) -> bool: ... + if sys.version_info >= (3, 12): + def __new__(cls, origin: ParamSpec) -> Self: ... + else: + def __init__(self, origin: ParamSpec) -> None: ... + + def __eq__(self, other: object, /) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] @final class ParamSpecKwargs: @property def __origin__(self) -> ParamSpec: ... - def __init__(self, origin: ParamSpec) -> None: ... - def __eq__(self, other: object) -> bool: ... + if sys.version_info >= (3, 12): + def __new__(cls, origin: ParamSpec) -> Self: ... + else: + def __init__(self, origin: ParamSpec) -> None: ... + + def __eq__(self, other: object, /) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] @final class ParamSpec: @property def __name__(self) -> str: ... @property - def __bound__(self) -> Any | None: ... + def __bound__(self) -> Any | None: ... # AnnotationForm @property def __covariant__(self) -> bool: ... @property @@ -269,31 +315,45 @@ if sys.version_info >= (3, 10): def __infer_variance__(self) -> bool: ... if sys.version_info >= (3, 13): @property - def __default__(self) -> Any: ... + def __default__(self) -> Any: ... # AnnotationForm if sys.version_info >= (3, 13): - def __init__( - self, + def __new__( + cls, name: str, *, - bound: Any | None = None, + bound: Any | None = None, # AnnotationForm contravariant: bool = False, covariant: bool = False, infer_variance: bool = False, - default: Any = ..., - ) -> None: ... + default: Any = ..., # AnnotationForm + ) -> Self: ... elif sys.version_info >= (3, 12): - def __init__( - self, + def __new__( + cls, name: str, *, - bound: Any | None = None, + bound: Any | None = None, # AnnotationForm contravariant: bool = False, covariant: bool = False, infer_variance: bool = False, - ) -> None: ... + ) -> Self: ... + elif sys.version_info >= (3, 11): + def __new__( + cls, + name: str, + *, + bound: Any | None = None, # AnnotationForm + contravariant: bool = False, + covariant: bool = False, + ) -> Self: ... else: def __init__( - self, name: str, *, bound: Any | None = None, contravariant: bool = False, covariant: bool = False + self, + name: str, + *, + bound: Any | None = None, # AnnotationForm + contravariant: bool = False, + covariant: bool = False, ) -> None: ... @property @@ -301,20 +361,23 @@ if sys.version_info >= (3, 10): @property def kwargs(self) -> ParamSpecKwargs: ... if sys.version_info >= (3, 11): - def __typing_subst__(self, arg: Any) -> Any: ... - def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... + def __typing_subst__(self, arg: Any, /) -> Any: ... + def __typing_prepare_subst__(self, alias: Any, args: Any, /) -> tuple[Any, ...]: ... - def __or__(self, right: Any) -> _SpecialForm: ... - def __ror__(self, left: Any) -> _SpecialForm: ... + def __or__(self, right: Any, /) -> _SpecialForm: ... + def __ror__(self, left: Any, /) -> _SpecialForm: ... if sys.version_info >= (3, 13): def has_default(self) -> bool: ... + if sys.version_info >= (3, 14): + @property + def evaluate_default(self) -> EvaluateFunc | None: ... Concatenate: _SpecialForm TypeAlias: _SpecialForm TypeGuard: _SpecialForm class NewType: - def __init__(self, name: str, tp: Any) -> None: ... + def __init__(self, name: str, tp: Any) -> None: ... # AnnotationForm if sys.version_info >= (3, 11): @staticmethod def __call__(x: _T, /) -> _T: ... @@ -332,6 +395,8 @@ _F = TypeVar("_F", bound=Callable[..., Any]) _P = _ParamSpec("_P") _T = TypeVar("_T") +_FT = TypeVar("_FT", bound=Callable[..., Any] | type) + # These type variables are used by the container types. _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. @@ -346,10 +411,11 @@ def no_type_check(arg: _F) -> _F: ... def no_type_check_decorator(decorator: Callable[_P, _T]) -> Callable[_P, _T]: ... # This itself is only available during type checking -def type_check_only(func_or_cls: _F) -> _F: ... +def type_check_only(func_or_cls: _FT) -> _FT: ... # Type aliases and type constructors +@type_check_only class _Alias: # Class for defining generic aliases for library types. def __getitem__(self, typeargs: Any) -> Any: ... @@ -365,12 +431,25 @@ ChainMap = _Alias() OrderedDict = _Alias() -if sys.version_info >= (3, 9): - Annotated: _SpecialForm +Annotated: _SpecialForm # Predefined type variables. AnyStr = TypeVar("AnyStr", str, bytes) # noqa: Y001 +@type_check_only +class _Generic: + if sys.version_info < (3, 12): + __slots__ = () + + if sys.version_info >= (3, 10): + @classmethod + def __class_getitem__(cls, args: TypeVar | ParamSpec | tuple[TypeVar | ParamSpec, ...]) -> _Final: ... + else: + @classmethod + def __class_getitem__(cls, args: TypeVar | tuple[TypeVar, ...]) -> _Final: ... + +Generic: type[_Generic] + class _ProtocolMeta(ABCMeta): if sys.version_info >= (3, 12): def __init__(cls, *args: Any, **kwargs: Any) -> None: ... @@ -380,36 +459,43 @@ class _ProtocolMeta(ABCMeta): def runtime_checkable(cls: _TC) -> _TC: ... @runtime_checkable class SupportsInt(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __int__(self) -> int: ... @runtime_checkable class SupportsFloat(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __float__(self) -> float: ... @runtime_checkable class SupportsComplex(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __complex__(self) -> complex: ... @runtime_checkable class SupportsBytes(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __bytes__(self) -> bytes: ... @runtime_checkable class SupportsIndex(Protocol, metaclass=ABCMeta): + __slots__ = () @abstractmethod def __index__(self) -> int: ... @runtime_checkable class SupportsAbs(Protocol[_T_co]): + __slots__ = () @abstractmethod def __abs__(self) -> _T_co: ... @runtime_checkable class SupportsRound(Protocol[_T_co]): + __slots__ = () @overload @abstractmethod def __round__(self) -> int: ... @@ -450,7 +536,8 @@ _YieldT_co = TypeVar("_YieldT_co", covariant=True) _SendT_contra = TypeVar("_SendT_contra", contravariant=True, default=None) _ReturnT_co = TypeVar("_ReturnT_co", covariant=True, default=None) -class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _ReturnT_co]): +@runtime_checkable +class Generator(Iterator[_YieldT_co], Protocol[_YieldT_co, _SendT_contra, _ReturnT_co]): def __next__(self) -> _YieldT_co: ... @abstractmethod def send(self, value: _SendT_contra, /) -> _YieldT_co: ... @@ -462,16 +549,12 @@ 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: ... - @property - def gi_frame(self) -> FrameType: ... - @property - def gi_running(self) -> bool: ... - @property - def gi_yieldfrom(self) -> Generator[Any, Any, Any] | None: ... # NOTE: Prior to Python 3.13 these aliases are lacking the second _ExitT_co parameter if sys.version_info >= (3, 13): @@ -490,23 +573,16 @@ class Awaitable(Protocol[_T_co]): @abstractmethod def __await__(self) -> Generator[Any, Any, _T_co]: ... -# Non-default variations to accommodate couroutines, and `AwaitableGenerator` having a 4th type parameter. -_SendT_contra_nd = TypeVar("_SendT_contra_nd", contravariant=True) -_ReturnT_co_nd = TypeVar("_ReturnT_co_nd", covariant=True) +# Non-default variations to accommodate coroutines, and `AwaitableGenerator` having a 4th type parameter. +_SendT_nd_contra = TypeVar("_SendT_nd_contra", contravariant=True) +_ReturnT_nd_co = TypeVar("_ReturnT_nd_co", covariant=True) -class Coroutine(Awaitable[_ReturnT_co_nd], Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd]): +class Coroutine(Awaitable[_ReturnT_nd_co], Generic[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co]): __name__: str __qualname__: str - @property - def cr_await(self) -> Any | None: ... - @property - def cr_code(self) -> CodeType: ... - @property - def cr_frame(self) -> FrameType | None: ... - @property - def cr_running(self) -> bool: ... + @abstractmethod - def send(self, value: _SendT_contra_nd, /) -> _YieldT_co: ... + def send(self, value: _SendT_nd_contra, /) -> _YieldT_co: ... @overload @abstractmethod def throw( @@ -520,11 +596,12 @@ class Coroutine(Awaitable[_ReturnT_co_nd], Generic[_YieldT_co, _SendT_contra_nd, # NOTE: This type does not exist in typing.py or PEP 484 but mypy needs it to exist. # The parameters correspond to Generator, but the 4th is the original type. +# Obsolete, use _typeshed._type_checker_internals.AwaitableGenerator instead. @type_check_only class AwaitableGenerator( - Awaitable[_ReturnT_co_nd], - Generator[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd], - Generic[_YieldT_co, _SendT_contra_nd, _ReturnT_co_nd, _S], + Awaitable[_ReturnT_nd_co], + Generator[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co], + Generic[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co, _S], metaclass=ABCMeta, ): ... @@ -539,7 +616,8 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): def __anext__(self) -> Awaitable[_T_co]: ... def __aiter__(self) -> AsyncIterator[_T_co]: ... -class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): +@runtime_checkable +class AsyncGenerator(AsyncIterator[_YieldT_co], Protocol[_YieldT_co, _SendT_contra]): def __anext__(self) -> Coroutine[Any, Any, _YieldT_co]: ... @abstractmethod def asend(self, value: _SendT_contra, /) -> Coroutine[Any, Any, _YieldT_co]: ... @@ -554,14 +632,6 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contr 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 - def ag_code(self) -> CodeType: ... - @property - def ag_frame(self) -> FrameType: ... - @property - def ag_running(self) -> bool: ... @runtime_checkable class Container(Protocol[_T_co]): @@ -575,7 +645,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: ... @@ -650,16 +720,16 @@ class MutableSet(AbstractSet[_T]): def __isub__(self, it: AbstractSet[Any]) -> typing_extensions.Self: ... class MappingView(Sized): - def __init__(self, mapping: Mapping[Any, Any]) -> None: ... # undocumented + __slots__ = ("_mapping",) + def __init__(self, mapping: Sized) -> None: ... # undocumented def __len__(self) -> int: ... class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, _VT_co]): - def __init__(self, mapping: Mapping[_KT_co, _VT_co]) -> None: ... # undocumented + def __init__(self, mapping: SupportsGetItemViewable[_KT_co, _VT_co]) -> None: ... # undocumented def __and__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ... def __rand__(self, other: Iterable[_T]) -> set[_T]: ... - def __contains__(self, item: object) -> bool: ... + def __contains__(self, item: tuple[object, object]) -> bool: ... # type: ignore[override] 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]]: ... @@ -668,12 +738,11 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, def __rxor__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... class KeysView(MappingView, AbstractSet[_KT_co]): - def __init__(self, mapping: Mapping[_KT_co, Any]) -> None: ... # undocumented + def __init__(self, mapping: Viewable[_KT_co]) -> None: ... # undocumented def __and__(self, other: Iterable[Any]) -> set[_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]: ... @@ -682,10 +751,9 @@ class KeysView(MappingView, AbstractSet[_KT_co]): def __rxor__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... class ValuesView(MappingView, Collection[_VT_co]): - def __init__(self, mapping: Mapping[Any, _VT_co]) -> None: ... # undocumented + def __init__(self, mapping: SupportsGetItemViewable[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, @@ -696,7 +764,9 @@ class Mapping(Collection[_KT], Generic[_KT, _VT_co]): @overload def get(self, key: _KT, /) -> _VT_co | None: ... @overload - def get(self, key: _KT, /, default: _VT_co | _T) -> _VT_co | _T: ... + def get(self, key: _KT, /, default: _VT_co) -> _VT_co: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter + @overload + def get(self, key: _KT, /, default: _T) -> _VT_co | _T: ... def items(self) -> ItemsView[_KT, _VT_co]: ... def keys(self) -> KeysView[_KT]: ... def values(self) -> ValuesView[_VT_co]: ... @@ -747,23 +817,28 @@ class MutableMapping(Mapping[_KT, _VT]): # -- weakref.WeakValueDictionary.__ior__ # -- weakref.WeakKeyDictionary.__ior__ @overload - def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ... + def update(self, m: SupportsKeysAndGetItem[_KT, _VT], /) -> None: ... + @overload + def update(self: SupportsGetItem[str, _VT], m: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT) -> None: ... @overload - def update(self, m: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + def update(self, m: Iterable[tuple[_KT, _VT]], /) -> None: ... @overload - def update(self, **kwargs: _VT) -> None: ... + def update(self: SupportsGetItem[str, _VT], m: Iterable[tuple[str, _VT]], /, **kwargs: _VT) -> None: ... + @overload + def update(self: SupportsGetItem[str, _VT], **kwargs: _VT) -> None: ... Text = str -TYPE_CHECKING: bool +TYPE_CHECKING: Final[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 + __slots__ = () @property def mode(self) -> str: ... # Usually str, but may be bytes if a bytes path was passed to open(). See #10737. @@ -822,11 +897,13 @@ class IO(Iterator[AnyStr]): ) -> None: ... class BinaryIO(IO[bytes]): + __slots__ = () @abstractmethod def __enter__(self) -> BinaryIO: ... class TextIO(IO[str]): # See comment regarding the @properties in the `IO` class + __slots__ = () @property def buffer(self) -> BinaryIO: ... @property @@ -840,8 +917,7 @@ class TextIO(IO[str]): @abstractmethod def __enter__(self) -> TextIO: ... -if sys.version_info < (3, 14): - ByteString: typing_extensions.TypeAlias = bytes | bytearray | memoryview +ByteString: typing_extensions.TypeAlias = bytes | bytearray | memoryview # Functions @@ -857,20 +933,25 @@ _get_type_hints_obj_allowed_types: typing_extensions.TypeAlias = ( # noqa: Y042 | MethodDescriptorType ) -if sys.version_info >= (3, 9): +if sys.version_info >= (3, 14): def get_type_hints( obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None, include_extras: bool = False, - ) -> dict[str, Any]: ... + *, + format: Format | None = None, + ) -> dict[str, Any]: ... # AnnotationForm else: def get_type_hints( - obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None - ) -> dict[str, Any]: ... + obj: _get_type_hints_obj_allowed_types, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, + ) -> dict[str, Any]: ... # AnnotationForm -def get_args(tp: Any) -> tuple[Any, ...]: ... +def get_args(tp: Any) -> tuple[Any, ...]: ... # AnnotationForm if sys.version_info >= (3, 10): @overload @@ -878,15 +959,10 @@ if sys.version_info >= (3, 10): @overload def get_origin(tp: UnionType) -> type[UnionType]: ... -if sys.version_info >= (3, 9): - @overload - def get_origin(tp: GenericAlias) -> type: ... - @overload - def get_origin(tp: Any) -> Any | None: ... - -else: - def get_origin(tp: Any) -> Any | None: ... - +@overload +def get_origin(tp: GenericAlias) -> type: ... +@overload +def get_origin(tp: Any) -> Any | None: ... # AnnotationForm @overload def cast(typ: type[_T], val: Any) -> _T: ... @overload @@ -897,7 +973,7 @@ def cast(typ: object, val: Any) -> Any: ... if sys.version_info >= (3, 11): def reveal_type(obj: _T, /) -> _T: ... def assert_never(arg: Never, /) -> Never: ... - def assert_type(val: _T, typ: Any, /) -> _T: ... + def assert_type(val: _T, typ: Any, /) -> _T: ... # AnnotationForm def clear_overloads() -> None: ... def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... def dataclass_transform( @@ -912,9 +988,8 @@ if sys.version_info >= (3, 11): # Type constructors +# Obsolete, will be changed to a function. Use _typeshed._type_checker_internals.NamedTupleFallback instead. class NamedTuple(tuple[Any, ...]): - if sys.version_info < (3, 9): - _field_types: ClassVar[dict[str, type]] _field_defaults: ClassVar[dict[str, Any]] _fields: ClassVar[tuple[str, ...]] # __orig_bases__ sometimes exists on <3.12, but not consistently @@ -938,16 +1013,19 @@ class NamedTuple(tuple[Any, ...]): # Internal mypy fallback type for all typed dicts (does not exist at runtime) # N.B. Keep this mostly in sync with typing_extensions._TypedDict/mypy_extensions._TypedDict +# Obsolete, use _typeshed._type_checker_internals.TypedDictFallback instead. @type_check_only class _TypedDict(Mapping[str, object], metaclass=ABCMeta): __total__: ClassVar[bool] - if sys.version_info >= (3, 9): - __required_keys__: ClassVar[frozenset[str]] - __optional_keys__: ClassVar[frozenset[str]] + __required_keys__: ClassVar[frozenset[str]] + __optional_keys__: ClassVar[frozenset[str]] # __orig_bases__ sometimes exists on <3.12, but not consistently, # so we only add it to the stub on 3.12+ if sys.version_info >= (3, 12): __orig_bases__: ClassVar[tuple[Any, ...]] + if sys.version_info >= (3, 13): + __readonly_keys__: ClassVar[frozenset[str]] + __mutable_keys__: ClassVar[frozenset[str]] def copy(self) -> typing_extensions.Self: ... # Using Never so that only calls using mypy plugin hook that specialize the signature @@ -955,78 +1033,95 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): def setdefault(self, k: _Never, default: object) -> object: ... # Mypy plugin hook for 'pop' expects that 'default' has a type variable type. def pop(self, k: _Never, default: _T = ...) -> object: ... # pyright: ignore[reportInvalidTypeVarUse] - def update(self: _T, m: _T, /) -> None: ... + def update(self, m: typing_extensions.Self, /) -> None: ... def __delitem__(self, k: _Never) -> None: ... def items(self) -> dict_items[str, object]: ... def keys(self) -> dict_keys[str, object]: ... def values(self) -> dict_values[str, object]: ... - if sys.version_info >= (3, 9): - @overload - def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... - @overload - def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... - @overload - def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... - @overload - def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... - # supposedly incompatible definitions of __or__ and __ior__ - def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... # type: ignore[misc] + @overload + def __or__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... + @overload + def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... + @overload + def __ror__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... + @overload + def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... + # supposedly incompatible definitions of __or__ and __ior__ + def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... # type: ignore[misc] -@final -class ForwardRef: - __forward_arg__: str - __forward_code__: CodeType - __forward_evaluated__: bool - __forward_value__: Any | None - __forward_is_argument__: bool - __forward_is_class__: bool - __forward_module__: Any | None - if sys.version_info >= (3, 9): - # The module and is_class arguments were added in later Python 3.9 versions. - def __init__(self, arg: str, is_argument: bool = True, module: Any | None = None, *, is_class: bool = False) -> None: ... - else: - def __init__(self, arg: str, is_argument: bool = True) -> None: ... +if sys.version_info >= (3, 14): + from annotationlib import ForwardRef as ForwardRef - if sys.version_info >= (3, 13): - @overload - @deprecated( - "Failing to pass a value to the 'type_params' parameter of ForwardRef._evaluate() is deprecated, " - "as it leads to incorrect behaviour when evaluating a stringified annotation " - "that references a PEP 695 type parameter. It will be disallowed in Python 3.15." + def evaluate_forward_ref( + forward_ref: ForwardRef, + *, + owner: object = None, + globals: dict[str, Any] | None = None, + locals: Mapping[str, Any] | None = None, + type_params: tuple[TypeVar, ParamSpec, TypeVarTuple] | None = None, + format: Format | None = None, + ) -> Any: ... # AnnotationForm + +else: + @final + class ForwardRef(_Final): + __slots__ = ( + "__forward_arg__", + "__forward_code__", + "__forward_evaluated__", + "__forward_value__", + "__forward_is_argument__", + "__forward_is_class__", + "__forward_module__", ) - def _evaluate( - 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: Mapping[str, Any] | None, - type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...], - *, - recursive_guard: frozenset[str], - ) -> Any | None: ... - elif sys.version_info >= (3, 12): - def _evaluate( - self, - globalns: 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: Mapping[str, Any] | None, recursive_guard: frozenset[str] - ) -> Any | None: ... - else: - def _evaluate(self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None) -> Any | None: ... + __forward_arg__: str + __forward_code__: CodeType + __forward_evaluated__: bool + __forward_value__: Any | None # AnnotationForm + __forward_is_argument__: bool + __forward_is_class__: bool + __forward_module__: Any | None - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - if sys.version_info >= (3, 11): - def __or__(self, other: Any) -> _SpecialForm: ... - def __ror__(self, other: Any) -> _SpecialForm: ... + def __init__(self, arg: str, is_argument: bool = True, module: Any | None = None, *, is_class: bool = False) -> None: ... + + if sys.version_info >= (3, 13): + @overload + @deprecated( + "Failing to pass a value to the 'type_params' parameter of ForwardRef._evaluate() is deprecated, " + "as it leads to incorrect behaviour when evaluating a stringified annotation " + "that references a PEP 695 type parameter. It will be disallowed in Python 3.15." + ) + def _evaluate( + self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, *, recursive_guard: frozenset[str] + ) -> Any | None: ... # AnnotationForm + @overload + def _evaluate( + self, + globalns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...], + *, + recursive_guard: frozenset[str], + ) -> Any | None: ... # AnnotationForm + elif sys.version_info >= (3, 12): + def _evaluate( + self, + globalns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, + type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, + *, + recursive_guard: frozenset[str], + ) -> Any | None: ... # AnnotationForm + else: + def _evaluate( + self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, recursive_guard: frozenset[str] + ) -> Any | None: ... # AnnotationForm + + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + if sys.version_info >= (3, 11): + def __or__(self, other: Any) -> _SpecialForm: ... + def __ror__(self, other: Any) -> _SpecialForm: ... if sys.version_info >= (3, 10): def is_typeddict(tp: object) -> bool: ... @@ -1034,31 +1129,42 @@ if sys.version_info >= (3, 10): def _type_repr(obj: object) -> str: ... if sys.version_info >= (3, 12): + _TypeParameter: typing_extensions.TypeAlias = ( + TypeVar + | typing_extensions.TypeVar + | ParamSpec + | typing_extensions.ParamSpec + | TypeVarTuple + | typing_extensions.TypeVarTuple + ) + def override(method: _F, /) -> _F: ... @final class TypeAliasType: - def __init__( - self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () - ) -> None: ... + def __new__(cls, name: str, value: Any, *, type_params: tuple[_TypeParameter, ...] = ()) -> Self: ... @property - def __value__(self) -> Any: ... + def __value__(self) -> Any: ... # AnnotationForm @property - def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ... + def __type_params__(self) -> tuple[_TypeParameter, ...]: ... @property - def __parameters__(self) -> tuple[Any, ...]: ... + def __parameters__(self) -> tuple[Any, ...]: ... # AnnotationForm @property def __name__(self) -> str: ... # 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) -> GenericAlias: ... - def __or__(self, right: Any) -> _SpecialForm: ... - def __ror__(self, left: Any) -> _SpecialForm: ... + def __getitem__(self, parameters: Any, /) -> GenericAlias: ... # AnnotationForm + def __or__(self, right: Any, /) -> _SpecialForm: ... + def __ror__(self, left: Any, /) -> _SpecialForm: ... + if sys.version_info >= (3, 14): + @property + def evaluate_value(self) -> EvaluateFunc: ... if sys.version_info >= (3, 13): def is_protocol(tp: type, /) -> bool: ... def get_protocol_members(tp: type, /) -> frozenset[str]: ... @final + @type_check_only class _NoDefaultType: ... NoDefault: _NoDefaultType diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 3240eff0f5e92..5fd3f4578a8bd 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -1,66 +1,65 @@ import abc +import enum import sys -import typing from _collections_abc import dict_items, dict_keys, dict_values -from _typeshed import IdentityFunction -from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager -from typing import ( # noqa: Y022,Y037,Y038,Y039 - IO as IO, - TYPE_CHECKING as TYPE_CHECKING, - AbstractSet as AbstractSet, - Any as Any, - AnyStr as AnyStr, +from _typeshed import AnnotationForm, IdentityFunction, Incomplete, Unused +from collections.abc import ( AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, AsyncIterator as AsyncIterator, Awaitable as Awaitable, - BinaryIO as BinaryIO, - Callable as Callable, - ChainMap as ChainMap, - ClassVar as ClassVar, Collection as Collection, Container as Container, Coroutine as Coroutine, - Counter as Counter, - DefaultDict as DefaultDict, - Deque as Deque, - Dict as Dict, - ForwardRef as ForwardRef, - FrozenSet as FrozenSet, Generator as Generator, - Generic as Generic, Hashable as Hashable, ItemsView as ItemsView, Iterable as Iterable, Iterator as Iterator, KeysView as KeysView, - List as List, Mapping as Mapping, MappingView as MappingView, - Match as Match, MutableMapping as MutableMapping, MutableSequence as MutableSequence, MutableSet as MutableSet, - NoReturn as NoReturn, - Optional as Optional, - Pattern as Pattern, Reversible as Reversible, Sequence as Sequence, - Set as Set, Sized as Sized, - SupportsAbs as SupportsAbs, - SupportsBytes as SupportsBytes, - SupportsComplex as SupportsComplex, - SupportsFloat as SupportsFloat, - SupportsInt as SupportsInt, - SupportsRound as SupportsRound, + ValuesView as ValuesView, +) +from contextlib import AbstractAsyncContextManager as AsyncContextManager, AbstractContextManager as ContextManager +from re import Match as Match, Pattern as Pattern +from types import GenericAlias, ModuleType +from typing import ( # noqa: Y022,Y037,Y038,Y039,UP035 + IO as IO, + TYPE_CHECKING as TYPE_CHECKING, + AbstractSet as AbstractSet, + Any as Any, + AnyStr as AnyStr, + BinaryIO as BinaryIO, + Callable as Callable, + ChainMap as ChainMap, + ClassVar as ClassVar, + Counter as Counter, + DefaultDict as DefaultDict, + Deque as Deque, + Dict as Dict, + ForwardRef as ForwardRef, + FrozenSet as FrozenSet, + Generic as Generic, + List as List, + NoReturn as NoReturn, + Optional as Optional, + Set as Set, Text as Text, TextIO as TextIO, Tuple as Tuple, Type as Type, + TypedDict as TypedDict, + TypeVar as _TypeVar, Union as Union, - ValuesView as ValuesView, _Alias, + _SpecialForm, cast as cast, no_type_check as no_type_check, no_type_check_decorator as no_type_check_decorator, @@ -70,12 +69,11 @@ from typing import ( # noqa: Y022,Y037,Y038,Y039 if sys.version_info >= (3, 10): from types import UnionType -if sys.version_info >= (3, 9): - from types import GenericAlias +# Please keep order the same as at runtime. __all__ = [ + # Super-special typing primitives. "Any", - "Buffer", "ClassVar", "Concatenate", "Final", @@ -88,14 +86,16 @@ __all__ = [ "TypeVar", "TypeVarTuple", "Unpack", + # ABCs (from collections.abc). "Awaitable", "AsyncIterator", "AsyncIterable", "Coroutine", "AsyncGenerator", "AsyncContextManager", - "CapsuleType", + "Buffer", "ChainMap", + # Concrete collection types. "ContextManager", "Counter", "Deque", @@ -103,44 +103,63 @@ __all__ = [ "NamedTuple", "OrderedDict", "TypedDict", - "SupportsIndex", + # Structural checks, a.k.a. protocols. "SupportsAbs", - "SupportsRound", "SupportsBytes", "SupportsComplex", "SupportsFloat", + "SupportsIndex", "SupportsInt", + "SupportsRound", + "Reader", + "Writer", + # One-off things. "Annotated", "assert_never", "assert_type", + "clear_overloads", "dataclass_transform", "deprecated", + "disjoint_base", + "Doc", + "evaluate_forward_ref", + "get_overloads", "final", + "Format", + "get_annotations", + "get_args", + "get_origin", + "get_original_bases", + "get_protocol_members", + "get_type_hints", "IntVar", + "is_protocol", "is_typeddict", "Literal", "NewType", "overload", "override", "Protocol", + "Sentinel", "reveal_type", "runtime", "runtime_checkable", "Text", "TypeAlias", "TypeAliasType", + "TypeForm", "TypeGuard", + "TypeIs", "TYPE_CHECKING", + "type_repr", "Never", "NoReturn", + "ReadOnly", "Required", "NotRequired", - "clear_overloads", - "get_args", - "get_origin", - "get_original_bases", - "get_overloads", - "get_type_hints", + "NoDefault", + "NoExtraItems", + # Pure aliases, have always been in typing "AbstractSet", "AnyStr", "BinaryIO", @@ -148,7 +167,6 @@ __all__ = [ "Collection", "Container", "Dict", - "Doc", "ForwardRef", "FrozenSet", "Generator", @@ -166,7 +184,6 @@ __all__ = [ "MutableMapping", "MutableSequence", "MutableSet", - "NoDefault", "Optional", "Pattern", "Reversible", @@ -178,24 +195,17 @@ __all__ = [ "Union", "ValuesView", "cast", - "get_protocol_members", - "is_protocol", "no_type_check", "no_type_check_decorator", - "ReadOnly", - "TypeIs", + # Added dynamically + "CapsuleType", ] -_T = typing.TypeVar("_T") -_F = typing.TypeVar("_F", bound=Callable[..., Any]) -_TC = typing.TypeVar("_TC", bound=type[object]) - -# unfortunately we have to duplicate this class definition from typing.pyi or we break pytype -class _SpecialForm: - def __getitem__(self, parameters: Any) -> object: ... - if sys.version_info >= (3, 10): - def __or__(self, other: Any) -> _SpecialForm: ... - def __ror__(self, other: Any) -> _SpecialForm: ... +_T = _TypeVar("_T") +_F = _TypeVar("_F", bound=Callable[..., Any]) +_TC = _TypeVar("_TC", bound=type[object]) +_T_co = _TypeVar("_T_co", covariant=True) # Any type covariant containers. +_T_contra = _TypeVar("_T_contra", contravariant=True) # Do not import (and re-export) Protocol or runtime_checkable from # typing module because type checkers need to be able to distinguish @@ -211,6 +221,7 @@ runtime = runtime_checkable Final: _SpecialForm def final(f: _F) -> _F: ... +def disjoint_base(cls: _TC) -> _TC: ... Literal: _SpecialForm @@ -228,65 +239,56 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): __readonly_keys__: ClassVar[frozenset[str]] __mutable_keys__: ClassVar[frozenset[str]] # PEP 728 - __closed__: ClassVar[bool] - __extra_items__: ClassVar[Any] + __closed__: ClassVar[bool | None] + __extra_items__: ClassVar[AnnotationForm] def copy(self) -> Self: ... # Using Never so that only calls using mypy plugin hook that specialize the signature # can go through. def setdefault(self, k: Never, default: object) -> object: ... # Mypy plugin hook for 'pop' expects that 'default' has a type variable type. def pop(self, k: Never, default: _T = ...) -> object: ... # pyright: ignore[reportInvalidTypeVarUse] - def update(self: _T, m: _T, /) -> None: ... + def update(self, m: Self, /) -> None: ... def items(self) -> dict_items[str, object]: ... def keys(self) -> dict_keys[str, object]: ... def values(self) -> dict_values[str, object]: ... def __delitem__(self, k: Never) -> None: ... - if sys.version_info >= (3, 9): - @overload - def __or__(self, value: Self, /) -> Self: ... - @overload - def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... - @overload - def __ror__(self, value: Self, /) -> Self: ... - @overload - def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... - # 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 + @overload + def __or__(self, value: Self, /) -> Self: ... + @overload + def __or__(self, value: dict[str, Any], /) -> dict[str, object]: ... + @overload + def __ror__(self, value: Self, /) -> Self: ... + @overload + def __ror__(self, value: dict[str, Any], /) -> dict[str, object]: ... + # supposedly incompatible definitions of `__ior__` and `__or__`: + # Since this module defines "Self" it is not recognized by Ruff as typing_extensions.Self + def __ior__(self, value: Self, /) -> Self: ... # type: ignore[misc] OrderedDict = _Alias() -def get_type_hints( - obj: Callable[..., Any], - globalns: 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, ...]: ... +if sys.version_info >= (3, 13): + from typing import get_type_hints as get_type_hints +else: + def get_type_hints( + obj: Any, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None, include_extras: bool = False + ) -> dict[str, AnnotationForm]: ... + +def get_args(tp: AnnotationForm) -> tuple[AnnotationForm, ...]: ... if sys.version_info >= (3, 10): @overload def get_origin(tp: UnionType) -> type[UnionType]: ... -if sys.version_info >= (3, 9): - @overload - def get_origin(tp: GenericAlias) -> type: ... - +@overload +def get_origin(tp: GenericAlias) -> type: ... @overload def get_origin(tp: ParamSpecArgs | ParamSpecKwargs) -> ParamSpec: ... @overload -def get_origin(tp: Any) -> Any | None: ... +def get_origin(tp: AnnotationForm) -> AnnotationForm | None: ... Annotated: _SpecialForm _AnnotatedAlias: Any # undocumented -@runtime_checkable -class SupportsIndex(Protocol, metaclass=abc.ABCMeta): - @abc.abstractmethod - def __index__(self) -> int: ... - # New and changed things in 3.10 if sys.version_info >= (3, 10): from typing import ( @@ -338,7 +340,7 @@ else: Never: _SpecialForm def reveal_type(obj: _T, /) -> _T: ... def assert_never(arg: Never, /) -> Never: ... - def assert_type(val: _T, typ: Any, /) -> _T: ... + def assert_type(val: _T, typ: AnnotationForm, /) -> _T: ... def clear_overloads() -> None: ... def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... @@ -358,8 +360,6 @@ else: ) -> IdentityFunction: ... class NamedTuple(tuple[Any, ...]): - if sys.version_info < (3, 9): - _field_types: ClassVar[dict[str, type]] _field_defaults: ClassVar[dict[str, Any]] _fields: ClassVar[tuple[str, ...]] __orig_bases__: ClassVar[tuple[Any, ...]] @@ -373,7 +373,7 @@ else: def _replace(self, **kwargs: Any) -> Self: ... class NewType: - def __init__(self, name: str, tp: Any) -> None: ... + def __init__(self, name: str, tp: AnnotationForm) -> None: ... def __call__(self, obj: _T, /) -> _T: ... __supertype__: type | NewType if sys.version_info >= (3, 10): @@ -383,38 +383,90 @@ else: if sys.version_info >= (3, 12): from collections.abc import Buffer as Buffer from types import get_original_bases as get_original_bases - from typing import TypeAliasType as TypeAliasType, override as override + from typing import ( + SupportsAbs as SupportsAbs, + SupportsBytes as SupportsBytes, + SupportsComplex as SupportsComplex, + SupportsFloat as SupportsFloat, + SupportsIndex as SupportsIndex, + SupportsInt as SupportsInt, + SupportsRound as SupportsRound, + override as override, + ) else: def override(arg: _F, /) -> _F: ... def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... - @final - class TypeAliasType: - def __init__( - self, name: str, value: Any, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () - ) -> None: ... - @property - def __value__(self) -> Any: ... - @property - def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ... - @property - def __parameters__(self) -> tuple[Any, ...]: ... - @property - def __name__(self) -> str: ... - # 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: ... + @runtime_checkable + class SupportsInt(Protocol, metaclass=abc.ABCMeta): + __slots__ = () + @abc.abstractmethod + def __int__(self) -> int: ... + + @runtime_checkable + class SupportsFloat(Protocol, metaclass=abc.ABCMeta): + __slots__ = () + @abc.abstractmethod + def __float__(self) -> float: ... + + @runtime_checkable + class SupportsComplex(Protocol, metaclass=abc.ABCMeta): + __slots__ = () + @abc.abstractmethod + def __complex__(self) -> complex: ... + + @runtime_checkable + class SupportsBytes(Protocol, metaclass=abc.ABCMeta): + __slots__ = () + @abc.abstractmethod + def __bytes__(self) -> bytes: ... + + @runtime_checkable + class SupportsIndex(Protocol, metaclass=abc.ABCMeta): + __slots__ = () + @abc.abstractmethod + def __index__(self) -> int: ... + + @runtime_checkable + class SupportsAbs(Protocol[_T_co]): + __slots__ = () + @abc.abstractmethod + def __abs__(self) -> _T_co: ... + + @runtime_checkable + class SupportsRound(Protocol[_T_co]): + __slots__ = () + @overload + @abc.abstractmethod + def __round__(self) -> int: ... + @overload + @abc.abstractmethod + def __round__(self, ndigits: int, /) -> _T_co: ... + +if sys.version_info >= (3, 14): + from io import Reader as Reader, Writer as Writer +else: + @runtime_checkable + class Reader(Protocol[_T_co]): + __slots__ = () + @abc.abstractmethod + def read(self, size: int = ..., /) -> _T_co: ... + + @runtime_checkable + class Writer(Protocol[_T_contra]): + __slots__ = () + @abc.abstractmethod + def write(self, data: _T_contra, /) -> int: ... + if sys.version_info >= (3, 13): from types import CapsuleType as CapsuleType from typing import ( @@ -432,6 +484,7 @@ else: def is_protocol(tp: type, /) -> bool: ... def get_protocol_members(tp: type, /) -> frozenset[str]: ... @final + @type_check_only class _NoDefaultType: ... NoDefault: _NoDefaultType @@ -450,9 +503,9 @@ else: @property def __name__(self) -> str: ... @property - def __bound__(self) -> Any | None: ... + def __bound__(self) -> AnnotationForm | None: ... @property - def __constraints__(self) -> tuple[Any, ...]: ... + def __constraints__(self) -> tuple[AnnotationForm, ...]: ... @property def __covariant__(self) -> bool: ... @property @@ -460,15 +513,15 @@ else: @property def __infer_variance__(self) -> bool: ... @property - def __default__(self) -> Any: ... + def __default__(self) -> AnnotationForm: ... def __init__( self, name: str, - *constraints: Any, - bound: Any | None = None, + *constraints: AnnotationForm, + bound: AnnotationForm | None = None, covariant: bool = False, contravariant: bool = False, - default: Any = ..., + default: AnnotationForm = ..., infer_variance: bool = False, ) -> None: ... def has_default(self) -> bool: ... @@ -484,7 +537,7 @@ else: @property def __name__(self) -> str: ... @property - def __bound__(self) -> Any | None: ... + def __bound__(self) -> AnnotationForm | None: ... @property def __covariant__(self) -> bool: ... @property @@ -492,15 +545,15 @@ else: @property def __infer_variance__(self) -> bool: ... @property - def __default__(self) -> Any: ... + def __default__(self) -> AnnotationForm: ... def __init__( self, name: str, *, - bound: None | type[Any] | str = None, + bound: None | AnnotationForm | str = None, contravariant: bool = False, covariant: bool = False, - default: Any = ..., + default: AnnotationForm = ..., ) -> None: ... @property def args(self) -> ParamSpecArgs: ... @@ -517,8 +570,8 @@ else: @property def __name__(self) -> str: ... @property - def __default__(self) -> Any: ... - def __init__(self, name: str, *, default: Any = ...) -> None: ... + def __default__(self) -> AnnotationForm: ... + def __init__(self, name: str, *, default: AnnotationForm = ...) -> None: ... def __iter__(self) -> Any: ... # Unpack[Self] def has_default(self) -> bool: ... def __typing_prepare_subst__(self, alias: Any, args: Any) -> tuple[Any, ...]: ... @@ -526,8 +579,131 @@ else: ReadOnly: _SpecialForm TypeIs: _SpecialForm +# TypeAliasType was added in Python 3.12, but had significant changes in 3.14. +if sys.version_info >= (3, 14): + from typing import TypeAliasType as TypeAliasType +else: + @final + class TypeAliasType: + def __init__( + self, name: str, value: AnnotationForm, *, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () + ) -> None: ... + @property + def __value__(self) -> AnnotationForm: ... + @property + def __type_params__(self) -> tuple[TypeVar | ParamSpec | TypeVarTuple, ...]: ... + @property + # `__parameters__` can include special forms if a `TypeVarTuple` was + # passed as a `type_params` element to the constructor method. + def __parameters__(self) -> tuple[TypeVar | ParamSpec | AnnotationForm, ...]: ... + @property + def __name__(self) -> str: ... + # 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: Incomplete | tuple[Incomplete, ...]) -> AnnotationForm: ... + def __init_subclass__(cls, *args: Unused, **kwargs: Unused) -> NoReturn: ... + if sys.version_info >= (3, 10): + def __or__(self, right: Any, /) -> _SpecialForm: ... + def __ror__(self, left: Any, /) -> _SpecialForm: ... + +# PEP 727 class Doc: documentation: str def __init__(self, documentation: str, /) -> None: ... def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... + +# PEP 728 +@type_check_only +class _NoExtraItemsType: ... + +NoExtraItems: _NoExtraItemsType + +# PEP 747 +TypeForm: _SpecialForm + +# PEP 649/749 +if sys.version_info >= (3, 14): + from typing import evaluate_forward_ref as evaluate_forward_ref + + from annotationlib import Format as Format, get_annotations as get_annotations, type_repr as type_repr +else: + class Format(enum.IntEnum): + VALUE = 1 + VALUE_WITH_FAKE_GLOBALS = 2 + FORWARDREF = 3 + STRING = 4 + + @overload + def get_annotations( + obj: Any, # any object with __annotations__ or __annotate__ + *, + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key + eval_str: bool = False, + format: Literal[Format.STRING], + ) -> dict[str, str]: ... + @overload + def get_annotations( + obj: Any, # any object with __annotations__ or __annotate__ + *, + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key + eval_str: bool = False, + format: Literal[Format.FORWARDREF], + ) -> dict[str, AnnotationForm | ForwardRef]: ... + @overload + def get_annotations( + obj: Any, # any object with __annotations__ or __annotate__ + *, + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key + eval_str: bool = False, + format: Format = Format.VALUE, # noqa: Y011 + ) -> dict[str, AnnotationForm]: ... + @overload + def evaluate_forward_ref( + forward_ref: ForwardRef, + *, + owner: Callable[..., object] | type[object] | ModuleType | None = None, # any callable, class, or module + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key + type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None, + format: Literal[Format.STRING], + _recursive_guard: Container[str] = ..., + ) -> str: ... + @overload + def evaluate_forward_ref( + forward_ref: ForwardRef, + *, + owner: Callable[..., object] | type[object] | ModuleType | None = None, # any callable, class, or module + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key + type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None, + format: Literal[Format.FORWARDREF], + _recursive_guard: Container[str] = ..., + ) -> AnnotationForm | ForwardRef: ... + @overload + def evaluate_forward_ref( + forward_ref: ForwardRef, + *, + owner: Callable[..., object] | type[object] | ModuleType | None = None, # any callable, class, or module + globals: Mapping[str, Any] | None = None, # value types depend on the key + locals: Mapping[str, Any] | None = None, # value types depend on the key + type_params: Iterable[TypeVar | ParamSpec | TypeVarTuple] | None = None, + format: Format | None = None, + _recursive_guard: Container[str] = ..., + ) -> AnnotationForm: ... + def type_repr(value: object) -> str: ... + +# PEP 661 +class Sentinel: + def __init__(self, name: str, repr: str | None = None) -> None: ... + if sys.version_info >= (3, 14): + def __or__(self, other: Any) -> UnionType: ... # other can be any type form legal for unions + def __ror__(self, other: Any) -> UnionType: ... # other can be any type form legal for unions + elif sys.version_info >= (3, 10): + def __or__(self, other: Any) -> _SpecialForm: ... # other can be any type form legal for unions + def __ror__(self, other: Any) -> _SpecialForm: ... # other can be any type form legal for unions diff --git a/mypy/typeshed/stdlib/unicodedata.pyi b/mypy/typeshed/stdlib/unicodedata.pyi index 77d69edf06af9..9fff042f0b964 100644 --- a/mypy/typeshed/stdlib/unicodedata.pyi +++ b/mypy/typeshed/stdlib/unicodedata.pyi @@ -1,10 +1,10 @@ import sys from _typeshed import ReadOnlyBuffer -from typing import Any, Literal, TypeVar, final, overload +from typing import Any, Final, Literal, TypeVar, final, overload from typing_extensions import TypeAlias ucd_3_2_0: UCD -unidata_version: str +unidata_version: Final[str] if sys.version_info < (3, 10): ucnhash_CAPI: Any diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi index 565dd91c0fda8..0b3fb9122c7b9 100644 --- a/mypy/typeshed/stdlib/unittest/async_case.pyi +++ b/mypy/typeshed/stdlib/unittest/async_case.pyi @@ -21,5 +21,5 @@ class IsolatedAsyncioTestCase(TestCase): def addAsyncCleanup(self, func: Callable[_P, Awaitable[object]], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... if sys.version_info >= (3, 11): async def enterAsyncContext(self, cm: AbstractAsyncContextManager[_T]) -> _T: ... - if sys.version_info >= (3, 9): - def __del__(self) -> None: ... + + def __del__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index a92f03f9745fe..a602196e73c64 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -2,37 +2,21 @@ import logging import sys import unittest.result from _typeshed import SupportsDunderGE, SupportsDunderGT, SupportsDunderLE, SupportsDunderLT, SupportsRSub, SupportsSub +from builtins import _ClassInfo from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Set as AbstractSet from contextlib import AbstractContextManager from re import Pattern -from types import TracebackType -from typing import ( - Any, - AnyStr, - ClassVar, - Final, - Generic, - NamedTuple, - NoReturn, - Protocol, - SupportsAbs, - SupportsRound, - TypeVar, - overload, -) -from typing_extensions import ParamSpec, Self, TypeAlias +from types import GenericAlias, TracebackType +from typing import Any, AnyStr, Final, Generic, NoReturn, Protocol, SupportsAbs, SupportsRound, TypeVar, overload, type_check_only +from typing_extensions import Never, ParamSpec, Self +from unittest._log import _AssertLogsContext, _LoggingWatcher from warnings import WarningMessage -if sys.version_info >= (3, 9): - from types import GenericAlias - -if sys.version_info >= (3, 10): - from types import UnionType - _T = TypeVar("_T") _S = TypeVar("_S", bound=SupportsSub[Any, Any]) _E = TypeVar("_E", bound=BaseException) _FT = TypeVar("_FT", bound=Callable[..., Any]) +_SB = TypeVar("_SB", str, bytes, bytearray) _P = ParamSpec("_P") DIFF_OMITTED: Final[str] @@ -58,29 +42,6 @@ class _AssertRaisesBaseContext(_BaseTestCaseContext): # but it's not possible to construct an overload which expresses that def handle(self, name: str, args: list[Any], kwargs: dict[str, Any]) -> Any: ... -if sys.version_info >= (3, 9): - from unittest._log import _AssertLogsContext, _LoggingWatcher -else: - # Unused dummy for _AssertLogsContext. Starting with Python 3.10, - # this is generic over the logging watcher, but in lower versions - # the watcher is hard-coded. - _L = TypeVar("_L") - - class _LoggingWatcher(NamedTuple): - records: list[logging.LogRecord] - output: list[str] - - class _AssertLogsContext(_BaseTestCaseContext, Generic[_L]): - LOGGING_FORMAT: ClassVar[str] - logger_name: str - level: int - msg: None - def __init__(self, test_case: TestCase, logger_name: str, level: int) -> None: ... - def __enter__(self) -> _LoggingWatcher: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None - ) -> bool | None: ... - def addModuleCleanup(function: Callable[_P, object], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... def doModuleCleanups() -> None: ... @@ -95,16 +56,9 @@ def skipUnless(condition: object, reason: str) -> Callable[[_FT], _FT]: ... class SkipTest(Exception): def __init__(self, reason: str) -> None: ... +@type_check_only class _SupportsAbsAndDunderGE(SupportsDunderGE[Any], SupportsAbs[Any], Protocol): ... -# Keep this alias in sync with builtins._ClassInfo -# We can't import it from builtins or pytype crashes, -# due to the fact that pytype uses a custom builtins stub rather than typeshed's builtins stub -if sys.version_info >= (3, 10): - _ClassInfo: TypeAlias = type | UnionType | tuple[_ClassInfo, ...] -else: - _ClassInfo: TypeAlias = type | tuple[_ClassInfo, ...] - class TestCase: failureException: type[BaseException] longMessage: bool @@ -323,6 +277,20 @@ class TestCase: self, subset: Mapping[Any, Any], dictionary: Mapping[Any, Any], msg: object = None ) -> None: ... + if sys.version_info >= (3, 10): + # Runtime has *args, **kwargs, but will error if any are supplied + def __init_subclass__(cls, *args: Never, **kwargs: Never) -> None: ... + + if sys.version_info >= (3, 14): + def assertIsSubclass(self, cls: type, superclass: type | tuple[type, ...], msg: Any = None) -> None: ... + def assertNotIsSubclass(self, cls: type, superclass: type | tuple[type, ...], msg: Any = None) -> None: ... + def assertHasAttr(self, obj: object, name: str, msg: Any = None) -> None: ... + def assertNotHasAttr(self, obj: object, name: str, msg: Any = None) -> None: ... + def assertStartsWith(self, s: _SB, prefix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ... + def assertNotStartsWith(self, s: _SB, prefix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ... + def assertEndsWith(self, s: _SB, suffix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ... + def assertNotEndsWith(self, s: _SB, suffix: _SB | tuple[_SB, ...], msg: Any = None) -> None: ... + class FunctionTestCase(TestCase): def __init__( self, @@ -341,8 +309,7 @@ class _AssertRaisesContext(_AssertRaisesBaseContext, Generic[_E]): def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, tb: TracebackType | None ) -> bool: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class _AssertWarnsContext(_AssertRaisesBaseContext): warning: WarningMessage diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi index 598e3cd84a5e8..81de40c898496 100644 --- a/mypy/typeshed/stdlib/unittest/loader.pyi +++ b/mypy/typeshed/stdlib/unittest/loader.pyi @@ -35,21 +35,38 @@ class TestLoader: defaultTestLoader: TestLoader 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: ... + if sys.version_info >= (3, 11): + @deprecated("Deprecated since Python 3.11; removed in Python 3.13.") + def getTestCaseNames( + testCaseClass: type[unittest.case.TestCase], + prefix: str, + sortUsing: _SortComparisonMethod = ..., + testNamePatterns: list[str] | None = None, + ) -> Sequence[str]: ... + @deprecated("Deprecated since Python 3.11; removed in Python 3.13.") + def makeSuite( + testCaseClass: type[unittest.case.TestCase], + prefix: str = "test", + sortUsing: _SortComparisonMethod = ..., + suiteClass: _SuiteClass = ..., + ) -> unittest.suite.TestSuite: ... + @deprecated("Deprecated since Python 3.11; removed in Python 3.13.") + def findTestCases( + module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ... + ) -> unittest.suite.TestSuite: ... + else: + 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: ... diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index 22f2ec10634d6..23ead1638ecc2 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -5,12 +5,13 @@ import unittest.result import unittest.suite from collections.abc import Iterable from types import ModuleType -from typing import Any, Final, Protocol +from typing import Any, Final, Protocol, type_check_only from typing_extensions import deprecated MAIN_EXAMPLES: Final[str] MODULE_EXAMPLES: Final[str] +@type_check_only class _TestRunner(Protocol): def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase, /) -> unittest.result.TestResult: ... @@ -63,8 +64,11 @@ class TestProgram: ) -> 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: ... + if sys.version_info >= (3, 11): + @deprecated("Deprecated since Python 3.11; removed in Python 3.13.") + def usageExit(self, msg: Any = None) -> None: ... + else: + 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: ... diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index 1cfd38f540a49..f3e58bcd1c009 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -1,9 +1,10 @@ import sys +from _typeshed import MaybeNone from collections.abc import Awaitable, Callable, Coroutine, Iterable, Mapping, Sequence from contextlib import _GeneratorContextManager from types import TracebackType -from typing import Any, Final, Generic, Literal, TypeVar, overload -from typing_extensions import ParamSpec, Self, TypeAlias +from typing import Any, ClassVar, Final, Generic, Literal, TypeVar, overload, type_check_only +from typing_extensions import ParamSpec, Self, TypeAlias, disjoint_base _T = TypeVar("_T") _TT = TypeVar("_TT", bound=type[Any]) @@ -51,10 +52,7 @@ else: "seal", ) -if sys.version_info < (3, 9): - __version__: Final[str] - -FILTER_DIR: Any +FILTER_DIR: bool # controls the way mock objects respond to `dir` function class _SentinelObject: name: Any @@ -63,38 +61,73 @@ class _SentinelObject: class _Sentinel: def __getattr__(self, name: str) -> Any: ... -sentinel: Any +sentinel: _Sentinel DEFAULT: Any _ArgsKwargs: TypeAlias = tuple[tuple[Any, ...], Mapping[str, Any]] _NameArgsKwargs: TypeAlias = tuple[str, tuple[Any, ...], Mapping[str, Any]] _CallValue: TypeAlias = str | tuple[Any, ...] | Mapping[str, Any] | _ArgsKwargs | _NameArgsKwargs -class _Call(tuple[Any, ...]): - def __new__( - cls, value: _CallValue = (), name: str | None = "", parent: Any | None = None, two: bool = False, from_kall: bool = True - ) -> Self: ... - name: Any - parent: Any - from_kall: Any - def __init__( - self, - value: _CallValue = (), - name: str | None = None, - parent: Any | None = None, - two: bool = False, - from_kall: bool = True, - ) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __ne__(self, value: object, /) -> bool: ... - def __call__(self, *args: Any, **kwargs: Any) -> _Call: ... - def __getattr__(self, attr: str) -> Any: ... - def __getattribute__(self, attr: str) -> Any: ... - @property - def args(self) -> tuple[Any, ...]: ... - @property - def kwargs(self) -> Mapping[str, Any]: ... - def call_list(self) -> Any: ... +if sys.version_info >= (3, 12): + class _Call(tuple[Any, ...]): + def __new__( + cls, + value: _CallValue = (), + name: str | None = "", + parent: _Call | None = None, + two: bool = False, + from_kall: bool = True, + ) -> Self: ... + def __init__( + self, + value: _CallValue = (), + name: str | None = None, + parent: _Call | None = None, + two: bool = False, + from_kall: bool = True, + ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] + def __eq__(self, other: object) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __call__(self, *args: Any, **kwargs: Any) -> _Call: ... + def __getattr__(self, attr: str) -> Any: ... + def __getattribute__(self, attr: str) -> Any: ... + @property + def args(self) -> tuple[Any, ...]: ... + @property + def kwargs(self) -> Mapping[str, Any]: ... + def call_list(self) -> Any: ... + +else: + @disjoint_base + class _Call(tuple[Any, ...]): + def __new__( + cls, + value: _CallValue = (), + name: str | None = "", + parent: _Call | None = None, + two: bool = False, + from_kall: bool = True, + ) -> Self: ... + def __init__( + self, + value: _CallValue = (), + name: str | None = None, + parent: _Call | None = None, + two: bool = False, + from_kall: bool = True, + ) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] + def __eq__(self, other: object) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __call__(self, *args: Any, **kwargs: Any) -> _Call: ... + def __getattr__(self, attr: str) -> Any: ... + def __getattribute__(self, attr: str) -> Any: ... + @property + def args(self) -> tuple[Any, ...]: ... + @property + def kwargs(self) -> Mapping[str, Any]: ... + def call_list(self) -> Any: ... call: _Call @@ -164,7 +197,7 @@ class NonCallableMock(Base, Any): side_effect: Any called: bool call_count: int - call_args: Any + call_args: _Call | MaybeNone call_args_list: _CallList mock_calls: _CallList def _format_mock_call_signature(self, args: Any, kwargs: Any) -> str: ... @@ -266,7 +299,8 @@ class _patch(Generic[_T]): # This class does not exist at runtime, it's a hack to make this work: # @patch("foo") # def bar(..., mock: MagicMock) -> None: ... -class _patch_default_new(_patch[MagicMock | AsyncMock]): +@type_check_only +class _patch_pass_arg(_patch[_T]): @overload def __call__(self, func: _TT) -> _TT: ... # Can't use the following as ParamSpec is only allowed as last parameter: @@ -292,6 +326,7 @@ class _patch_dict: # This class does not exist at runtime, it's a hack to add methods to the # patch() function. +@type_check_only class _patcher: TEST_PREFIX: str dict: type[_patch_dict] @@ -299,41 +334,64 @@ 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__( + def __call__( # type: ignore[overload-overlap] self, target: str, new: _T, - spec: Any | None = ..., - create: bool = ..., - spec_set: Any | None = ..., - autospec: Any | None = ..., - new_callable: Any | None = ..., - **kwargs: Any, + spec: Literal[False] | None = None, + create: bool = False, + spec_set: Literal[False] | None = None, + autospec: Literal[False] | None = None, + new_callable: None = None, + *, + unsafe: bool = False, ) -> _patch[_T]: ... @overload def __call__( self, target: str, *, - spec: Any | None = ..., - create: bool = ..., - spec_set: Any | None = ..., - autospec: Any | None = ..., - new_callable: Any | None = ..., + # If not False or None, this is passed to new_callable + spec: Any | Literal[False] | None = None, + create: bool = False, + # If not False or None, this is passed to new_callable + spec_set: Any | Literal[False] | None = None, + autospec: Literal[False] | None = None, + new_callable: Callable[..., _T], + unsafe: bool = False, + # kwargs are passed to new_callable + **kwargs: Any, + ) -> _patch_pass_arg[_T]: ... + @overload + def __call__( + self, + target: str, + *, + spec: Any | bool | None = None, + create: bool = False, + spec_set: Any | bool | None = None, + autospec: Any | bool | None = None, + new_callable: None = None, + unsafe: bool = False, + # kwargs are passed to the MagicMock/AsyncMock constructor **kwargs: Any, - ) -> _patch_default_new: ... + ) -> _patch_pass_arg[MagicMock | AsyncMock]: ... + # This overload also covers the case, where new==DEFAULT. In this case, the return type is _patch[Any]. + # 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 @staticmethod def object( target: Any, attribute: str, new: _T, - spec: Any | None = ..., - create: bool = ..., - spec_set: Any | None = ..., - autospec: Any | None = ..., - new_callable: Any | None = ..., - **kwargs: Any, + spec: Literal[False] | None = None, + create: bool = False, + spec_set: Literal[False] | None = None, + autospec: Literal[False] | None = None, + new_callable: None = None, + *, + unsafe: bool = False, ) -> _patch[_T]: ... @overload @staticmethod @@ -341,21 +399,71 @@ class _patcher: target: Any, attribute: str, *, - spec: Any | None = ..., - create: bool = ..., - spec_set: Any | None = ..., - autospec: Any | None = ..., - new_callable: Any | None = ..., + # If not False or None, this is passed to new_callable + spec: Any | Literal[False] | None = None, + create: bool = False, + # If not False or None, this is passed to new_callable + spec_set: Any | Literal[False] | None = None, + autospec: Literal[False] | None = None, + new_callable: Callable[..., _T], + unsafe: bool = False, + # kwargs are passed to new_callable **kwargs: Any, - ) -> _patch[MagicMock | AsyncMock]: ... + ) -> _patch_pass_arg[_T]: ... + @overload @staticmethod - def multiple( + def object( target: Any, - spec: Any | None = ..., - create: bool = ..., - spec_set: Any | None = ..., - autospec: Any | None = ..., - new_callable: Any | None = ..., + attribute: str, + *, + spec: Any | bool | None = None, + create: bool = False, + spec_set: Any | bool | None = None, + autospec: Any | bool | None = None, + new_callable: None = None, + unsafe: bool = False, + # kwargs are passed to the MagicMock/AsyncMock constructor + **kwargs: Any, + ) -> _patch_pass_arg[MagicMock | AsyncMock]: ... + @overload + @staticmethod + def multiple( + target: Any | str, + # If not False or None, this is passed to new_callable + spec: Any | Literal[False] | None = None, + create: bool = False, + # If not False or None, this is passed to new_callable + spec_set: Any | Literal[False] | None = None, + autospec: Literal[False] | None = None, + *, + new_callable: Callable[..., _T], + # The kwargs must be DEFAULT + **kwargs: Any, + ) -> _patch_pass_arg[_T]: ... + @overload + @staticmethod + def multiple( + target: Any | str, + # If not False or None, this is passed to new_callable + spec: Any | Literal[False] | None, + create: bool, + # If not False or None, this is passed to new_callable + spec_set: Any | Literal[False] | None, + autospec: Literal[False] | None, + new_callable: Callable[..., _T], + # The kwargs must be DEFAULT + **kwargs: Any, + ) -> _patch_pass_arg[_T]: ... + @overload + @staticmethod + def multiple( + target: Any | str, + spec: Any | bool | None = None, + create: bool = False, + spec_set: Any | bool | None = None, + autospec: Any | bool | None = None, + new_callable: None = None, + # The kwargs are the mock objects or DEFAULT **kwargs: Any, ) -> _patch[Any]: ... @staticmethod @@ -363,7 +471,7 @@ class _patcher: patch: _patcher -class MagicMixin: +class MagicMixin(Base): def __init__(self, *args: Any, **kw: Any) -> None: ... class NonCallableMagicMock(MagicMixin, NonCallableMock): ... @@ -393,18 +501,20 @@ 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: ... def create_mock(self) -> Any: ... def __get__(self, obj: Any, _type: Any | None = None) -> Any: ... -class _ANY: +# See https://github.com/python/typeshed/issues/14701 +class _ANY(Any): def __eq__(self, other: object) -> Literal[True]: ... def __ne__(self, other: object) -> Literal[False]: ... + __hash__: ClassVar[None] # type: ignore[assignment] -ANY: Any +ANY: _ANY if sys.version_info >= (3, 10): def create_autospec( diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index 46da85619d301..f76771f55e131 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -4,26 +4,33 @@ import unittest.result import unittest.suite from _typeshed import SupportsFlush, SupportsWrite from collections.abc import Callable, Iterable -from typing import Any, Generic, Protocol, TypeVar +from typing import Any, Generic, Protocol, TypeVar, type_check_only from typing_extensions import Never, TypeAlias +from warnings import _ActionKind -_ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult] +_ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult[Any]] +@type_check_only class _SupportsWriteAndFlush(SupportsWrite[str], SupportsFlush, Protocol): ... # All methods used by unittest.runner.TextTestResult's stream +@type_check_only class _TextTestStream(_SupportsWriteAndFlush, Protocol): - def writeln(self, arg: str | None = None) -> str: ... + def writeln(self, arg: str | None = None, /) -> None: ... # _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(_TextTestStream): - def __init__(self, stream: _TextTestStream) -> None: ... +class _WritelnDecorator: + def __init__(self, stream: _SupportsWriteAndFlush) -> None: ... + def writeln(self, arg: str | None = None) -> None: ... 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) @@ -35,10 +42,8 @@ class TextTestResult(unittest.result.TestResult, Generic[_StreamT]): showAll: bool # undocumented stream: _StreamT # undocumented if sys.version_info >= (3, 12): - durations: unittest.result._DurationsType | None - def __init__( - self, stream: _StreamT, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None - ) -> None: ... + durations: int | None + def __init__(self, stream: _StreamT, descriptions: bool, verbosity: int, *, durations: int | None = None) -> None: ... else: def __init__(self, stream: _StreamT, descriptions: bool, verbosity: int) -> None: ... @@ -52,11 +57,11 @@ class TextTestRunner: verbosity: int failfast: bool buffer: bool - warnings: str | None + warnings: _ActionKind | None tb_locals: bool if sys.version_info >= (3, 12): - durations: unittest.result._DurationsType | None + durations: int | None def __init__( self, stream: _SupportsWriteAndFlush | None = None, @@ -65,10 +70,10 @@ class TextTestRunner: failfast: bool = False, buffer: bool = False, resultclass: _ResultClassType | None = None, - warnings: str | None = None, + warnings: _ActionKind | None = None, *, tb_locals: bool = False, - durations: unittest.result._DurationsType | None = None, + durations: int | None = None, ) -> None: ... else: def __init__( diff --git a/mypy/typeshed/stdlib/unittest/suite.pyi b/mypy/typeshed/stdlib/unittest/suite.pyi index c10cbc75d7fd8..443396164b6fe 100644 --- a/mypy/typeshed/stdlib/unittest/suite.pyi +++ b/mypy/typeshed/stdlib/unittest/suite.pyi @@ -1,11 +1,12 @@ import unittest.case import unittest.result from collections.abc import Iterable, Iterator +from typing import ClassVar 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: ... @@ -17,6 +18,7 @@ class BaseTestSuite(Iterable[_TestType]): def countTestCases(self) -> int: ... def __iter__(self) -> Iterator[_TestType]: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] class TestSuite(BaseTestSuite): def run(self, result: unittest.result.TestResult, debug: bool = False) -> unittest.result.TestResult: ... diff --git a/mypy/typeshed/stdlib/unittest/util.pyi b/mypy/typeshed/stdlib/unittest/util.pyi index 945b0cecfed09..763c1478f5e6d 100644 --- a/mypy/typeshed/stdlib/unittest/util.pyi +++ b/mypy/typeshed/stdlib/unittest/util.pyi @@ -1,16 +1,33 @@ from collections.abc import MutableSequence, Sequence -from typing import Any, Final, TypeVar +from typing import Any, Final, Literal, Protocol, TypeVar, type_check_only from typing_extensions import TypeAlias +@type_check_only +class _SupportsDunderLT(Protocol): + def __lt__(self, other: Any, /) -> bool: ... + +@type_check_only +class _SupportsDunderGT(Protocol): + def __gt__(self, other: Any, /) -> bool: ... + +@type_check_only +class _SupportsDunderLE(Protocol): + def __le__(self, other: Any, /) -> bool: ... + +@type_check_only +class _SupportsDunderGE(Protocol): + def __ge__(self, other: Any, /) -> bool: ... + _T = TypeVar("_T") _Mismatch: TypeAlias = tuple[_T, _T, int] +_SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT -_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] +_MAX_LENGTH: Final = 80 +_PLACEHOLDER_LEN: Final = 12 +_MIN_BEGIN_LEN: Final = 5 +_MIN_END_LEN: Final = 5 +_MIN_COMMON_LEN: Final = 5 +_MIN_DIFF_LEN: Final = 41 def _shorten(s: str, prefixlen: int, suffixlen: int) -> str: ... def _common_shorten_repr(*args: str) -> tuple[str, ...]: ... @@ -18,6 +35,6 @@ def safe_repr(obj: object, short: bool = False) -> str: ... def strclass(cls: type) -> str: ... def sorted_list_difference(expected: Sequence[_T], actual: Sequence[_T]) -> tuple[list[_T], list[_T]]: ... def unorderable_list_difference(expected: MutableSequence[_T], actual: MutableSequence[_T]) -> tuple[list[_T], list[_T]]: ... -def three_way_cmp(x: Any, y: Any) -> int: ... +def three_way_cmp(x: _SupportsComparison, y: _SupportsComparison) -> Literal[-1, 0, 1]: ... def _count_diff_all_purpose(actual: Sequence[_T], expected: Sequence[_T]) -> list[_Mismatch[_T]]: ... def _count_diff_hashable(actual: Sequence[_T], expected: Sequence[_T]) -> list[_Mismatch[_T]]: ... diff --git a/mypy/typeshed/stdlib/urllib/error.pyi b/mypy/typeshed/stdlib/urllib/error.pyi index 89cec9bf289c4..2173d7e6efaa5 100644 --- a/mypy/typeshed/stdlib/urllib/error.pyi +++ b/mypy/typeshed/stdlib/urllib/error.pyi @@ -6,6 +6,8 @@ __all__ = ["URLError", "HTTPError", "ContentTooShortError"] class URLError(OSError): reason: str | BaseException + # The `filename` attribute only exists if it was provided to `__init__` and wasn't `None`. + filename: str def __init__(self, reason: str | BaseException, filename: str | None = None) -> None: ... class HTTPError(URLError, addinfourl): @@ -16,6 +18,9 @@ class HTTPError(URLError, addinfourl): @property def reason(self) -> str: ... # type: ignore[override] code: int + msg: str + hdrs: Message + fp: IO[bytes] def __init__(self, url: str, code: int, msg: str, hdrs: Message, fp: IO[bytes] | None) -> None: ... class ContentTooShortError(URLError): diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi index 785bb9678ec7f..364892ecdf698 100644 --- a/mypy/typeshed/stdlib/urllib/parse.pyi +++ b/mypy/typeshed/stdlib/urllib/parse.pyi @@ -1,11 +1,9 @@ import sys -from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import Any, AnyStr, Generic, Literal, NamedTuple, TypeVar, overload +from collections.abc import Iterable, Mapping, Sequence +from types import GenericAlias +from typing import Any, AnyStr, Final, Generic, Literal, NamedTuple, Protocol, overload, type_check_only from typing_extensions import TypeAlias -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = [ "urlparse", "urlunparse", @@ -30,23 +28,26 @@ __all__ = [ "SplitResultBytes", ] -uses_relative: list[str] -uses_netloc: list[str] -uses_params: list[str] -non_hierarchical: list[str] -uses_query: list[str] -uses_fragment: list[str] -scheme_chars: str +uses_relative: Final[list[str]] +uses_netloc: Final[list[str]] +uses_params: Final[list[str]] +non_hierarchical: Final[list[str]] +uses_query: Final[list[str]] +uses_fragment: Final[list[str]] +scheme_chars: Final[str] if sys.version_info < (3, 11): - MAX_CACHE_SIZE: int + MAX_CACHE_SIZE: Final[int] class _ResultMixinStr: + __slots__ = () def encode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinBytes: ... class _ResultMixinBytes: + __slots__ = () def decode(self, encoding: str = "ascii", errors: str = "strict") -> _ResultMixinStr: ... class _NetlocResultMixinBase(Generic[AnyStr]): + __slots__ = () @property def username(self) -> AnyStr | None: ... @property @@ -55,11 +56,13 @@ class _NetlocResultMixinBase(Generic[AnyStr]): def hostname(self) -> AnyStr | None: ... @property def port(self) -> int | None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): + __slots__ = () -class _NetlocResultMixinStr(_NetlocResultMixinBase[str], _ResultMixinStr): ... -class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): ... +class _NetlocResultMixinBytes(_NetlocResultMixinBase[bytes], _ResultMixinBytes): + __slots__ = () class _DefragResultBase(NamedTuple, Generic[AnyStr]): url: AnyStr @@ -127,13 +130,7 @@ def quote_from_bytes(bs: bytes | bytearray, safe: str | Iterable[int] = "/") -> def quote_plus(string: str, safe: str | Iterable[int] = "", encoding: str | None = None, errors: str | None = None) -> str: ... @overload def quote_plus(string: bytes | bytearray, safe: str | Iterable[int] = "") -> str: ... - -if sys.version_info >= (3, 9): - def unquote(string: str | bytes, encoding: str = "utf-8", errors: str = "replace") -> str: ... - -else: - def unquote(string: str, encoding: str = "utf-8", errors: str = "replace") -> str: ... - +def unquote(string: str | bytes, encoding: str = "utf-8", errors: str = "replace") -> str: ... def unquote_to_bytes(string: str | bytes | bytearray) -> bytes: ... def unquote_plus(string: str, encoding: str = "utf-8", errors: str = "replace") -> str: ... @overload @@ -141,38 +138,32 @@ def urldefrag(url: str) -> DefragResult: ... @overload def urldefrag(url: bytes | bytearray | None) -> DefragResultBytes: ... -_Q = TypeVar("_Q", bound=str | Iterable[int]) +# The values are passed through `str()` (unless they are bytes), so anything is valid. _QueryType: TypeAlias = ( - Mapping[Any, Any] | Mapping[Any, Sequence[Any]] | Sequence[tuple[Any, Any]] | Sequence[tuple[Any, Sequence[Any]]] + Mapping[str, object] + | Mapping[bytes, object] + | Mapping[str | bytes, object] + | Mapping[str, Sequence[object]] + | Mapping[bytes, Sequence[object]] + | Mapping[str | bytes, Sequence[object]] + | Sequence[tuple[str | bytes, object]] + | Sequence[tuple[str | bytes, Sequence[object]]] ) -@overload -def urlencode( - query: _QueryType, - doseq: bool = False, - safe: str = "", - encoding: str | None = None, - errors: str | None = None, - quote_via: Callable[[AnyStr, str, str, str], str] = ..., -) -> str: ... -@overload -def urlencode( - query: _QueryType, - doseq: bool, - safe: _Q, - encoding: str | None = None, - errors: str | None = None, - quote_via: Callable[[AnyStr, _Q, str, str], str] = ..., -) -> str: ... -@overload +@type_check_only +class _QuoteVia(Protocol): + @overload + def __call__(self, string: str, safe: str | bytes, encoding: str, errors: str, /) -> str: ... + @overload + def __call__(self, string: bytes, safe: str | bytes, /) -> str: ... + def urlencode( query: _QueryType, doseq: bool = False, - *, - safe: _Q, + safe: str | bytes = "", encoding: str | None = None, errors: str | None = None, - quote_via: Callable[[AnyStr, _Q, str, str], str] = ..., + quote_via: _QuoteVia = ..., ) -> str: ... def urljoin(base: AnyStr, url: AnyStr | None, allow_fragments: bool = True) -> AnyStr: ... @overload diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index ad4f91fc31ae7..876b9d3f165cd 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -6,8 +6,8 @@ from email.message import Message from http.client import HTTPConnection, HTTPMessage, HTTPResponse from http.cookiejar import CookieJar from re import Pattern -from typing import IO, Any, ClassVar, NoReturn, Protocol, TypeVar, overload -from typing_extensions import TypeAlias +from typing import IO, Any, ClassVar, NoReturn, Protocol, TypeVar, overload, type_check_only +from typing_extensions import TypeAlias, deprecated from urllib.error import HTTPError as HTTPError from urllib.response import addclosehook, addinfourl @@ -43,10 +43,10 @@ __all__ = [ "getproxies", "urlretrieve", "urlcleanup", - "URLopener", - "FancyURLopener", "HTTPSHandler", ] +if sys.version_info < (3, 14): + __all__ += ["URLopener", "FancyURLopener"] _T = TypeVar("_T") _UrlopenRet: TypeAlias = Any @@ -72,11 +72,16 @@ else: def install_opener(opener: OpenerDirector) -> None: ... def build_opener(*handlers: BaseHandler | Callable[[], BaseHandler]) -> OpenerDirector: ... -if sys.platform == "win32": - from nturl2path import pathname2url as pathname2url, url2pathname as url2pathname +if sys.version_info >= (3, 14): + def url2pathname(url: str, *, require_scheme: bool = False, resolve_host: bool = False) -> str: ... + def pathname2url(pathname: str, *, add_scheme: bool = False) -> str: ... + else: - def url2pathname(pathname: str) -> str: ... - def pathname2url(pathname: str) -> str: ... + if sys.platform == "win32": + from nturl2path import pathname2url as pathname2url, url2pathname as url2pathname + else: + def url2pathname(pathname: str) -> str: ... + def pathname2url(pathname: str) -> str: ... def getproxies() -> dict[str, str]: ... def getproxies_environment() -> dict[str, str]: ... @@ -175,7 +180,7 @@ class HTTPCookieProcessor(BaseHandler): class ProxyHandler(BaseHandler): def __init__(self, proxies: dict[str, str] | None = None) -> None: ... def proxy_open(self, req: Request, proxy: str, type: str) -> _UrlopenRet | None: ... # undocumented - # TODO add a method for every (common) proxy protocol + # TODO: add a method for every (common) proxy protocol class HTTPPasswordMgr: def add_password(self, realm: str, uri: str | Sequence[str], user: str, passwd: str) -> None: ... @@ -232,6 +237,7 @@ class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): auth_header: ClassVar[str] # undocumented def http_error_407(self, req: Request, fp: IO[bytes], code: int, msg: str, headers: HTTPMessage) -> _UrlopenRet | None: ... +@type_check_only class _HTTPConnectionProtocol(Protocol): def __call__( self, @@ -318,91 +324,94 @@ def urlretrieve( ) -> tuple[str, HTTPMessage]: ... def urlcleanup() -> None: ... -class URLopener: - version: ClassVar[str] - def __init__(self, proxies: dict[str, str] | None = None, **x509: str) -> None: ... - def open(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... - def open_unknown(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... - def retrieve( - self, - url: str, - filename: str | None = None, - reporthook: Callable[[int, int, int], object] | None = None, - data: ReadableBuffer | None = None, - ) -> tuple[str, Message | None]: ... - def addheader(self, *args: tuple[str, str]) -> None: ... # undocumented - def cleanup(self) -> None: ... # undocumented - def close(self) -> None: ... # undocumented - def http_error( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: bytes | None = None - ) -> _UrlopenRet: ... # undocumented - def http_error_default( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage - ) -> _UrlopenRet: ... # undocumented - def open_data(self, url: str, data: ReadableBuffer | None = None) -> addinfourl: ... # undocumented - def open_file(self, url: str) -> addinfourl: ... # undocumented - def open_ftp(self, url: str) -> addinfourl: ... # undocumented - def open_http(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... # undocumented - def open_https(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... # undocumented - def open_local_file(self, url: str) -> addinfourl: ... # undocumented - def open_unknown_proxy(self, proxy: str, fullurl: str, data: ReadableBuffer | None = None) -> None: ... # undocumented - def __del__(self) -> None: ... - -class FancyURLopener(URLopener): - def prompt_user_passwd(self, host: str, realm: str) -> tuple[str, str]: ... - def get_user_passwd(self, host: str, realm: str, clear_cache: int = 0) -> tuple[str, str]: ... # undocumented - def http_error_301( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None - ) -> _UrlopenRet | addinfourl | None: ... # undocumented - def http_error_302( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None - ) -> _UrlopenRet | addinfourl | None: ... # undocumented - def http_error_303( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None - ) -> _UrlopenRet | addinfourl | None: ... # undocumented - def http_error_307( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None - ) -> _UrlopenRet | addinfourl | None: ... # undocumented - if sys.version_info >= (3, 11): - def http_error_308( +if sys.version_info < (3, 14): + @deprecated("Deprecated since Python 3.3; removed in Python 3.14. Use newer `urlopen` functions and methods.") + class URLopener: + version: ClassVar[str] + def __init__(self, proxies: dict[str, str] | None = None, **x509: str) -> None: ... + def open(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... + def open_unknown(self, fullurl: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... + def retrieve( + self, + url: str, + filename: str | None = None, + reporthook: Callable[[int, int, int], object] | None = None, + data: ReadableBuffer | None = None, + ) -> tuple[str, Message | None]: ... + def addheader(self, *args: tuple[str, str]) -> None: ... # undocumented + def cleanup(self) -> None: ... # undocumented + def close(self) -> None: ... # undocumented + def http_error( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: bytes | None = None + ) -> _UrlopenRet: ... # undocumented + def http_error_default( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage + ) -> _UrlopenRet: ... # undocumented + def open_data(self, url: str, data: ReadableBuffer | None = None) -> addinfourl: ... # undocumented + def open_file(self, url: str) -> addinfourl: ... # undocumented + def open_ftp(self, url: str) -> addinfourl: ... # undocumented + def open_http(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... # undocumented + def open_https(self, url: str, data: ReadableBuffer | None = None) -> _UrlopenRet: ... # undocumented + def open_local_file(self, url: str) -> addinfourl: ... # undocumented + def open_unknown_proxy(self, proxy: str, fullurl: str, data: ReadableBuffer | None = None) -> None: ... # undocumented + def __del__(self) -> None: ... + + @deprecated("Deprecated since Python 3.3; removed in Python 3.14. Use newer `urlopen` functions and methods.") + class FancyURLopener(URLopener): + def prompt_user_passwd(self, host: str, realm: str) -> tuple[str, str]: ... + def get_user_passwd(self, host: str, realm: str, clear_cache: int = 0) -> tuple[str, str]: ... # undocumented + def http_error_301( self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None ) -> _UrlopenRet | addinfourl | None: ... # undocumented - - def http_error_401( - self, - url: str, - fp: IO[bytes], - errcode: int, - errmsg: str, - headers: HTTPMessage, - data: ReadableBuffer | None = None, - retry: bool = False, - ) -> _UrlopenRet | None: ... # undocumented - def http_error_407( - self, - url: str, - fp: IO[bytes], - errcode: int, - errmsg: str, - headers: HTTPMessage, - data: ReadableBuffer | None = None, - retry: bool = False, - ) -> _UrlopenRet | None: ... # undocumented - def http_error_default( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage - ) -> addinfourl: ... # undocumented - def redirect_internal( - self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None - ) -> _UrlopenRet | None: ... # undocumented - def retry_http_basic_auth( - self, url: str, realm: str, data: ReadableBuffer | None = None - ) -> _UrlopenRet | None: ... # undocumented - def retry_https_basic_auth( - self, url: str, realm: str, data: ReadableBuffer | None = None - ) -> _UrlopenRet | None: ... # undocumented - def retry_proxy_http_basic_auth( - self, url: str, realm: str, data: ReadableBuffer | None = None - ) -> _UrlopenRet | None: ... # undocumented - def retry_proxy_https_basic_auth( - self, url: str, realm: str, data: ReadableBuffer | None = None - ) -> _UrlopenRet | None: ... # undocumented + def http_error_302( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None + ) -> _UrlopenRet | addinfourl | None: ... # undocumented + def http_error_303( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None + ) -> _UrlopenRet | addinfourl | None: ... # undocumented + def http_error_307( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None + ) -> _UrlopenRet | addinfourl | None: ... # undocumented + if sys.version_info >= (3, 11): + def http_error_308( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None = None + ) -> _UrlopenRet | addinfourl | None: ... # undocumented + + def http_error_401( + self, + url: str, + fp: IO[bytes], + errcode: int, + errmsg: str, + headers: HTTPMessage, + data: ReadableBuffer | None = None, + retry: bool = False, + ) -> _UrlopenRet | None: ... # undocumented + def http_error_407( + self, + url: str, + fp: IO[bytes], + errcode: int, + errmsg: str, + headers: HTTPMessage, + data: ReadableBuffer | None = None, + retry: bool = False, + ) -> _UrlopenRet | None: ... # undocumented + def http_error_default( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage + ) -> addinfourl: ... # undocumented + def redirect_internal( + self, url: str, fp: IO[bytes], errcode: int, errmsg: str, headers: HTTPMessage, data: ReadableBuffer | None + ) -> _UrlopenRet | None: ... # undocumented + def retry_http_basic_auth( + self, url: str, realm: str, data: ReadableBuffer | None = None + ) -> _UrlopenRet | None: ... # undocumented + def retry_https_basic_auth( + self, url: str, realm: str, data: ReadableBuffer | None = None + ) -> _UrlopenRet | None: ... # undocumented + def retry_proxy_http_basic_auth( + self, url: str, realm: str, data: ReadableBuffer | None = None + ) -> _UrlopenRet | None: ... # undocumented + def retry_proxy_https_basic_auth( + self, url: str, realm: str, data: ReadableBuffer | None = None + ) -> _UrlopenRet | None: ... # undocumented diff --git a/mypy/typeshed/stdlib/urllib/response.pyi b/mypy/typeshed/stdlib/urllib/response.pyi index bbec4cacc7501..65df9cdff58ff 100644 --- a/mypy/typeshed/stdlib/urllib/response.pyi +++ b/mypy/typeshed/stdlib/urllib/response.pyi @@ -1,4 +1,3 @@ -import sys import tempfile from _typeshed import ReadableBuffer from collections.abc import Callable, Iterable @@ -34,10 +33,8 @@ class addinfo(addbase): class addinfourl(addinfo): url: str code: int | None - if sys.version_info >= (3, 9): - @property - def status(self) -> int | None: ... - + @property + def status(self) -> int | None: ... def __init__(self, fp: IO[bytes], headers: Message, url: str, code: int | None = None) -> None: ... def geturl(self) -> str: ... def getcode(self) -> int | None: ... diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi index 1be7a5ef009fc..055f4def311cd 100644 --- a/mypy/typeshed/stdlib/uuid.pyi +++ b/mypy/typeshed/stdlib/uuid.pyi @@ -2,7 +2,8 @@ import builtins import sys from _typeshed import Unused from enum import Enum -from typing_extensions import TypeAlias +from typing import Final, NoReturn +from typing_extensions import LiteralString, TypeAlias _FieldsType: TypeAlias = tuple[int, int, int, int, int, int] @@ -12,6 +13,10 @@ class SafeUUID(Enum): unknown = None class UUID: + __slots__ = ("int", "is_safe", "__weakref__") + is_safe: Final[SafeUUID] + int: Final[builtins.int] + def __init__( self, hex: str | None = None, @@ -21,11 +26,9 @@ class UUID: int: builtins.int | None = None, version: builtins.int | None = None, *, - is_safe: SafeUUID = ..., + is_safe: SafeUUID = SafeUUID.unknown, ) -> None: ... @property - def is_safe(self) -> SafeUUID: ... - @property def bytes(self) -> builtins.bytes: ... @property def bytes_le(self) -> builtins.bytes: ... @@ -40,8 +43,6 @@ class UUID: @property def hex(self) -> str: ... @property - def int(self) -> builtins.int: ... - @property def node(self) -> builtins.int: ... @property def time(self) -> builtins.int: ... @@ -64,15 +65,16 @@ class UUID: def __gt__(self, other: UUID) -> bool: ... def __ge__(self, other: UUID) -> bool: ... def __hash__(self) -> builtins.int: ... + def __setattr__(self, name: Unused, value: Unused) -> NoReturn: ... -if sys.version_info >= (3, 9): - def getnode() -> int: ... - -else: - def getnode(*, getters: Unused = None) -> int: ... # undocumented - +def getnode() -> int: ... def uuid1(node: int | None = None, clock_seq: int | None = None) -> UUID: ... +if sys.version_info >= (3, 14): + def uuid6(node: int | None = None, clock_seq: int | None = None) -> UUID: ... + def uuid7() -> UUID: ... + def uuid8(a: int | None = None, b: int | None = None, c: int | None = None) -> UUID: ... + if sys.version_info >= (3, 12): def uuid3(namespace: UUID, name: str | bytes) -> UUID: ... @@ -87,14 +89,18 @@ if sys.version_info >= (3, 12): else: def uuid5(namespace: UUID, name: str) -> UUID: ... -NAMESPACE_DNS: UUID -NAMESPACE_URL: UUID -NAMESPACE_OID: UUID -NAMESPACE_X500: UUID -RESERVED_NCS: str -RFC_4122: str -RESERVED_MICROSOFT: str -RESERVED_FUTURE: str +if sys.version_info >= (3, 14): + NIL: Final[UUID] + MAX: Final[UUID] + +NAMESPACE_DNS: Final[UUID] +NAMESPACE_URL: Final[UUID] +NAMESPACE_OID: Final[UUID] +NAMESPACE_X500: Final[UUID] +RESERVED_NCS: Final[LiteralString] +RFC_4122: Final[LiteralString] +RESERVED_MICROSOFT: Final[LiteralString] +RESERVED_FUTURE: Final[LiteralString] if sys.version_info >= (3, 12): def main() -> None: ... diff --git a/mypy/typeshed/stdlib/venv/__init__.pyi b/mypy/typeshed/stdlib/venv/__init__.pyi index 0490c35b44f2a..14db88523dba4 100644 --- a/mypy/typeshed/stdlib/venv/__init__.pyi +++ b/mypy/typeshed/stdlib/venv/__init__.pyi @@ -3,11 +3,11 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Iterable, Sequence from types import SimpleNamespace +from typing import Final logger: logging.Logger -if sys.version_info >= (3, 9): - CORE_VENV_DEPS: tuple[str, ...] +CORE_VENV_DEPS: Final[tuple[str, ...]] class EnvBuilder: system_site_packages: bool @@ -30,17 +30,6 @@ class EnvBuilder: *, scm_ignore_files: Iterable[str] = ..., ) -> None: ... - elif sys.version_info >= (3, 9): - def __init__( - self, - system_site_packages: bool = False, - clear: bool = False, - symlinks: bool = False, - upgrade: bool = False, - with_pip: bool = False, - prompt: str | None = None, - upgrade_deps: bool = False, - ) -> None: ... else: def __init__( self, @@ -50,6 +39,7 @@ class EnvBuilder: upgrade: bool = False, with_pip: bool = False, prompt: str | None = None, + upgrade_deps: bool = False, ) -> None: ... def create(self, env_dir: StrOrBytesPath) -> None: ... @@ -65,8 +55,7 @@ class EnvBuilder: def post_setup(self, context: SimpleNamespace) -> None: ... def replace_variables(self, text: str, context: SimpleNamespace) -> str: ... # undocumented def install_scripts(self, context: SimpleNamespace, path: str) -> None: ... - if sys.version_info >= (3, 9): - def upgrade_dependencies(self, context: SimpleNamespace) -> None: ... + def upgrade_dependencies(self, context: SimpleNamespace) -> None: ... if sys.version_info >= (3, 13): def create_git_ignore_file(self, context: SimpleNamespace) -> None: ... @@ -83,17 +72,6 @@ if sys.version_info >= (3, 13): scm_ignore_files: Iterable[str] = ..., ) -> None: ... -elif sys.version_info >= (3, 9): - def create( - env_dir: StrOrBytesPath, - system_site_packages: bool = False, - clear: bool = False, - symlinks: bool = False, - with_pip: bool = False, - prompt: str | None = None, - upgrade_deps: bool = False, - ) -> None: ... - else: def create( env_dir: StrOrBytesPath, @@ -102,6 +80,7 @@ else: symlinks: bool = False, with_pip: bool = False, prompt: str | None = None, + upgrade_deps: bool = False, ) -> None: ... def main(args: Sequence[str] | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index c7ab1cb091ddb..49c98cb07540e 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -1,9 +1,10 @@ +import re import sys from _warnings import warn as warn, warn_explicit as warn_explicit from collections.abc import Sequence from types import ModuleType, TracebackType -from typing import Any, Generic, Literal, TextIO, TypeVar, overload -from typing_extensions import LiteralString, TypeAlias +from typing import Any, Generic, Literal, TextIO, overload +from typing_extensions import LiteralString, TypeAlias, TypeVar __all__ = [ "warn", @@ -20,12 +21,13 @@ if sys.version_info >= (3, 13): __all__ += ["deprecated"] _T = TypeVar("_T") -_W = TypeVar("_W", bound=list[WarningMessage] | None) +_W_co = TypeVar("_W_co", bound=list[WarningMessage] | None, default=list[WarningMessage] | None, covariant=True) + 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, str | None, type[Warning], str | None, int]] # undocumented, do not mutate +filters: Sequence[tuple[str, re.Pattern[str] | None, type[Warning], re.Pattern[str] | None, int]] # undocumented, do not mutate def showwarning( message: Warning | str, @@ -65,7 +67,7 @@ class WarningMessage: source: Any | None = None, ) -> None: ... -class catch_warnings(Generic[_W]): +class catch_warnings(Generic[_W_co]): if sys.version_info >= (3, 11): @overload def __init__( @@ -91,7 +93,7 @@ class catch_warnings(Generic[_W]): ) -> None: ... @overload def __init__( - self: catch_warnings[list[WarningMessage] | None], + self, *, record: bool, module: ModuleType | None = None, @@ -108,11 +110,9 @@ class catch_warnings(Generic[_W]): self: catch_warnings[list[WarningMessage]], *, record: Literal[True], module: ModuleType | None = None ) -> None: ... @overload - def __init__( - self: catch_warnings[list[WarningMessage] | None], *, record: bool, module: ModuleType | None = None - ) -> None: ... + def __init__(self, *, record: bool, module: ModuleType | None = None) -> None: ... - def __enter__(self) -> _W: ... + def __enter__(self) -> _W_co: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi index 9319d5347c791..fd7dbfade884b 100644 --- a/mypy/typeshed/stdlib/wave.pyi +++ b/mypy/typeshed/stdlib/wave.pyi @@ -3,16 +3,13 @@ from _typeshed import ReadableBuffer, Unused from typing import IO, Any, BinaryIO, Final, Literal, NamedTuple, NoReturn, overload from typing_extensions import Self, TypeAlias, deprecated -if sys.version_info >= (3, 9): - __all__ = ["open", "Error", "Wave_read", "Wave_write"] -else: - __all__ = ["open", "openfp", "Error", "Wave_read", "Wave_write"] +__all__ = ["open", "Error", "Wave_read", "Wave_write"] _File: TypeAlias = str | IO[bytes] class Error(Exception): ... -WAVE_FORMAT_PCM: Final = 1 +WAVE_FORMAT_PCM: Final = 0x0001 class _wave_params(NamedTuple): nchannels: int @@ -38,10 +35,15 @@ class Wave_read: def getcomptype(self) -> str: ... def getcompname(self) -> str: ... def getparams(self) -> _wave_params: ... - @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") - def getmarkers(self) -> None: ... - @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") - def getmark(self, id: Any) -> NoReturn: ... + if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def getmarkers(self) -> None: ... + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def getmark(self, id: Any) -> NoReturn: ... + else: + def getmarkers(self) -> None: ... + def getmark(self, id: Any) -> NoReturn: ... + def setpos(self, pos: int) -> None: ... def readframes(self, nframes: int) -> bytes: ... @@ -63,12 +65,18 @@ class Wave_write: def getcompname(self) -> str: ... def setparams(self, params: _wave_params | tuple[int, int, int, int, str, str]) -> None: ... def getparams(self) -> _wave_params: ... - @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") - def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ... - @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") - def getmark(self, id: Any) -> NoReturn: ... - @deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") - def getmarkers(self) -> None: ... + if sys.version_info >= (3, 13): + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ... + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def getmark(self, id: Any) -> NoReturn: ... + @deprecated("Deprecated since Python 3.13; will be removed in Python 3.15.") + def getmarkers(self) -> None: ... + else: + def setmark(self, id: Any, pos: Any, name: Any) -> NoReturn: ... + def getmark(self, id: Any) -> NoReturn: ... + def getmarkers(self) -> None: ... + def tell(self) -> int: ... def writeframesraw(self, data: ReadableBuffer) -> None: ... def writeframes(self, data: ReadableBuffer) -> None: ... @@ -80,6 +88,3 @@ def open(f: _File, mode: Literal["r", "rb"]) -> Wave_read: ... def open(f: _File, mode: Literal["w", "wb"]) -> Wave_write: ... @overload def open(f: _File, mode: str | None = None) -> Any: ... - -if sys.version_info < (3, 9): - openfp = open diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index aaba7ffc98d95..76ab86b957a13 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -1,18 +1,10 @@ -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_extensions import ParamSpec, Self +from types import GenericAlias +from typing import Any, ClassVar, Generic, TypeVar, final, overload +from typing_extensions import ParamSpec, Self, disjoint_base __all__ = [ "ref", @@ -40,11 +32,42 @@ _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 + __hash__: ClassVar[None] # type: ignore[assignment] + +@final +class ProxyType(Generic[_T]): # "weakproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + __hash__: ClassVar[None] # type: ignore[assignment] + +@disjoint_base +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: ... + 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: ... + __slots__ = ("_func_ref", "_meth_type", "_alive", "__weakref__") + 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: ... @@ -78,6 +101,8 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): @overload def get(self, key: _KT, default: None = None) -> _VT | None: ... @overload + def get(self, key: _KT, default: _VT) -> _VT: ... + @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... # These are incompatible with Mapping def keys(self) -> Iterator[_KT]: ... # type: ignore[override] @@ -92,19 +117,25 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _VT) -> _VT: ... @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... - if sys.version_info >= (3, 9): - def __or__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... - def __ror__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... - # WeakValueDictionary.__ior__ should be kept roughly in line with MutableMapping.update() - @overload # type: ignore[misc] - def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... - @overload - def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... + @overload + def update(self, other: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ... + @overload + def update(self, other: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + @overload + def update(self, other: None = None, /, **kwargs: _VT) -> None: ... + def __or__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... + def __ror__(self, other: Mapping[_T1, _T2]) -> WeakValueDictionary[_KT | _T1, _VT | _T2]: ... + # WeakValueDictionary.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... + @overload + def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... class KeyedRef(ref[_T], Generic[_KT, _T]): + __slots__ = ("key",) 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 @@ -123,6 +154,8 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): @overload def get(self, key: _KT, default: None = None) -> _VT | None: ... @overload + def get(self, key: _KT, default: _VT) -> _VT: ... + @overload def get(self, key: _KT, default: _T) -> _VT | _T: ... # These are incompatible with Mapping def keys(self) -> Iterator[_KT]: ... # type: ignore[override] @@ -140,20 +173,26 @@ class WeakKeyDictionary(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _VT) -> _VT: ... @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... - if sys.version_info >= (3, 9): - def __or__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... - def __ror__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... - # WeakKeyDictionary.__ior__ should be kept roughly in line with MutableMapping.update() - @overload # type: ignore[misc] - def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... - @overload - def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... + @overload + def update(self, dict: SupportsKeysAndGetItem[_KT, _VT], /, **kwargs: _VT) -> None: ... + @overload + def update(self, dict: Iterable[tuple[_KT, _VT]], /, **kwargs: _VT) -> None: ... + @overload + def update(self, dict: None = None, /, **kwargs: _VT) -> None: ... + def __or__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... + def __ror__(self, other: Mapping[_T1, _T2]) -> WeakKeyDictionary[_KT | _T1, _VT | _T2]: ... + # WeakKeyDictionary.__ior__ should be kept roughly in line with MutableMapping.update() + @overload # type: ignore[misc] + def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... + @overload + def __ior__(self, other: Iterable[tuple[_KT, _VT]]) -> Self: ... -class finalize: # TODO: This is a good candidate for to be a `Generic[_P, _T]` class - def __init__(self, obj: object, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... +class finalize(Generic[_P, _T]): + __slots__ = () + def __init__(self, obj: _T, func: Callable[_P, Any], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... def __call__(self, _: Any = None) -> Any | None: ... - def detach(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ... - def peek(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ... + def detach(self) -> tuple[_T, Callable[_P, Any], tuple[Any, ...], dict[str, Any]] | None: ... + def peek(self) -> tuple[_T, Callable[_P, Any], tuple[Any, ...], dict[str, Any]] | None: ... @property def alive(self) -> bool: ... atexit: bool diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index d7bf033172f65..56c30f8727277 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -64,9 +64,16 @@ if sys.platform == "win32": if sys.platform == "darwin": if sys.version_info < (3, 13): - @deprecated("Deprecated in 3.11, to be removed in 3.13.") - class MacOSX(BaseBrowser): - def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... + if sys.version_info >= (3, 11): + @deprecated("Deprecated since Python 3.11; removed in Python 3.13.") + class MacOSX(BaseBrowser): + def __init__(self, name: str) -> None: ... + def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... + + else: + 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/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index d4d04817d7e09..a654bbcdfb615 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -18,13 +18,13 @@ if sys.platform == "win32": def ExpandEnvironmentStrings(string: str, /) -> str: ... def FlushKey(key: _KeyType, /) -> None: ... def LoadKey(key: _KeyType, sub_key: str, file_name: str, /) -> None: ... - def OpenKey(key: _KeyType, sub_key: str, reserved: int = 0, access: int = 131097) -> HKEYType: ... - def OpenKeyEx(key: _KeyType, sub_key: str, reserved: int = 0, access: int = 131097) -> HKEYType: ... + def OpenKey(key: _KeyType, sub_key: str | None, reserved: int = 0, access: int = 131097) -> HKEYType: ... + def OpenKeyEx(key: _KeyType, sub_key: str | None, reserved: int = 0, access: int = 131097) -> HKEYType: ... def QueryInfoKey(key: _KeyType, /) -> tuple[int, int, int]: ... def QueryValue(key: _KeyType, sub_key: str | None, /) -> str: ... def QueryValueEx(key: _KeyType, name: str, /) -> tuple[Any, int]: ... def SaveKey(key: _KeyType, file_name: str, /) -> None: ... - def SetValue(key: _KeyType, sub_key: str, type: int, value: str, /) -> None: ... + def SetValue(key: _KeyType, sub_key: str | None, type: int, value: str, /) -> None: ... @overload # type=REG_DWORD|REG_QWORD def SetValueEx( key: _KeyType, value_name: str | None, reserved: Unused, type: Literal[4, 5], value: int | None, / @@ -59,13 +59,13 @@ if sys.platform == "win32": def EnableReflectionKey(key: _KeyType, /) -> None: ... def QueryReflectionKey(key: _KeyType, /) -> bool: ... - HKEY_CLASSES_ROOT: int - HKEY_CURRENT_USER: int - HKEY_LOCAL_MACHINE: int - HKEY_USERS: int - HKEY_PERFORMANCE_DATA: int - HKEY_CURRENT_CONFIG: int - HKEY_DYN_DATA: int + HKEY_CLASSES_ROOT: Final[int] + HKEY_CURRENT_USER: Final[int] + HKEY_LOCAL_MACHINE: Final[int] + HKEY_USERS: Final[int] + HKEY_PERFORMANCE_DATA: Final[int] + HKEY_CURRENT_CONFIG: Final[int] + HKEY_DYN_DATA: Final[int] KEY_ALL_ACCESS: Final = 983103 KEY_WRITE: Final = 131078 @@ -123,7 +123,7 @@ if sys.platform == "win32": def __int__(self) -> int: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> bool | None: ... def Close(self) -> None: ... def Detach(self) -> int: ... diff --git a/mypy/typeshed/stdlib/winsound.pyi b/mypy/typeshed/stdlib/winsound.pyi index a20e81f94f98f..39dfa7b8b9c42 100644 --- a/mypy/typeshed/stdlib/winsound.pyi +++ b/mypy/typeshed/stdlib/winsound.pyi @@ -13,12 +13,22 @@ if sys.platform == "win32": SND_NODEFAULT: Final = 2 SND_NOSTOP: Final = 16 SND_NOWAIT: Final = 8192 + if sys.version_info >= (3, 14): + SND_SENTRY: Final = 524288 + SND_SYNC: Final = 0 + SND_SYSTEM: Final = 2097152 MB_ICONASTERISK: Final = 64 MB_ICONEXCLAMATION: Final = 48 MB_ICONHAND: Final = 16 MB_ICONQUESTION: Final = 32 MB_OK: Final = 0 + if sys.version_info >= (3, 14): + MB_ICONERROR: Final = 16 + MB_ICONINFORMATION: Final = 64 + MB_ICONSTOP: Final = 16 + MB_ICONWARNING: Final = 48 + 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/headers.pyi b/mypy/typeshed/stdlib/wsgiref/headers.pyi index 2654d79bf4e53..9febad4b32775 100644 --- a/mypy/typeshed/stdlib/wsgiref/headers.pyi +++ b/mypy/typeshed/stdlib/wsgiref/headers.pyi @@ -1,10 +1,10 @@ from re import Pattern -from typing import overload +from typing import Final, overload from typing_extensions import TypeAlias _HeaderList: TypeAlias = list[tuple[str, str]] -tspecials: Pattern[str] # undocumented +tspecials: Final[Pattern[str]] # undocumented class Headers: def __init__(self, headers: _HeaderList | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/wsgiref/simple_server.pyi b/mypy/typeshed/stdlib/wsgiref/simple_server.pyi index 547f562cc1d47..bdf58719c8289 100644 --- a/mypy/typeshed/stdlib/wsgiref/simple_server.pyi +++ b/mypy/typeshed/stdlib/wsgiref/simple_server.pyi @@ -1,14 +1,14 @@ from _typeshed.wsgi import ErrorStream, StartResponse, WSGIApplication, WSGIEnvironment from http.server import BaseHTTPRequestHandler, HTTPServer -from typing import TypeVar, overload +from typing import Final, TypeVar, overload from .handlers import SimpleHandler __all__ = ["WSGIServer", "WSGIRequestHandler", "demo_app", "make_server"] -server_version: str # undocumented -sys_version: str # undocumented -software_version: str # undocumented +server_version: Final[str] # undocumented +sys_version: Final[str] # undocumented +software_version: Final[str] # undocumented class ServerHandler(SimpleHandler): # undocumented server_software: str 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/NodeFilter.pyi b/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi index 80fb73d234332..7b301373f5288 100644 --- a/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi +++ b/mypy/typeshed/stdlib/xml/dom/NodeFilter.pyi @@ -1,19 +1,22 @@ +from typing import Final +from xml.dom.minidom import Node + class NodeFilter: - FILTER_ACCEPT: int - FILTER_REJECT: int - FILTER_SKIP: int + FILTER_ACCEPT: Final = 1 + FILTER_REJECT: Final = 2 + FILTER_SKIP: Final = 3 - SHOW_ALL: int - SHOW_ELEMENT: int - SHOW_ATTRIBUTE: int - SHOW_TEXT: int - SHOW_CDATA_SECTION: int - SHOW_ENTITY_REFERENCE: int - SHOW_ENTITY: int - SHOW_PROCESSING_INSTRUCTION: int - SHOW_COMMENT: int - SHOW_DOCUMENT: int - SHOW_DOCUMENT_TYPE: int - SHOW_DOCUMENT_FRAGMENT: int - SHOW_NOTATION: int - def acceptNode(self, node) -> int: ... + SHOW_ALL: Final = 0xFFFFFFFF + SHOW_ELEMENT: Final = 0x00000001 + SHOW_ATTRIBUTE: Final = 0x00000002 + SHOW_TEXT: Final = 0x00000004 + SHOW_CDATA_SECTION: Final = 0x00000008 + SHOW_ENTITY_REFERENCE: Final = 0x00000010 + SHOW_ENTITY: Final = 0x00000020 + SHOW_PROCESSING_INSTRUCTION: Final = 0x00000040 + SHOW_COMMENT: Final = 0x00000080 + SHOW_DOCUMENT: Final = 0x00000100 + SHOW_DOCUMENT_TYPE: Final = 0x00000200 + SHOW_DOCUMENT_FRAGMENT: Final = 0x00000400 + SHOW_NOTATION: Final = 0x00000800 + def acceptNode(self, node: Node) -> int: ... diff --git a/mypy/typeshed/stdlib/xml/dom/__init__.pyi b/mypy/typeshed/stdlib/xml/dom/__init__.pyi index 8738015638a93..5dbb6c536f617 100644 --- a/mypy/typeshed/stdlib/xml/dom/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/dom/__init__.pyi @@ -1,69 +1,101 @@ -from typing import Any, Final +from typing import Any, Final, Literal from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation class Node: - ELEMENT_NODE: int - ATTRIBUTE_NODE: int - TEXT_NODE: int - CDATA_SECTION_NODE: int - ENTITY_REFERENCE_NODE: int - ENTITY_NODE: int - PROCESSING_INSTRUCTION_NODE: int - COMMENT_NODE: int - DOCUMENT_NODE: int - DOCUMENT_TYPE_NODE: int - DOCUMENT_FRAGMENT_NODE: int - NOTATION_NODE: int + __slots__ = () + ELEMENT_NODE: Final = 1 + ATTRIBUTE_NODE: Final = 2 + TEXT_NODE: Final = 3 + CDATA_SECTION_NODE: Final = 4 + ENTITY_REFERENCE_NODE: Final = 5 + ENTITY_NODE: Final = 6 + PROCESSING_INSTRUCTION_NODE: Final = 7 + COMMENT_NODE: Final = 8 + DOCUMENT_NODE: Final = 9 + DOCUMENT_TYPE_NODE: Final = 10 + DOCUMENT_FRAGMENT_NODE: Final = 11 + NOTATION_NODE: Final = 12 # ExceptionCode -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] +INDEX_SIZE_ERR: Final = 1 +DOMSTRING_SIZE_ERR: Final = 2 +HIERARCHY_REQUEST_ERR: Final = 3 +WRONG_DOCUMENT_ERR: Final = 4 +INVALID_CHARACTER_ERR: Final = 5 +NO_DATA_ALLOWED_ERR: Final = 6 +NO_MODIFICATION_ALLOWED_ERR: Final = 7 +NOT_FOUND_ERR: Final = 8 +NOT_SUPPORTED_ERR: Final = 9 +INUSE_ATTRIBUTE_ERR: Final = 10 +INVALID_STATE_ERR: Final = 11 +SYNTAX_ERR: Final = 12 +INVALID_MODIFICATION_ERR: Final = 13 +NAMESPACE_ERR: Final = 14 +INVALID_ACCESS_ERR: Final = 15 +VALIDATION_ERR: Final = 16 class DOMException(Exception): code: int def __init__(self, *args: Any, **kw: Any) -> None: ... def _get_code(self) -> int: ... -class IndexSizeErr(DOMException): ... -class DomstringSizeErr(DOMException): ... -class HierarchyRequestErr(DOMException): ... -class WrongDocumentErr(DOMException): ... -class InvalidCharacterErr(DOMException): ... -class NoDataAllowedErr(DOMException): ... -class NoModificationAllowedErr(DOMException): ... -class NotFoundErr(DOMException): ... -class NotSupportedErr(DOMException): ... -class InuseAttributeErr(DOMException): ... -class InvalidStateErr(DOMException): ... -class SyntaxErr(DOMException): ... -class InvalidModificationErr(DOMException): ... -class NamespaceErr(DOMException): ... -class InvalidAccessErr(DOMException): ... -class ValidationErr(DOMException): ... +class IndexSizeErr(DOMException): + code: Literal[1] + +class DomstringSizeErr(DOMException): + code: Literal[2] + +class HierarchyRequestErr(DOMException): + code: Literal[3] + +class WrongDocumentErr(DOMException): + code: Literal[4] + +class InvalidCharacterErr(DOMException): + code: Literal[5] + +class NoDataAllowedErr(DOMException): + code: Literal[6] + +class NoModificationAllowedErr(DOMException): + code: Literal[7] + +class NotFoundErr(DOMException): + code: Literal[8] + +class NotSupportedErr(DOMException): + code: Literal[9] + +class InuseAttributeErr(DOMException): + code: Literal[10] + +class InvalidStateErr(DOMException): + code: Literal[11] + +class SyntaxErr(DOMException): + code: Literal[12] + +class InvalidModificationErr(DOMException): + code: Literal[13] + +class NamespaceErr(DOMException): + code: Literal[14] + +class InvalidAccessErr(DOMException): + code: Literal[15] + +class ValidationErr(DOMException): + code: Literal[16] class UserDataHandler: - NODE_CLONED: int - NODE_IMPORTED: int - NODE_DELETED: int - NODE_RENAMED: int - -XML_NAMESPACE: Final[str] -XMLNS_NAMESPACE: Final[str] -XHTML_NAMESPACE: Final[str] + NODE_CLONED: Final = 1 + NODE_IMPORTED: Final = 2 + NODE_DELETED: Final = 3 + NODE_RENAMED: Final = 4 + +XML_NAMESPACE: Final = "http://www.w3.org/XML/1998/namespace" +XMLNS_NAMESPACE: Final = "http://www.w3.org/2000/xmlns/" +XHTML_NAMESPACE: Final = "http://www.w3.org/1999/xhtml" EMPTY_NAMESPACE: Final[None] EMPTY_PREFIX: Final[None] diff --git a/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi index 45f0af7aa9799..2b9ac88769700 100644 --- a/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/expatbuilder.pyi @@ -1,100 +1,126 @@ -from _typeshed import Incomplete, ReadableBuffer, SupportsRead -from typing import Any, NoReturn -from xml.dom.minidom import Document, DOMImplementation, Node, TypeInfo +from _typeshed import ReadableBuffer, SupportsRead +from typing import Any, Final, NoReturn +from typing_extensions import TypeAlias +from xml.dom.minidom import Document, DocumentFragment, DOMImplementation, Element, Node, TypeInfo from xml.dom.xmlbuilder import DOMBuilderFilter, Options +from xml.parsers.expat import XMLParserType -TEXT_NODE = Node.TEXT_NODE -CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE -DOCUMENT_NODE = Node.DOCUMENT_NODE -FILTER_ACCEPT = DOMBuilderFilter.FILTER_ACCEPT -FILTER_REJECT = DOMBuilderFilter.FILTER_REJECT -FILTER_SKIP = DOMBuilderFilter.FILTER_SKIP -FILTER_INTERRUPT = DOMBuilderFilter.FILTER_INTERRUPT -theDOMImplementation: DOMImplementation | None +_Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]] # same as in pyexpat + +TEXT_NODE: Final = Node.TEXT_NODE +CDATA_SECTION_NODE: Final = Node.CDATA_SECTION_NODE +DOCUMENT_NODE: Final = Node.DOCUMENT_NODE +FILTER_ACCEPT: Final = DOMBuilderFilter.FILTER_ACCEPT +FILTER_REJECT: Final = DOMBuilderFilter.FILTER_REJECT +FILTER_SKIP: Final = DOMBuilderFilter.FILTER_SKIP +FILTER_INTERRUPT: Final = DOMBuilderFilter.FILTER_INTERRUPT +theDOMImplementation: DOMImplementation class ElementInfo: - tagName: Incomplete - def __init__(self, tagName, model: Incomplete | None = None) -> None: ... - def getAttributeType(self, aname) -> TypeInfo: ... - def getAttributeTypeNS(self, namespaceURI, localName) -> TypeInfo: ... + __slots__ = ("_attr_info", "_model", "tagName") + tagName: str + def __init__(self, tagName: str, model: _Model | None = None) -> None: ... + def getAttributeType(self, aname: str) -> TypeInfo: ... + def getAttributeTypeNS(self, namespaceURI: str | None, localName: str) -> TypeInfo: ... def isElementContent(self) -> bool: ... def isEmpty(self) -> bool: ... - def isId(self, aname) -> bool: ... - def isIdNS(self, euri, ename, auri, aname) -> bool: ... + def isId(self, aname: str) -> bool: ... + def isIdNS(self, euri: str, ename: str, auri: str, aname: str) -> bool: ... class ExpatBuilder: document: Document # Created in self.reset() - curNode: Incomplete # Created in self.reset() + curNode: DocumentFragment | Element | Document # Created in self.reset() def __init__(self, options: Options | None = None) -> None: ... - def createParser(self): ... - def getParser(self): ... + def createParser(self) -> XMLParserType: ... + def getParser(self) -> XMLParserType: ... def reset(self) -> None: ... - def install(self, parser) -> None: ... + def install(self, parser: XMLParserType) -> None: ... def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> Document: ... def parseString(self, string: str | ReadableBuffer) -> Document: ... - def start_doctype_decl_handler(self, doctypeName, systemId, publicId, has_internal_subset) -> None: ... + def start_doctype_decl_handler( + self, doctypeName: str, systemId: str | None, publicId: str | None, has_internal_subset: bool + ) -> None: ... def end_doctype_decl_handler(self) -> None: ... - def pi_handler(self, target, data) -> None: ... - def character_data_handler_cdata(self, data) -> None: ... - def character_data_handler(self, data) -> None: ... + def pi_handler(self, target: str, data: str) -> None: ... + def character_data_handler_cdata(self, data: str) -> None: ... + def character_data_handler(self, data: str) -> None: ... def start_cdata_section_handler(self) -> None: ... def end_cdata_section_handler(self) -> None: ... - def entity_decl_handler(self, entityName, is_parameter_entity, value, base, systemId, publicId, notationName) -> None: ... - def notation_decl_handler(self, notationName, base, systemId, publicId) -> None: ... - def comment_handler(self, data) -> None: ... - def external_entity_ref_handler(self, context, base, systemId, publicId) -> int: ... - def first_element_handler(self, name, attributes) -> None: ... - def start_element_handler(self, name, attributes) -> None: ... - def end_element_handler(self, name) -> None: ... - def element_decl_handler(self, name, model) -> None: ... - def attlist_decl_handler(self, elem, name, type, default, required) -> None: ... - def xml_decl_handler(self, version, encoding, standalone) -> None: ... + def entity_decl_handler( + self, + entityName: str, + is_parameter_entity: bool, + value: str | None, + base: str | None, + systemId: str, + publicId: str | None, + notationName: str | None, + ) -> None: ... + def notation_decl_handler(self, notationName: str, base: str | None, systemId: str, publicId: str | None) -> None: ... + def comment_handler(self, data: str) -> None: ... + def external_entity_ref_handler(self, context: str, base: str | None, systemId: str | None, publicId: str | None) -> int: ... + def first_element_handler(self, name: str, attributes: list[str]) -> None: ... + def start_element_handler(self, name: str, attributes: list[str]) -> None: ... + def end_element_handler(self, name: str) -> None: ... + def element_decl_handler(self, name: str, model: _Model) -> None: ... + def attlist_decl_handler(self, elem: str, name: str, type: str, default: str | None, required: bool) -> None: ... + def xml_decl_handler(self, version: str, encoding: str | None, standalone: int) -> None: ... class FilterVisibilityController: + __slots__ = ("filter",) filter: DOMBuilderFilter def __init__(self, filter: DOMBuilderFilter) -> None: ... def startContainer(self, node: Node) -> int: ... def acceptNode(self, node: Node) -> int: ... class FilterCrutch: - def __init__(self, builder) -> None: ... + __slots__ = ("_builder", "_level", "_old_start", "_old_end") + def __init__(self, builder: ExpatBuilder) -> None: ... class Rejecter(FilterCrutch): + __slots__ = () def start_element_handler(self, *args: Any) -> None: ... def end_element_handler(self, *args: Any) -> None: ... class Skipper(FilterCrutch): + __slots__ = () def start_element_handler(self, *args: Any) -> None: ... def end_element_handler(self, *args: Any) -> None: ... class FragmentBuilder(ExpatBuilder): - fragment: Incomplete | None - originalDocument: Incomplete - context: Incomplete - def __init__(self, context, options: Options | None = None) -> None: ... + fragment: DocumentFragment | None + originalDocument: Document + context: Node + def __init__(self, context: Node, options: Options | None = None) -> None: ... + def reset(self) -> None: ... + def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> DocumentFragment: ... # type: ignore[override] + def parseString(self, string: ReadableBuffer | str) -> DocumentFragment: ... # type: ignore[override] + def external_entity_ref_handler(self, context: str, base: str | None, systemId: str | None, publicId: str | None) -> int: ... class Namespaces: - def createParser(self): ... - def install(self, parser) -> None: ... - def start_namespace_decl_handler(self, prefix, uri) -> None: ... - def start_element_handler(self, name, attributes) -> None: ... - def end_element_handler(self, name) -> None: ... + def createParser(self) -> XMLParserType: ... + def install(self, parser: XMLParserType) -> None: ... + def start_namespace_decl_handler(self, prefix: str | None, uri: str) -> None: ... + def start_element_handler(self, name: str, attributes: list[str]) -> None: ... + def end_element_handler(self, name: str) -> None: ... # only exists if __debug__ class ExpatBuilderNS(Namespaces, ExpatBuilder): ... class FragmentBuilderNS(Namespaces, FragmentBuilder): ... class ParseEscape(Exception): ... class InternalSubsetExtractor(ExpatBuilder): - subset: Any | None - def getSubset(self) -> Any | None: ... + subset: str | list[str] | None = None + def getSubset(self) -> str: ... def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> None: ... # type: ignore[override] def parseString(self, string: str | ReadableBuffer) -> None: ... # type: ignore[override] - def start_doctype_decl_handler(self, name, publicId, systemId, has_internal_subset) -> None: ... # type: ignore[override] + def start_doctype_decl_handler( # type: ignore[override] + self, name: str, publicId: str | None, systemId: str | None, has_internal_subset: bool + ) -> None: ... def end_doctype_decl_handler(self) -> NoReturn: ... - def start_element_handler(self, name, attrs) -> NoReturn: ... + def start_element_handler(self, name: str, attrs: list[str]) -> NoReturn: ... -def parse(file: str | SupportsRead[ReadableBuffer | str], namespaces: bool = True): ... -def parseString(string: str | ReadableBuffer, namespaces: bool = True): ... -def parseFragment(file, context, namespaces: bool = True): ... -def parseFragmentString(string: str, context, namespaces: bool = True): ... +def parse(file: str | SupportsRead[ReadableBuffer | str], namespaces: bool = True) -> Document: ... +def parseString(string: str | ReadableBuffer, namespaces: bool = True) -> Document: ... +def parseFragment(file: str | SupportsRead[ReadableBuffer | str], context: Node, namespaces: bool = True) -> DocumentFragment: ... +def parseFragmentString(string: str | ReadableBuffer, context: Node, namespaces: bool = True) -> DocumentFragment: ... def makeBuilder(options: Options) -> ExpatBuilderNS | ExpatBuilder: ... diff --git a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi index 162f60254a585..6fcaee019dc20 100644 --- a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi @@ -8,11 +8,13 @@ _T = TypeVar("_T") StringTypes: tuple[type[str]] class NodeList(list[_T]): + __slots__ = () @property def length(self) -> int: ... def item(self, index: int) -> _T | None: ... class EmptyNodeList(tuple[()]): + __slots__ = () @property def length(self) -> Literal[0]: ... def item(self, index: int) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index fae2c4d987146..e0431417aa3c0 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -1,404 +1,678 @@ -import sys import xml.dom +from _collections_abc import dict_keys, dict_values from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite -from typing import Literal, NoReturn, TypeVar, overload -from typing_extensions import Self -from xml.dom.minicompat import NodeList +from collections.abc import Iterable, Sequence +from types import TracebackType +from typing import Any, ClassVar, Generic, Literal, NoReturn, Protocol, TypeVar, overload, type_check_only +from typing_extensions import Self, TypeAlias +from xml.dom.minicompat import EmptyNodeList, NodeList from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS from xml.sax.xmlreader import XMLReader +_NSName: TypeAlias = tuple[str | None, str] + +# Entity can also have children, but it's not implemented the same way as the +# others, so is deliberately omitted here. +_NodesWithChildren: TypeAlias = DocumentFragment | Attr | Element | Document +_NodesThatAreChildren: TypeAlias = CDATASection | Comment | DocumentType | Element | Notation | ProcessingInstruction | Text + +_AttrChildren: TypeAlias = Text # Also EntityReference, but we don't implement it +_ElementChildren: TypeAlias = Element | ProcessingInstruction | Comment | Text | CDATASection +_EntityChildren: TypeAlias = Text # I think; documentation is a little unclear +_DocumentFragmentChildren: TypeAlias = Element | Text | CDATASection | ProcessingInstruction | Comment | Notation +_DocumentChildren: TypeAlias = Comment | DocumentType | Element | ProcessingInstruction + _N = TypeVar("_N", bound=Node) +_ChildNodeVar = TypeVar("_ChildNodeVar", bound=_NodesThatAreChildren) +_ChildNodePlusFragmentVar = TypeVar("_ChildNodePlusFragmentVar", bound=_NodesThatAreChildren | DocumentFragment) +_DocumentChildrenVar = TypeVar("_DocumentChildrenVar", bound=_DocumentChildren) +_ImportableNodeVar = TypeVar( + "_ImportableNodeVar", + bound=DocumentFragment + | Attr + | Element + | ProcessingInstruction + | CharacterData + | Text + | Comment + | CDATASection + | Entity + | Notation, +) + +@type_check_only +class _DOMErrorHandler(Protocol): + def handleError(self, error: Exception) -> bool: ... + +@type_check_only +class _UserDataHandler(Protocol): + def handle(self, operation: int, key: str, data: Any, src: Node, dst: Node) -> None: ... def parse( file: str | SupportsRead[ReadableBuffer | str], parser: XMLReader | None = None, bufsize: int | None = None ) -> Document: ... def parseString(string: str | ReadableBuffer, parser: XMLReader | None = None) -> Document: ... -def getDOMImplementation(features=None) -> DOMImplementation | None: ... +@overload +def getDOMImplementation(features: None = None) -> DOMImplementation: ... +@overload +def getDOMImplementation(features: str | Iterable[tuple[str, str | None]]) -> DOMImplementation | None: ... class Node(xml.dom.Node): - namespaceURI: str | None - parentNode: Incomplete - ownerDocument: Incomplete - nextSibling: Incomplete - previousSibling: Incomplete - prefix: Incomplete + parentNode: _NodesWithChildren | Entity | None + ownerDocument: Document | None + nextSibling: _NodesThatAreChildren | None + previousSibling: _NodesThatAreChildren | None + namespaceURI: str | None # non-null only for Element and Attr + prefix: str | None # non-null only for NS Element and Attr + + # These aren't defined on Node, but they exist on all Node subclasses + # and various methods of Node require them to exist. + childNodes: ( + NodeList[_DocumentFragmentChildren] + | NodeList[_AttrChildren] + | NodeList[_ElementChildren] + | NodeList[_DocumentChildren] + | NodeList[_EntityChildren] + | EmptyNodeList + ) + nodeType: ClassVar[Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]] + nodeName: str | None # only possibly None on DocumentType + + # Not defined on Node, but exist on all Node subclasses. + nodeValue: str | None # non-null for Attr, ProcessingInstruction, Text, Comment, and CDATASection + attributes: NamedNodeMap | None # non-null only for Element + @property - def firstChild(self) -> Node | None: ... + def firstChild(self) -> _NodesThatAreChildren | None: ... @property - def lastChild(self) -> Node | None: ... + def lastChild(self) -> _NodesThatAreChildren | None: ... @property - def localName(self) -> str | None: ... + def localName(self) -> str | None: ... # non-null only for Element and Attr def __bool__(self) -> Literal[True]: ... - if sys.version_info >= (3, 9): - @overload - def toxml(self, encoding: str, standalone: bool | None = None) -> bytes: ... - @overload - def toxml(self, encoding: None = None, standalone: bool | None = None) -> str: ... - @overload - def toprettyxml( - self, - indent: str = "\t", - newl: str = "\n", - # Handle any case where encoding is not provided or where it is passed with None - encoding: None = None, - standalone: bool | None = None, - ) -> str: ... - @overload - def toprettyxml( - self, - indent: str, - newl: str, - # Handle cases where encoding is passed as str *positionally* - encoding: str, - standalone: bool | None = None, - ) -> bytes: ... - @overload - def toprettyxml( - self, - indent: str = "\t", - newl: str = "\n", - # Handle all cases where encoding is passed as a keyword argument; because standalone - # comes after, it will also have to be a keyword arg if encoding is - *, - encoding: str, - standalone: bool | None = None, - ) -> bytes: ... - else: - @overload - def toxml(self, encoding: str) -> bytes: ... - @overload - def toxml(self, encoding: None = None) -> str: ... - @overload - def toprettyxml( - self, - indent: str = "\t", - newl: str = "\n", - # Handle any case where encoding is not provided or where it is passed with None - encoding: None = None, - ) -> str: ... - @overload - def toprettyxml( - self, - indent: str, - newl: str, - # Handle cases where encoding is passed as str *positionally* - encoding: str, - ) -> bytes: ... - @overload - def toprettyxml( - self, - indent: str = "\t", - newl: str = "\n", - # Handle all cases where encoding is passed as a keyword argument - *, - encoding: str, - ) -> bytes: ... - + @overload + def toxml(self, encoding: str, standalone: bool | None = None) -> bytes: ... + @overload + def toxml(self, encoding: None = None, standalone: bool | None = None) -> str: ... + @overload + def toprettyxml( + self, + indent: str = "\t", + newl: str = "\n", + # Handle any case where encoding is not provided or where it is passed with None + encoding: None = None, + standalone: bool | None = None, + ) -> str: ... + @overload + def toprettyxml( + self, + indent: str, + newl: str, + # Handle cases where encoding is passed as str *positionally* + encoding: str, + standalone: bool | None = None, + ) -> bytes: ... + @overload + def toprettyxml( + self, + indent: str = "\t", + newl: str = "\n", + # Handle all cases where encoding is passed as a keyword argument; because standalone + # comes after, it will also have to be a keyword arg if encoding is + *, + encoding: str, + standalone: bool | None = None, + ) -> bytes: ... def hasChildNodes(self) -> bool: ... - def insertBefore(self, newChild, refChild): ... - def appendChild(self, node: _N) -> _N: ... - def replaceChild(self, newChild, oldChild): ... - def removeChild(self, oldChild): ... - def normalize(self) -> None: ... - def cloneNode(self, deep): ... - def isSupported(self, feature, version): ... - def isSameNode(self, other): ... - def getInterface(self, feature): ... - def getUserData(self, key): ... - def setUserData(self, key, data, handler): ... - childNodes: Incomplete + def insertBefore( # type: ignore[misc] + self: _NodesWithChildren, # pyright: ignore[reportGeneralTypeIssues] + newChild: _ChildNodePlusFragmentVar, + refChild: _NodesThatAreChildren | None, + ) -> _ChildNodePlusFragmentVar: ... + def appendChild( # type: ignore[misc] + self: _NodesWithChildren, node: _ChildNodePlusFragmentVar # pyright: ignore[reportGeneralTypeIssues] + ) -> _ChildNodePlusFragmentVar: ... + @overload + def replaceChild( # type: ignore[misc] + self: _NodesWithChildren, newChild: DocumentFragment, oldChild: _ChildNodeVar + ) -> _ChildNodeVar | DocumentFragment: ... + @overload + def replaceChild( # type: ignore[misc] + self: _NodesWithChildren, newChild: _NodesThatAreChildren, oldChild: _ChildNodeVar + ) -> _ChildNodeVar | None: ... + def removeChild(self: _NodesWithChildren, oldChild: _ChildNodeVar) -> _ChildNodeVar: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def normalize(self: _NodesWithChildren) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def cloneNode(self, deep: bool) -> Self | None: ... + def isSupported(self, feature: str, version: str | None) -> bool: ... + def isSameNode(self, other: Node) -> bool: ... + def getInterface(self, feature: str) -> Self | None: ... + def getUserData(self, key: str) -> Any | None: ... + def setUserData(self, key: str, data: Any, handler: _UserDataHandler) -> Any: ... def unlink(self) -> None: ... def __enter__(self) -> Self: ... - def __exit__(self, et, ev, tb) -> None: ... + def __exit__(self, et: type[BaseException] | None, ev: BaseException | None, tb: TracebackType | None) -> None: ... + +_DFChildrenVar = TypeVar("_DFChildrenVar", bound=_DocumentFragmentChildren) +_DFChildrenPlusFragment = TypeVar("_DFChildrenPlusFragment", bound=_DocumentFragmentChildren | DocumentFragment) class DocumentFragment(Node): - nodeType: int - nodeName: str - nodeValue: Incomplete - attributes: Incomplete - parentNode: Incomplete - childNodes: Incomplete + nodeType: ClassVar[Literal[11]] + nodeName: Literal["#document-fragment"] + nodeValue: None + attributes: None + + parentNode: None + nextSibling: None + previousSibling: None + childNodes: NodeList[_DocumentFragmentChildren] + @property + def firstChild(self) -> _DocumentFragmentChildren | None: ... + @property + def lastChild(self) -> _DocumentFragmentChildren | None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... def __init__(self) -> None: ... + def insertBefore( # type: ignore[override] + self, newChild: _DFChildrenPlusFragment, refChild: _DocumentFragmentChildren | None + ) -> _DFChildrenPlusFragment: ... + def appendChild(self, node: _DFChildrenPlusFragment) -> _DFChildrenPlusFragment: ... # type: ignore[override] + @overload # type: ignore[override] + def replaceChild(self, newChild: DocumentFragment, oldChild: _DFChildrenVar) -> _DFChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _DocumentFragmentChildren, oldChild: _DFChildrenVar) -> _DFChildrenVar | None: ... # type: ignore[override] + def removeChild(self, oldChild: _DFChildrenVar) -> _DFChildrenVar: ... # type: ignore[override] + +_AttrChildrenVar = TypeVar("_AttrChildrenVar", bound=_AttrChildren) +_AttrChildrenPlusFragment = TypeVar("_AttrChildrenPlusFragment", bound=_AttrChildren | DocumentFragment) class Attr(Node): - name: str - nodeType: int - attributes: Incomplete - specified: bool - ownerElement: Incomplete + __slots__ = ("_name", "_value", "namespaceURI", "_prefix", "childNodes", "_localName", "ownerDocument", "ownerElement") + nodeType: ClassVar[Literal[2]] + nodeName: str # same as Attr.name + nodeValue: str # same as Attr.value + attributes: None + + parentNode: None + nextSibling: None + previousSibling: None + childNodes: NodeList[_AttrChildren] + @property + def firstChild(self) -> _AttrChildren | None: ... + @property + def lastChild(self) -> _AttrChildren | None: ... + namespaceURI: str | None - childNodes: Incomplete - nodeName: Incomplete - nodeValue: str + prefix: str | None + @property + def localName(self) -> str: ... + + name: str value: str - prefix: Incomplete + specified: bool + ownerElement: Element | None + def __init__( - self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: Incomplete | None = None + self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: str | None = None ) -> None: ... def unlink(self) -> None: ... @property def isId(self) -> bool: ... @property - def schemaType(self): ... + def schemaType(self) -> TypeInfo: ... + def insertBefore(self, newChild: _AttrChildrenPlusFragment, refChild: _AttrChildren | None) -> _AttrChildrenPlusFragment: ... # type: ignore[override] + def appendChild(self, node: _AttrChildrenPlusFragment) -> _AttrChildrenPlusFragment: ... # type: ignore[override] + @overload # type: ignore[override] + def replaceChild(self, newChild: DocumentFragment, oldChild: _AttrChildrenVar) -> _AttrChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _AttrChildren, oldChild: _AttrChildrenVar) -> _AttrChildrenVar | None: ... # type: ignore[override] + def removeChild(self, oldChild: _AttrChildrenVar) -> _AttrChildrenVar: ... # type: ignore[override] +# In the DOM, this interface isn't specific to Attr, but our implementation is +# because that's the only place we use it. class NamedNodeMap: - def __init__(self, attrs, attrsNS, ownerElement) -> None: ... - def item(self, index): ... - def items(self): ... - def itemsNS(self): ... - def __contains__(self, key): ... - def keys(self): ... - def keysNS(self): ... - def values(self): ... - def get(self, name: str, value: Incomplete | None = None): ... + __slots__ = ("_attrs", "_attrsNS", "_ownerElement") + def __init__(self, attrs: dict[str, Attr], attrsNS: dict[_NSName, Attr], ownerElement: Element) -> None: ... + @property + def length(self) -> int: ... + def item(self, index: int) -> Node | None: ... + def items(self) -> list[tuple[str, str]]: ... + def itemsNS(self) -> list[tuple[_NSName, str]]: ... + def __contains__(self, key: str | _NSName) -> bool: ... + def keys(self) -> dict_keys[str, Attr]: ... + def keysNS(self) -> dict_keys[_NSName, Attr]: ... + def values(self) -> dict_values[str, Attr]: ... + def get(self, name: str, value: Attr | None = None) -> Attr | None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __len__(self) -> int: ... def __eq__(self, other: object) -> bool: ... def __ge__(self, other: NamedNodeMap) -> bool: ... def __gt__(self, other: NamedNodeMap) -> bool: ... def __le__(self, other: NamedNodeMap) -> bool: ... def __lt__(self, other: NamedNodeMap) -> bool: ... - def __getitem__(self, attname_or_tuple: tuple[str, str | None] | str): ... + def __getitem__(self, attname_or_tuple: _NSName | str) -> Attr: ... def __setitem__(self, attname: str, value: Attr | str) -> None: ... def getNamedItem(self, name: str) -> Attr | None: ... - def getNamedItemNS(self, namespaceURI: str, localName: str | None) -> Attr | None: ... + def getNamedItemNS(self, namespaceURI: str | None, localName: str) -> Attr | None: ... def removeNamedItem(self, name: str) -> Attr: ... - def removeNamedItemNS(self, namespaceURI: str, localName: str | None): ... - def setNamedItem(self, node: Attr) -> Attr: ... - def setNamedItemNS(self, node: Attr) -> Attr: ... - def __delitem__(self, attname_or_tuple: tuple[str, str | None] | str) -> None: ... - @property - def length(self) -> int: ... + def removeNamedItemNS(self, namespaceURI: str | None, localName: str) -> Attr: ... + def setNamedItem(self, node: Attr) -> Attr | None: ... + def setNamedItemNS(self, node: Attr) -> Attr | None: ... + def __delitem__(self, attname_or_tuple: _NSName | str) -> None: ... AttributeList = NamedNodeMap class TypeInfo: - namespace: Incomplete | None - name: str - def __init__(self, namespace: Incomplete | None, name: str) -> None: ... + __slots__ = ("namespace", "name") + namespace: str | None + name: str | None + def __init__(self, namespace: Incomplete | None, name: str | None) -> None: ... + +_ElementChildrenVar = TypeVar("_ElementChildrenVar", bound=_ElementChildren) +_ElementChildrenPlusFragment = TypeVar("_ElementChildrenPlusFragment", bound=_ElementChildren | DocumentFragment) class Element(Node): - nodeType: int - nodeValue: Incomplete - schemaType: Incomplete - parentNode: Incomplete - tagName: str - nodeName: str - prefix: Incomplete + __slots__ = ( + "ownerDocument", + "parentNode", + "tagName", + "nodeName", + "prefix", + "namespaceURI", + "_localName", + "childNodes", + "_attrs", + "_attrsNS", + "nextSibling", + "previousSibling", + ) + nodeType: ClassVar[Literal[1]] + nodeName: str # same as Element.tagName + nodeValue: None + @property + def attributes(self) -> NamedNodeMap: ... # type: ignore[override] + + parentNode: Document | Element | DocumentFragment | None + nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + childNodes: NodeList[_ElementChildren] + @property + def firstChild(self) -> _ElementChildren | None: ... + @property + def lastChild(self) -> _ElementChildren | None: ... + namespaceURI: str | None - childNodes: Incomplete - nextSibling: Incomplete + prefix: str | None + @property + def localName(self) -> str: ... + + schemaType: TypeInfo + tagName: str + def __init__( - self, tagName, namespaceURI: str | None = None, prefix: Incomplete | None = None, localName: Incomplete | None = None + self, tagName: str, namespaceURI: str | None = None, prefix: str | None = None, localName: str | None = None ) -> None: ... def unlink(self) -> None: ... def getAttribute(self, attname: str) -> str: ... - def getAttributeNS(self, namespaceURI: str, localName): ... + def getAttributeNS(self, namespaceURI: str | None, localName: str) -> str: ... def setAttribute(self, attname: str, value: str) -> None: ... - def setAttributeNS(self, namespaceURI: str, qualifiedName: str, value) -> None: ... - def getAttributeNode(self, attrname: str): ... - def getAttributeNodeNS(self, namespaceURI: str, localName): ... - def setAttributeNode(self, attr): ... - setAttributeNodeNS: Incomplete + def setAttributeNS(self, namespaceURI: str | None, qualifiedName: str, value: str) -> None: ... + def getAttributeNode(self, attrname: str) -> Attr | None: ... + def getAttributeNodeNS(self, namespaceURI: str | None, localName: str) -> Attr | None: ... + def setAttributeNode(self, attr: Attr) -> Attr | None: ... + setAttributeNodeNS = setAttributeNode def removeAttribute(self, name: str) -> None: ... - def removeAttributeNS(self, namespaceURI: str, localName) -> None: ... - def removeAttributeNode(self, node): ... - removeAttributeNodeNS: Incomplete + def removeAttributeNS(self, namespaceURI: str | None, localName: str) -> None: ... + def removeAttributeNode(self, node: Attr) -> Attr: ... + removeAttributeNodeNS = removeAttributeNode def hasAttribute(self, name: str) -> bool: ... - def hasAttributeNS(self, namespaceURI: str, localName) -> bool: ... + def hasAttributeNS(self, namespaceURI: str | None, localName: str) -> bool: ... def getElementsByTagName(self, name: str) -> NodeList[Element]: ... - def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ... + def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... def hasAttributes(self) -> bool: ... - def setIdAttribute(self, name) -> None: ... - def setIdAttributeNS(self, namespaceURI: str, localName) -> None: ... - def setIdAttributeNode(self, idAttr) -> None: ... - @property - def attributes(self) -> NamedNodeMap: ... + def setIdAttribute(self, name: str) -> None: ... + def setIdAttributeNS(self, namespaceURI: str | None, localName: str) -> None: ... + def setIdAttributeNode(self, idAttr: Attr) -> None: ... + def insertBefore( # type: ignore[override] + self, newChild: _ElementChildrenPlusFragment, refChild: _ElementChildren | None + ) -> _ElementChildrenPlusFragment: ... + def appendChild(self, node: _ElementChildrenPlusFragment) -> _ElementChildrenPlusFragment: ... # type: ignore[override] + @overload # type: ignore[override] + def replaceChild( + self, newChild: DocumentFragment, oldChild: _ElementChildrenVar + ) -> _ElementChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _ElementChildren, oldChild: _ElementChildrenVar) -> _ElementChildrenVar | None: ... # type: ignore[override] + def removeChild(self, oldChild: _ElementChildrenVar) -> _ElementChildrenVar: ... # type: ignore[override] class Childless: - attributes: Incomplete - childNodes: Incomplete - firstChild: Incomplete - lastChild: Incomplete - def appendChild(self, node) -> NoReturn: ... - def hasChildNodes(self) -> bool: ... - def insertBefore(self, newChild, refChild) -> NoReturn: ... - def removeChild(self, oldChild) -> NoReturn: ... + __slots__ = () + attributes: None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + def appendChild(self, node: _NodesThatAreChildren | DocumentFragment) -> NoReturn: ... + def hasChildNodes(self) -> Literal[False]: ... + def insertBefore( + self, newChild: _NodesThatAreChildren | DocumentFragment, refChild: _NodesThatAreChildren | None + ) -> NoReturn: ... + def removeChild(self, oldChild: _NodesThatAreChildren) -> NoReturn: ... def normalize(self) -> None: ... - def replaceChild(self, newChild, oldChild) -> NoReturn: ... + def replaceChild(self, newChild: _NodesThatAreChildren | DocumentFragment, oldChild: _NodesThatAreChildren) -> NoReturn: ... class ProcessingInstruction(Childless, Node): - nodeType: int - target: Incomplete - data: Incomplete - def __init__(self, target, data) -> None: ... - nodeValue: Incomplete - nodeName: Incomplete + __slots__ = ("target", "data") + nodeType: ClassVar[Literal[7]] + nodeName: str # same as ProcessingInstruction.target + nodeValue: str # same as ProcessingInstruction.data + attributes: None + + parentNode: Document | Element | DocumentFragment | None + nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + target: str + data: str + + def __init__(self, target: str, data: str) -> None: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class CharacterData(Childless, Node): - ownerDocument: Incomplete - previousSibling: Incomplete + __slots__ = ("_data", "ownerDocument", "parentNode", "previousSibling", "nextSibling") + nodeValue: str + attributes: None + + childNodes: EmptyNodeList + nextSibling: _NodesThatAreChildren | None + previousSibling: _NodesThatAreChildren | None + + @property + def localName(self) -> None: ... + + ownerDocument: Document | None + data: str + def __init__(self) -> None: ... + @property + def length(self) -> int: ... def __len__(self) -> int: ... - data: str - nodeValue: Incomplete def substringData(self, offset: int, count: int) -> str: ... def appendData(self, arg: str) -> None: ... def insertData(self, offset: int, arg: str) -> None: ... def deleteData(self, offset: int, count: int) -> None: ... def replaceData(self, offset: int, count: int, arg: str) -> None: ... - @property - def length(self) -> int: ... class Text(CharacterData): - nodeType: int - nodeName: str - attributes: Incomplete - data: Incomplete + __slots__ = () + nodeType: ClassVar[Literal[3]] + nodeName: Literal["#text"] + nodeValue: str # same as CharacterData.data, the content of the text node + attributes: None + + parentNode: Attr | Element | DocumentFragment | None + nextSibling: _DocumentFragmentChildren | _ElementChildren | _AttrChildren | None + previousSibling: _DocumentFragmentChildren | _ElementChildren | _AttrChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + data: str def splitText(self, offset: int) -> Self: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... - def replaceWholeText(self, content) -> Self | None: ... + def replaceWholeText(self, content: str) -> Self | None: ... @property def isWhitespaceInElementContent(self) -> bool: ... @property def wholeText(self) -> str: ... class Comment(CharacterData): - nodeType: int - nodeName: str - def __init__(self, data) -> None: ... + nodeType: ClassVar[Literal[8]] + nodeName: Literal["#comment"] + nodeValue: str # same as CharacterData.data, the content of the comment + attributes: None + + parentNode: Document | Element | DocumentFragment | None + nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + def __init__(self, data: str) -> None: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class CDATASection(Text): - nodeType: int - nodeName: str + __slots__ = () + nodeType: ClassVar[Literal[4]] # type: ignore[assignment] + nodeName: Literal["#cdata-section"] # type: ignore[assignment] + nodeValue: str # same as CharacterData.data, the content of the CDATA Section + attributes: None + + parentNode: Element | DocumentFragment | None + nextSibling: _DocumentFragmentChildren | _ElementChildren | None + previousSibling: _DocumentFragmentChildren | _ElementChildren | None + def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... -class ReadOnlySequentialNamedNodeMap: - def __init__(self, seq=()) -> None: ... +class ReadOnlySequentialNamedNodeMap(Generic[_N]): + __slots__ = ("_seq",) + def __init__(self, seq: Sequence[_N] = ()) -> None: ... def __len__(self) -> int: ... - def getNamedItem(self, name): ... - def getNamedItemNS(self, namespaceURI: str, localName): ... - def __getitem__(self, name_or_tuple): ... - def item(self, index): ... - def removeNamedItem(self, name) -> None: ... - def removeNamedItemNS(self, namespaceURI: str, localName) -> None: ... - def setNamedItem(self, node) -> None: ... - def setNamedItemNS(self, node) -> None: ... + def getNamedItem(self, name: str) -> _N | None: ... + def getNamedItemNS(self, namespaceURI: str | None, localName: str) -> _N | None: ... + def __getitem__(self, name_or_tuple: str | _NSName) -> _N | None: ... + def item(self, index: int) -> _N | None: ... + def removeNamedItem(self, name: str) -> NoReturn: ... + def removeNamedItemNS(self, namespaceURI: str | None, localName: str) -> NoReturn: ... + def setNamedItem(self, node: Node) -> NoReturn: ... + def setNamedItemNS(self, node: Node) -> NoReturn: ... @property def length(self) -> int: ... class Identified: - publicId: Incomplete - systemId: Incomplete + __slots__ = ("publicId", "systemId") + publicId: str | None + systemId: str | None class DocumentType(Identified, Childless, Node): - nodeType: int - nodeValue: Incomplete - name: Incomplete - internalSubset: Incomplete - entities: Incomplete - notations: Incomplete - nodeName: Incomplete - def __init__(self, qualifiedName: str) -> None: ... - def cloneNode(self, deep): ... + nodeType: ClassVar[Literal[10]] + nodeName: str | None # same as DocumentType.name + nodeValue: None + attributes: None + + parentNode: Document | None + nextSibling: _DocumentChildren | None + previousSibling: _DocumentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + name: str | None + internalSubset: str | None + entities: ReadOnlySequentialNamedNodeMap[Entity] + notations: ReadOnlySequentialNamedNodeMap[Notation] + + def __init__(self, qualifiedName: str | None) -> None: ... + def cloneNode(self, deep: bool) -> DocumentType | None: ... def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ... class Entity(Identified, Node): - attributes: Incomplete - nodeType: int - nodeValue: Incomplete - actualEncoding: Incomplete - encoding: Incomplete - version: Incomplete - nodeName: Incomplete - notationName: Incomplete - childNodes: Incomplete - def __init__(self, name, publicId, systemId, notation) -> None: ... - def appendChild(self, newChild) -> NoReturn: ... - def insertBefore(self, newChild, refChild) -> NoReturn: ... - def removeChild(self, oldChild) -> NoReturn: ... - def replaceChild(self, newChild, oldChild) -> NoReturn: ... + nodeType: ClassVar[Literal[6]] + nodeName: str # entity name + nodeValue: None + attributes: None + + parentNode: None + nextSibling: None + previousSibling: None + childNodes: NodeList[_EntityChildren] + @property + def firstChild(self) -> _EntityChildren | None: ... + @property + def lastChild(self) -> _EntityChildren | None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + actualEncoding: str | None + encoding: str | None + version: str | None + notationName: str | None + + def __init__(self, name: str, publicId: str | None, systemId: str | None, notation: str | None) -> None: ... + def appendChild(self, newChild: _EntityChildren) -> NoReturn: ... # type: ignore[override] + def insertBefore(self, newChild: _EntityChildren, refChild: _EntityChildren | None) -> NoReturn: ... # type: ignore[override] + def removeChild(self, oldChild: _EntityChildren) -> NoReturn: ... # type: ignore[override] + def replaceChild(self, newChild: _EntityChildren, oldChild: _EntityChildren) -> NoReturn: ... # type: ignore[override] class Notation(Identified, Childless, Node): - nodeType: int - nodeValue: Incomplete - nodeName: Incomplete - def __init__(self, name, publicId, systemId) -> None: ... + nodeType: ClassVar[Literal[12]] + nodeName: str # notation name + nodeValue: None + attributes: None + + parentNode: DocumentFragment | None + nextSibling: _DocumentFragmentChildren | None + previousSibling: _DocumentFragmentChildren | None + childNodes: EmptyNodeList + @property + def firstChild(self) -> None: ... + @property + def lastChild(self) -> None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + def __init__(self, name: str, publicId: str | None, systemId: str | None) -> None: ... class DOMImplementation(DOMImplementationLS): def hasFeature(self, feature: str, version: str | None) -> bool: ... def createDocument(self, namespaceURI: str | None, qualifiedName: str | None, doctype: DocumentType | None) -> Document: ... - def createDocumentType(self, qualifiedName: str | None, publicId: str, systemId: str) -> DocumentType: ... + def createDocumentType(self, qualifiedName: str | None, publicId: str | None, systemId: str | None) -> DocumentType: ... def getInterface(self, feature: str) -> Self | None: ... class ElementInfo: - tagName: Incomplete - def __init__(self, name) -> None: ... - def getAttributeType(self, aname): ... - def getAttributeTypeNS(self, namespaceURI: str, localName): ... - def isElementContent(self): ... - def isEmpty(self): ... - def isId(self, aname): ... - def isIdNS(self, namespaceURI: str, localName): ... + __slots__ = ("tagName",) + tagName: str + def __init__(self, name: str) -> None: ... + def getAttributeType(self, aname: str) -> TypeInfo: ... + def getAttributeTypeNS(self, namespaceURI: str | None, localName: str) -> TypeInfo: ... + def isElementContent(self) -> bool: ... + def isEmpty(self) -> bool: ... + def isId(self, aname: str) -> bool: ... + def isIdNS(self, namespaceURI: str | None, localName: str) -> bool: ... + +_DocumentChildrenPlusFragment = TypeVar("_DocumentChildrenPlusFragment", bound=_DocumentChildren | DocumentFragment) class Document(Node, DocumentLS): - implementation: Incomplete - nodeType: int - nodeName: str - nodeValue: Incomplete - attributes: Incomplete - parentNode: Incomplete - previousSibling: Incomplete - nextSibling: Incomplete - actualEncoding: Incomplete + __slots__ = ("_elem_info", "doctype", "_id_search_stack", "childNodes", "_id_cache") + nodeType: ClassVar[Literal[9]] + nodeName: Literal["#document"] + nodeValue: None + attributes: None + + parentNode: None + previousSibling: None + nextSibling: None + childNodes: NodeList[_DocumentChildren] + @property + def firstChild(self) -> _DocumentChildren | None: ... + @property + def lastChild(self) -> _DocumentChildren | None: ... + + namespaceURI: None + prefix: None + @property + def localName(self) -> None: ... + + implementation: DOMImplementation + actualEncoding: str | None encoding: str | None standalone: bool | None - version: Incomplete + version: str | None strictErrorChecking: bool - errorHandler: Incomplete - documentURI: Incomplete + errorHandler: _DOMErrorHandler | None + documentURI: str | None doctype: DocumentType | None - childNodes: Incomplete + documentElement: Element | None + def __init__(self) -> None: ... - def appendChild(self, node: _N) -> _N: ... - documentElement: Incomplete - def removeChild(self, oldChild): ... + def appendChild(self, node: _DocumentChildrenVar) -> _DocumentChildrenVar: ... # type: ignore[override] + def removeChild(self, oldChild: _DocumentChildrenVar) -> _DocumentChildrenVar: ... # type: ignore[override] def unlink(self) -> None: ... - def cloneNode(self, deep): ... + def cloneNode(self, deep: bool) -> Document | None: ... def createDocumentFragment(self) -> DocumentFragment: ... def createElement(self, tagName: str) -> Element: ... def createTextNode(self, data: str) -> Text: ... def createCDATASection(self, data: str) -> CDATASection: ... def createComment(self, data: str) -> Comment: ... - def createProcessingInstruction(self, target, data): ... - def createAttribute(self, qName) -> Attr: ... - def createElementNS(self, namespaceURI: str, qualifiedName: str): ... - def createAttributeNS(self, namespaceURI: str, qualifiedName: str) -> Attr: ... + def createProcessingInstruction(self, target: str, data: str) -> ProcessingInstruction: ... + def createAttribute(self, qName: str) -> Attr: ... + def createElementNS(self, namespaceURI: str | None, qualifiedName: str) -> Element: ... + def createAttributeNS(self, namespaceURI: str | None, qualifiedName: str) -> Attr: ... def getElementById(self, id: str) -> Element | None: ... def getElementsByTagName(self, name: str) -> NodeList[Element]: ... - def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ... + def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ... def isSupported(self, feature: str, version: str | None) -> bool: ... - def importNode(self, node, deep): ... - if sys.version_info >= (3, 9): - def writexml( - self, - writer: SupportsWrite[str], - indent: str = "", - addindent: str = "", - newl: str = "", - encoding: str | None = None, - standalone: bool | None = None, - ) -> None: ... - else: - def writexml( - self, - writer: SupportsWrite[str], - indent: str = "", - addindent: str = "", - newl: str = "", - encoding: Incomplete | None = None, - ) -> None: ... - - def renameNode(self, n, namespaceURI: str, name): ... + def importNode(self, node: _ImportableNodeVar, deep: bool) -> _ImportableNodeVar: ... + def writexml( + self, + writer: SupportsWrite[str], + indent: str = "", + addindent: str = "", + newl: str = "", + encoding: str | None = None, + standalone: bool | None = None, + ) -> None: ... + @overload + def renameNode(self, n: Element, namespaceURI: str, name: str) -> Element: ... + @overload + def renameNode(self, n: Attr, namespaceURI: str, name: str) -> Attr: ... + @overload + def renameNode(self, n: Element | Attr, namespaceURI: str, name: str) -> Element | Attr: ... + def insertBefore( + self, newChild: _DocumentChildrenPlusFragment, refChild: _DocumentChildren | None # type: ignore[override] + ) -> _DocumentChildrenPlusFragment: ... + @overload # type: ignore[override] + def replaceChild( + self, newChild: DocumentFragment, oldChild: _DocumentChildrenVar + ) -> _DocumentChildrenVar | DocumentFragment: ... + @overload + def replaceChild(self, newChild: _DocumentChildren, oldChild: _DocumentChildrenVar) -> _DocumentChildrenVar | None: ... diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi index 50250de5cb2f6..df7a3ad0eddb0 100644 --- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi @@ -1,11 +1,12 @@ import sys -from _typeshed import Incomplete, SupportsRead -from collections.abc import Sequence -from typing import Final, Literal -from typing_extensions import TypeAlias -from xml.dom.minidom import Document, DOMImplementation, Element, Text +from _typeshed import Incomplete, Unused +from collections.abc import MutableSequence, Sequence +from typing import Final, Literal, NoReturn +from typing_extensions import Self, TypeAlias +from xml.dom.minidom import Comment, Document, DOMImplementation, Element, ProcessingInstruction, Text +from xml.sax import _SupportsReadClose from xml.sax.handler import ContentHandler -from xml.sax.xmlreader import XMLReader +from xml.sax.xmlreader import AttributesImpl, AttributesNSImpl, Locator, XMLReader START_ELEMENT: Final = "START_ELEMENT" END_ELEMENT: Final = "END_ELEMENT" @@ -16,79 +17,93 @@ PROCESSING_INSTRUCTION: Final = "PROCESSING_INSTRUCTION" IGNORABLE_WHITESPACE: Final = "IGNORABLE_WHITESPACE" CHARACTERS: Final = "CHARACTERS" +_NSName: TypeAlias = tuple[str | None, str] _DocumentFactory: TypeAlias = DOMImplementation | None -_Node: TypeAlias = Document | Element | Text -_Event: TypeAlias = tuple[ - Literal[ - Literal["START_ELEMENT"], - Literal["END_ELEMENT"], - Literal["COMMENT"], - Literal["START_DOCUMENT"], - Literal["END_DOCUMENT"], - Literal["PROCESSING_INSTRUCTION"], - Literal["IGNORABLE_WHITESPACE"], - Literal["CHARACTERS"], - ], - _Node, -] +_Event: TypeAlias = ( + tuple[Literal["START_ELEMENT"], Element] + | tuple[Literal["END_ELEMENT"], Element] + | tuple[Literal["COMMENT"], Comment] + | tuple[Literal["START_DOCUMENT"], Document] + | tuple[Literal["END_DOCUMENT"], Document] + | tuple[Literal["PROCESSING_INSTRUCTION"], ProcessingInstruction] + | tuple[Literal["IGNORABLE_WHITESPACE"], Text] + | tuple[Literal["CHARACTERS"], Text] +) class PullDOM(ContentHandler): document: Document | None documentFactory: _DocumentFactory - firstEvent: Incomplete - lastEvent: Incomplete - elementStack: Sequence[Incomplete] - pending_events: Sequence[Incomplete] + + # firstEvent is a list of length 2 + # firstEvent[0] is always None + # firstEvent[1] is None prior to any events, after which it's a + # list of length 2, where the first item is of type _Event + # and the second item is None. + firstEvent: list[Incomplete] + + # lastEvent is also a list of length 2. The second item is always None, + # and the first item is of type _Event + # This is a slight lie: The second item is sometimes temporarily what was just + # described for the type of lastEvent, after which lastEvent is always updated + # with `self.lastEvent = self.lastEvent[1]`. + lastEvent: list[Incomplete] + + elementStack: MutableSequence[Element | Document] + pending_events: ( + list[Sequence[tuple[Literal["COMMENT"], str] | tuple[Literal["PROCESSING_INSTRUCTION"], str, str] | None]] | None + ) def __init__(self, documentFactory: _DocumentFactory = None) -> None: ... - def pop(self) -> Element: ... - def setDocumentLocator(self, locator) -> None: ... - def startPrefixMapping(self, prefix, uri) -> None: ... - def endPrefixMapping(self, prefix) -> None: ... - def startElementNS(self, name, tagName, attrs) -> None: ... - def endElementNS(self, name, tagName) -> None: ... - def startElement(self, name, attrs) -> None: ... - def endElement(self, name) -> None: ... - def comment(self, s) -> None: ... - def processingInstruction(self, target, data) -> None: ... - def ignorableWhitespace(self, chars) -> None: ... - def characters(self, chars) -> None: ... + def pop(self) -> Element | Document: ... + def setDocumentLocator(self, locator: Locator) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... + def startElementNS(self, name: _NSName, tagName: str | None, attrs: AttributesNSImpl) -> None: ... + def endElementNS(self, name: _NSName, tagName: str | None) -> None: ... + def startElement(self, name: str, attrs: AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def comment(self, s: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def ignorableWhitespace(self, chars: str) -> None: ... + def characters(self, chars: str) -> None: ... def startDocument(self) -> None: ... - def buildDocument(self, uri, tagname): ... + def buildDocument(self, uri: str | None, tagname: str | None) -> Element: ... def endDocument(self) -> None: ... def clear(self) -> None: ... class ErrorHandler: - def warning(self, exception) -> None: ... - def error(self, exception) -> None: ... - def fatalError(self, exception) -> None: ... + def warning(self, exception: BaseException) -> None: ... + def error(self, exception: BaseException) -> NoReturn: ... + def fatalError(self, exception: BaseException) -> NoReturn: ... class DOMEventStream: - stream: SupportsRead[bytes] | SupportsRead[str] - parser: XMLReader + stream: _SupportsReadClose[bytes] | _SupportsReadClose[str] + parser: XMLReader # Set to none after .clear() is called bufsize: int - def __init__(self, stream: SupportsRead[bytes] | SupportsRead[str], parser: XMLReader, bufsize: int) -> None: ... - pulldom: Incomplete + pulldom: PullDOM + def __init__(self, stream: _SupportsReadClose[bytes] | _SupportsReadClose[str], parser: XMLReader, bufsize: int) -> None: ... if sys.version_info < (3, 11): - def __getitem__(self, pos): ... + def __getitem__(self, pos: Unused) -> _Event: ... - def __next__(self): ... - def __iter__(self): ... - def getEvent(self) -> _Event: ... - def expandNode(self, node: _Node) -> None: ... + def __next__(self) -> _Event: ... + def __iter__(self) -> Self: ... + def getEvent(self) -> _Event | None: ... + def expandNode(self, node: Document) -> None: ... def reset(self) -> None: ... def clear(self) -> None: ... class SAX2DOM(PullDOM): - def startElementNS(self, name, tagName, attrs) -> None: ... - def startElement(self, name, attrs) -> None: ... - def processingInstruction(self, target, data) -> None: ... - def ignorableWhitespace(self, chars) -> None: ... - def characters(self, chars) -> None: ... + def startElementNS(self, name: _NSName, tagName: str | None, attrs: AttributesNSImpl) -> None: ... + def startElement(self, name: str, attrs: AttributesImpl) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def ignorableWhitespace(self, chars: str) -> None: ... + def characters(self, chars: str) -> None: ... -default_bufsize: int +default_bufsize: Final[int] def parse( - stream_or_string: str | SupportsRead[bytes] | SupportsRead[str], parser: XMLReader | None = None, bufsize: int | None = None + stream_or_string: str | _SupportsReadClose[bytes] | _SupportsReadClose[str], + parser: XMLReader | None = None, + bufsize: int | None = None, ) -> DOMEventStream: ... def parseString(string: str, parser: XMLReader | None = None) -> DOMEventStream: ... diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi index ab76d362e23f7..f19f7050b08df 100644 --- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi @@ -1,32 +1,9 @@ -from _typeshed import Incomplete, Unused -from typing import Any, Literal, NoReturn -from typing_extensions import TypeAlias -from urllib.request import OpenerDirector -from xml.dom.expatbuilder import ExpatBuilder, ExpatBuilderNS -from xml.dom.minidom import Node +from _typeshed import SupportsRead +from typing import Any, Final, Literal, NoReturn +from xml.dom.minidom import Document, Node, _DOMErrorHandler __all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"] -# UNKNOWN TYPES: -# - `Options.errorHandler`. -# The same as `_DOMBuilderErrorHandlerType`? -# Maybe `xml.sax.handler.ErrorHandler`? -# - Return type of DOMBuilder.getFeature(). -# We could get rid of the `Incomplete` if we knew more -# about `Options.errorHandler`. - -# ALIASES REPRESENTING MORE UNKNOWN TYPES: - -# probably the same as `Options.errorHandler`? -# Maybe `xml.sax.handler.ErrorHandler`? -_DOMBuilderErrorHandlerType: TypeAlias = Incomplete | None -# probably some kind of IO... -_DOMInputSourceCharacterStreamType: TypeAlias = Incomplete | None -# probably a string?? -_DOMInputSourceStringDataType: TypeAlias = Incomplete | None -# probably a string?? -_DOMInputSourceEncodingType: TypeAlias = Incomplete | None - class Options: namespaces: int namespace_declarations: bool @@ -45,64 +22,60 @@ class Options: charset_overrides_xml_encoding: bool infoset: bool supported_mediatypes_only: bool - errorHandler: Any | None - filter: DOMBuilderFilter | None # a guess, but seems likely + errorHandler: _DOMErrorHandler | None + filter: DOMBuilderFilter | None class DOMBuilder: - entityResolver: DOMEntityResolver | None # a guess, but seems likely - errorHandler: _DOMBuilderErrorHandlerType - filter: DOMBuilderFilter | None # a guess, but seems likely - ACTION_REPLACE: Literal[1] - ACTION_APPEND_AS_CHILDREN: Literal[2] - ACTION_INSERT_AFTER: Literal[3] - ACTION_INSERT_BEFORE: Literal[4] + entityResolver: DOMEntityResolver | None + errorHandler: _DOMErrorHandler | None + filter: DOMBuilderFilter | None + ACTION_REPLACE: Final = 1 + ACTION_APPEND_AS_CHILDREN: Final = 2 + ACTION_INSERT_AFTER: Final = 3 + ACTION_INSERT_BEFORE: Final = 4 + def __init__(self) -> None: ... def setFeature(self, name: str, state: int) -> None: ... def supportsFeature(self, name: str) -> bool: ... - def canSetFeature(self, name: str, state: int) -> bool: ... + def canSetFeature(self, name: str, state: Literal[1, 0]) -> bool: ... # getFeature could return any attribute from an instance of `Options` def getFeature(self, name: str) -> Any: ... - def parseURI(self, uri: str) -> ExpatBuilder | ExpatBuilderNS: ... - def parse(self, input: DOMInputSource) -> ExpatBuilder | ExpatBuilderNS: ... - # `input` and `cnode` argtypes for `parseWithContext` are unknowable - # as the function does nothing with them, and always raises an exception. - # But `input` is *probably* `DOMInputSource`? - def parseWithContext(self, input: Unused, cnode: Unused, action: Literal[1, 2, 3, 4]) -> NoReturn: ... + def parseURI(self, uri: str) -> Document: ... + def parse(self, input: DOMInputSource) -> Document: ... + def parseWithContext(self, input: DOMInputSource, cnode: Node, action: Literal[1, 2, 3, 4]) -> NoReturn: ... class DOMEntityResolver: + __slots__ = ("_opener",) def resolveEntity(self, publicId: str | None, systemId: str) -> DOMInputSource: ... class DOMInputSource: - byteStream: OpenerDirector | None - characterStream: _DOMInputSourceCharacterStreamType - stringData: _DOMInputSourceStringDataType - encoding: _DOMInputSourceEncodingType + __slots__ = ("byteStream", "characterStream", "stringData", "encoding", "publicId", "systemId", "baseURI") + byteStream: SupportsRead[bytes] | None + characterStream: SupportsRead[str] | None + stringData: str | None + encoding: str | None publicId: str | None systemId: str | None baseURI: str | None class DOMBuilderFilter: - FILTER_ACCEPT: Literal[1] - FILTER_REJECT: Literal[2] - FILTER_SKIP: Literal[3] - FILTER_INTERRUPT: Literal[4] + FILTER_ACCEPT: Final = 1 + FILTER_REJECT: Final = 2 + FILTER_SKIP: Final = 3 + FILTER_INTERRUPT: Final = 4 whatToShow: int - def acceptNode(self, element: Unused) -> Literal[1]: ... - def startContainer(self, element: Unused) -> Literal[1]: ... + def acceptNode(self, element: Node) -> Literal[1, 2, 3, 4]: ... + def startContainer(self, element: Node) -> Literal[1, 2, 3, 4]: ... class DocumentLS: async_: bool def abort(self) -> NoReturn: ... - # `load()` and `loadXML()` always raise exceptions - # so the argtypes of `uri` and `source` are unknowable. - # `source` is *probably* `DOMInputSource`? - # `uri` is *probably* a str? (see DOMBuilder.parseURI()) - def load(self, uri: Unused) -> NoReturn: ... - def loadXML(self, source: Unused) -> NoReturn: ... + def load(self, uri: str) -> NoReturn: ... + def loadXML(self, source: str) -> NoReturn: ... def saveXML(self, snode: Node | None) -> str: ... class DOMImplementationLS: - MODE_SYNCHRONOUS: Literal[1] - MODE_ASYNCHRONOUS: Literal[2] + MODE_SYNCHRONOUS: Final = 1 + MODE_ASYNCHRONOUS: Final = 2 def createDOMBuilder(self, mode: Literal[1], schemaType: None) -> DOMBuilder: ... def createDOMWriter(self) -> NoReturn: ... def createDOMInputSource(self) -> DOMInputSource: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi index 5a15772ec2a96..10784e7d40214 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi @@ -1,29 +1,27 @@ -import sys from _typeshed import FileDescriptorOrPath -from collections.abc import Callable -from typing import Final +from typing import Final, Literal, Protocol, overload, type_check_only from xml.etree.ElementTree import Element -XINCLUDE: Final[str] -XINCLUDE_INCLUDE: Final[str] -XINCLUDE_FALLBACK: Final[str] +@type_check_only +class _Loader(Protocol): + @overload + def __call__(self, href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ... + @overload + def __call__(self, href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ... -if sys.version_info >= (3, 9): - DEFAULT_MAX_INCLUSION_DEPTH: Final = 6 +XINCLUDE: Final = "{http://www.w3.org/2001/XInclude}" -class FatalIncludeError(SyntaxError): ... +XINCLUDE_INCLUDE: Final = "{http://www.w3.org/2001/XInclude}include" +XINCLUDE_FALLBACK: Final = "{http://www.w3.org/2001/XInclude}fallback" -def default_loader(href: FileDescriptorOrPath, parse: str, encoding: str | None = None) -> str | Element: ... +DEFAULT_MAX_INCLUSION_DEPTH: Final = 6 -# TODO: loader is of type default_loader ie it takes a callable that has the -# same signature as default_loader. But default_loader has a keyword argument -# Which can't be represented using Callable... -if sys.version_info >= (3, 9): - def include( - elem: Element, loader: Callable[..., str | Element] | None = None, base_url: str | None = None, max_depth: int | None = 6 - ) -> None: ... +class FatalIncludeError(SyntaxError): ... - class LimitedRecursiveIncludeError(FatalIncludeError): ... +@overload +def default_loader(href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ... +@overload +def default_loader(href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ... +def include(elem: Element, loader: _Loader | None = None, base_url: str | None = None, max_depth: int | None = 6) -> None: ... -else: - def include(elem: Element, loader: Callable[..., str | Element] | None = None) -> None: ... +class LimitedRecursiveIncludeError(FatalIncludeError): ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi index c3f6207ea2411..80f3c55c14899 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi @@ -1,34 +1,41 @@ -from collections.abc import Callable, Generator +from collections.abc import Callable, Generator, Iterable from re import Pattern -from typing import TypeVar +from typing import Any, Final, Literal, TypeVar, overload from typing_extensions import TypeAlias from xml.etree.ElementTree import Element -xpath_tokenizer_re: Pattern[str] +xpath_tokenizer_re: Final[Pattern[str]] _Token: TypeAlias = tuple[str, str] _Next: TypeAlias = Callable[[], _Token] -_Callback: TypeAlias = Callable[[_SelectorContext, list[Element]], Generator[Element, None, None]] +_Callback: TypeAlias = Callable[[_SelectorContext, Iterable[Element]], Generator[Element, None, None]] +_T = TypeVar("_T") def xpath_tokenizer(pattern: str, namespaces: dict[str, str] | None = None) -> Generator[_Token, None, None]: ... def get_parent_map(context: _SelectorContext) -> dict[Element, Element]: ... def prepare_child(next: _Next, token: _Token) -> _Callback: ... def prepare_star(next: _Next, token: _Token) -> _Callback: ... def prepare_self(next: _Next, token: _Token) -> _Callback: ... -def prepare_descendant(next: _Next, token: _Token) -> _Callback: ... +def prepare_descendant(next: _Next, token: _Token) -> _Callback | None: ... def prepare_parent(next: _Next, token: _Token) -> _Callback: ... -def prepare_predicate(next: _Next, token: _Token) -> _Callback: ... +def prepare_predicate(next: _Next, token: _Token) -> _Callback | None: ... -ops: dict[str, Callable[[_Next, _Token], _Callback]] +ops: Final[dict[str, Callable[[_Next, _Token], _Callback | None]]] class _SelectorContext: parent_map: dict[Element, Element] | None root: Element def __init__(self, root: Element) -> None: ... -_T = TypeVar("_T") - -def iterfind(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... -def find(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... -def findall(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... -def findtext(elem: Element, path: str, default: _T | None = None, namespaces: dict[str, str] | None = None) -> _T | str: ... +@overload +def iterfind( # type: ignore[overload-overlap] + elem: Element[Any], path: Literal[""], namespaces: dict[str, str] | None = None +) -> None: ... +@overload +def iterfind(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... +def find(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... +def findall(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... +@overload +def findtext(elem: Element[Any], path: str, default: None = None, namespaces: dict[str, str] | None = None) -> str | None: ... +@overload +def findtext(elem: Element[Any], path: str, default: _T, namespaces: dict[str, str] | None = None) -> _T | str: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 64ebbd3ee63f2..d42db1bc0c571 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -2,8 +2,9 @@ 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, Final, Literal, SupportsIndex, TypeVar, overload -from typing_extensions import TypeAlias, TypeGuard, deprecated +from typing import Any, Final, Generic, Literal, Protocol, SupportsIndex, TypeVar, overload, type_check_only +from typing_extensions import TypeAlias, TypeGuard, deprecated, disjoint_base +from xml.parsers.expat import XMLParserType __all__ = [ "C14NWriterTarget", @@ -14,6 +15,7 @@ __all__ = [ "canonicalize", "fromstring", "fromstringlist", + "indent", "iselement", "iterparse", "parse", @@ -33,9 +35,6 @@ __all__ = [ "register_namespace", ] -if sys.version_info >= (3, 9): - __all__ += ["indent"] - _T = TypeVar("_T") _FileRead: TypeAlias = FileDescriptorOrPath | SupportsRead[bytes] | SupportsRead[str] _FileWriteC14N: TypeAlias = FileDescriptorOrPath | SupportsWrite[bytes] @@ -78,15 +77,23 @@ def canonicalize( exclude_tags: Iterable[str] | None = None, ) -> None: ... -class Element: - tag: str +# The tag for Element can be set to the Comment or ProcessingInstruction +# functions defined in this module. +_ElementCallable: TypeAlias = Callable[..., Element[_ElementCallable]] + +_Tag = TypeVar("_Tag", default=str, bound=str | _ElementCallable) +_OtherTag = TypeVar("_OtherTag", default=str, bound=str | _ElementCallable) + +@disjoint_base +class Element(Generic[_Tag]): + tag: _Tag attrib: dict[str, str] text: str | None tail: str | None - def __init__(self, tag: str, attrib: dict[str, str] = ..., **extra: str) -> None: ... - def append(self, subelement: Element, /) -> None: ... + def __init__(self, tag: _Tag, attrib: dict[str, str] = {}, **extra: str) -> None: ... + def append(self, subelement: Element[Any], /) -> None: ... def clear(self) -> None: ... - def extend(self, elements: Iterable[Element], /) -> None: ... + def extend(self, elements: Iterable[Element[Any]], /) -> None: ... def find(self, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... def findall(self, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... @overload @@ -97,17 +104,20 @@ class Element: def get(self, key: str, default: None = None) -> str | None: ... @overload def get(self, key: str, default: _T) -> str | _T: ... - def insert(self, index: int, subelement: Element, /) -> None: ... + def insert(self, index: int, subelement: Element[Any], /) -> None: ... def items(self) -> ItemsView[str, str]: ... def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ... + @overload + def iterfind(self, path: Literal[""], namespaces: dict[str, str] | None = None) -> None: ... # type: ignore[overload-overlap] + @overload def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... def itertext(self) -> Generator[str, None, None]: ... def keys(self) -> dict_keys[str, str]: ... # makeelement returns the type of self in Python impl, but not in C impl - def makeelement(self, tag: str, attrib: dict[str, str], /) -> Element: ... - def remove(self, subelement: Element, /) -> None: ... + def makeelement(self, tag: _OtherTag, attrib: dict[str, str], /) -> Element[_OtherTag]: ... + def remove(self, subelement: Element[Any], /) -> None: ... def set(self, key: str, value: str, /) -> None: ... - def __copy__(self) -> Element: ... # returns the type of self in Python impl, but not in C impl + def __copy__(self) -> Element[_Tag]: ... # returns the type of self in Python impl, but not in C impl def __deepcopy__(self, memo: Any, /) -> Element: ... # Only exists in C impl def __delitem__(self, key: SupportsIndex | slice, /) -> None: ... @overload @@ -118,20 +128,17 @@ class Element: # Doesn't actually exist at runtime, but instance of the class are indeed iterable due to __getitem__. def __iter__(self) -> Iterator[Element]: ... @overload - def __setitem__(self, key: SupportsIndex, value: Element, /) -> None: ... + def __setitem__(self, key: SupportsIndex, value: Element[Any], /) -> None: ... @overload - def __setitem__(self, key: slice, value: Iterable[Element], /) -> None: ... + def __setitem__(self, key: slice, value: Iterable[Element[Any]], /) -> None: ... # Doesn't really exist in earlier versions, where __len__ is called implicitly instead @deprecated("Testing an element's truth value is deprecated.") def __bool__(self) -> bool: ... - if sys.version_info < (3, 9): - def getchildren(self) -> list[Element]: ... - def getiterator(self, tag: str | None = None) -> list[Element]: ... -def SubElement(parent: Element, tag: str, attrib: dict[str, str] = ..., **extra: str) -> Element: ... -def Comment(text: str | None = None) -> Element: ... -def ProcessingInstruction(target: str, text: str | None = None) -> Element: ... +def SubElement(parent: Element[Any], tag: str, attrib: dict[str, str] = ..., **extra: str) -> Element: ... +def Comment(text: str | None = None) -> Element[_ElementCallable]: ... +def ProcessingInstruction(target: str, text: str | None = None) -> Element[_ElementCallable]: ... PI = ProcessingInstruction @@ -145,20 +152,22 @@ class QName: def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... -class ElementTree: - def __init__(self, element: Element | None = None, file: _FileRead | None = None) -> None: ... - def getroot(self) -> Element | Any: ... +_Root = TypeVar("_Root", Element, Element | None, default=Element | None) + +class ElementTree(Generic[_Root]): + def __init__(self, element: Element[Any] | None = None, file: _FileRead | None = None) -> None: ... + def getroot(self) -> _Root: ... def parse(self, source: _FileRead, parser: XMLParser | None = None) -> Element: ... def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ... - if sys.version_info < (3, 9): - def getiterator(self, tag: str | None = None) -> list[Element]: ... - def find(self, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ... @overload def findtext(self, path: str, default: None = None, namespaces: dict[str, str] | None = None) -> str | None: ... @overload def findtext(self, path: str, default: _T, namespaces: dict[str, str] | None = None) -> _T | str: ... def findall(self, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ... + @overload + def iterfind(self, path: Literal[""], namespaces: dict[str, str] | None = None) -> None: ... # type: ignore[overload-overlap] + @overload def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ... def write( self, @@ -166,18 +175,20 @@ class ElementTree: encoding: str | None = None, xml_declaration: bool | None = None, default_namespace: str | None = None, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, short_empty_elements: bool = True, ) -> None: ... def write_c14n(self, file: _FileWriteC14N) -> None: ... +HTML_EMPTY: Final[set[str]] + def register_namespace(prefix: str, uri: str) -> None: ... @overload def tostring( - element: Element, + element: Element[Any], encoding: None = None, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -185,9 +196,9 @@ def tostring( ) -> bytes: ... @overload def tostring( - element: Element, + element: Element[Any], encoding: Literal["unicode"], - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -195,9 +206,9 @@ def tostring( ) -> str: ... @overload def tostring( - element: Element, + element: Element[Any], encoding: str, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -205,9 +216,9 @@ def tostring( ) -> Any: ... @overload def tostringlist( - element: Element, + element: Element[Any], encoding: None = None, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -215,9 +226,9 @@ def tostringlist( ) -> list[bytes]: ... @overload def tostringlist( - element: Element, + element: Element[Any], encoding: Literal["unicode"], - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, @@ -225,23 +236,22 @@ def tostringlist( ) -> list[str]: ... @overload def tostringlist( - element: Element, + element: Element[Any], encoding: str, - method: str | None = None, + method: Literal["xml", "html", "text", "c14n"] | None = None, *, xml_declaration: bool | None = None, default_namespace: str | None = None, short_empty_elements: bool = True, ) -> list[Any]: ... -def dump(elem: Element) -> None: ... - -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 dump(elem: Element[Any] | ElementTree[Any]) -> None: ... +def indent(tree: Element[Any] | ElementTree[Any], space: str = " ", level: int = 0) -> None: ... +def parse(source: _FileRead, parser: XMLParser[Any] | None = None) -> ElementTree[Element]: ... -class _IterParseIterator(Iterator[tuple[str, Any]]): - def __next__(self) -> tuple[str, Any]: ... +# This class is defined inside the body of iterparse +@type_check_only +class _IterParseIterator(Iterator[tuple[str, Element]], Protocol): + def __next__(self) -> tuple[str, Element]: ... if sys.version_info >= (3, 13): def close(self) -> None: ... if sys.version_info >= (3, 11): @@ -249,13 +259,13 @@ class _IterParseIterator(Iterator[tuple[str, Any]]): 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: ... +_EventQueue: TypeAlias = tuple[str] | tuple[str, tuple[str, str]] | tuple[str, None] + +class XMLPullParser(Generic[_E]): + def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser[_E] | None = None) -> None: ... def feed(self, data: str | ReadableBuffer) -> None: ... def close(self) -> None: ... - # Second element in the tuple could be `Element`, `tuple[str, str]` or `None`. - # Use `Any` to avoid false-positive errors. - def read_events(self) -> Iterator[tuple[str, Any]]: ... + def read_events(self) -> Iterator[_EventQueue | tuple[str, _E]]: ... def flush(self) -> None: ... def XML(text: str | ReadableBuffer, parser: XMLParser | None = None) -> Element: ... @@ -277,16 +287,17 @@ def fromstringlist(sequence: Sequence[str | ReadableBuffer], parser: XMLParser | # elementfactories. _ElementFactory: TypeAlias = Callable[[Any, dict[Any, Any]], Element] +@disjoint_base class TreeBuilder: # comment_factory can take None because passing None to Comment is not an error def __init__( self, - element_factory: _ElementFactory | None = ..., + element_factory: _ElementFactory | None = None, *, - comment_factory: Callable[[str | None], Element] | None = ..., - pi_factory: Callable[[str, str | None], Element] | None = ..., - insert_comments: bool = ..., - insert_pis: bool = ..., + comment_factory: Callable[[str | None], Element[Any]] | None = None, + pi_factory: Callable[[str, str | None], Element[Any]] | None = None, + insert_comments: bool = False, + insert_pis: bool = False, ) -> None: ... insert_comments: bool insert_pis: bool @@ -298,8 +309,8 @@ class TreeBuilder: def start(self, tag: Any, attrs: dict[Any, Any], /) -> Element: ... def end(self, tag: str, /) -> Element: ... # These two methods have pos-only parameters in the C implementation - def comment(self, text: str | None, /) -> Element: ... - def pi(self, target: str, text: str | None = None, /) -> Element: ... + def comment(self, text: str | None, /) -> Element[Any]: ... + def pi(self, target: str, text: str | None = None, /) -> Element[Any]: ... class C14NWriterTarget: def __init__( @@ -321,13 +332,35 @@ class C14NWriterTarget: def comment(self, text: str) -> None: ... def pi(self, target: str, data: str) -> None: ... -class XMLParser: - parser: Any - target: Any - # TODO-what is entity used for??? - entity: Any +# The target type is tricky, because the implementation doesn't +# require any particular attribute to be present. This documents the attributes +# that can be present, but uncommenting any of them would require them. +@type_check_only +class _Target(Protocol): + # start: Callable[str, dict[str, str], Any] | None + # end: Callable[[str], Any] | None + # start_ns: Callable[[str, str], Any] | None + # end_ns: Callable[[str], Any] | None + # data: Callable[[str], Any] | None + # comment: Callable[[str], Any] + # pi: Callable[[str, str], Any] | None + # close: Callable[[], Any] | None + ... + +_E = TypeVar("_E", default=Element) + +# This is generic because the return type of close() depends on the target. +# The default target is TreeBuilder, which returns Element. +# C14NWriterTarget does not implement a close method, so using it results +# in a type of XMLParser[None]. +@disjoint_base +class XMLParser(Generic[_E]): + parser: XMLParserType + target: _Target + # TODO: what is entity used for??? + entity: dict[str, str] version: str - def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ... - def close(self) -> Any: ... + def __init__(self, *, target: _Target | None = None, encoding: str | None = None) -> None: ... + def close(self) -> _E: ... def feed(self, data: str | ReadableBuffer, /) -> None: ... def flush(self) -> 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/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi index a2eecc5a78641..679466fa34d2c 100644 --- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi @@ -1,6 +1,7 @@ +import sys from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co from collections.abc import Iterable -from typing import Protocol +from typing import Final, Protocol, type_check_only from typing_extensions import TypeAlias from xml.sax._exceptions import ( SAXException as SAXException, @@ -10,16 +11,33 @@ from xml.sax._exceptions import ( SAXReaderNotAvailable as SAXReaderNotAvailable, ) from xml.sax.handler import ContentHandler as ContentHandler, ErrorHandler as ErrorHandler -from xml.sax.xmlreader import XMLReader +from xml.sax.xmlreader import InputSource as InputSource, XMLReader +@type_check_only class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]): def close(self) -> None: ... _Source: TypeAlias = StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str] -default_parser_list: list[str] +default_parser_list: Final[list[str]] def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ... def parse(source: _Source, handler: ContentHandler, errorHandler: ErrorHandler = ...) -> None: ... def parseString(string: ReadableBuffer | str, handler: ContentHandler, errorHandler: ErrorHandler | None = ...) -> None: ... def _create_parser(parser_name: str) -> XMLReader: ... + +if sys.version_info >= (3, 14): + __all__ = [ + "ContentHandler", + "ErrorHandler", + "InputSource", + "SAXException", + "SAXNotRecognizedException", + "SAXNotSupportedException", + "SAXParseException", + "SAXReaderNotAvailable", + "default_parser_list", + "make_parser", + "parse", + "parseString", + ] diff --git a/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi b/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi index 8a437a971f13b..e9cc8856a9c8d 100644 --- a/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi +++ b/mypy/typeshed/stdlib/xml/sax/_exceptions.pyi @@ -4,15 +4,15 @@ from xml.sax.xmlreader import Locator class SAXException(Exception): def __init__(self, msg: str, exception: Exception | None = None) -> None: ... def getMessage(self) -> str: ... - def getException(self) -> Exception: ... + def getException(self) -> Exception | None: ... def __getitem__(self, ix: object) -> NoReturn: ... class SAXParseException(SAXException): def __init__(self, msg: str, exception: Exception | None, locator: Locator) -> None: ... - def getColumnNumber(self) -> int: ... - def getLineNumber(self) -> int: ... - def getPublicId(self): ... - def getSystemId(self): ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int | None: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... class SAXNotRecognizedException(SAXException): ... class SAXNotSupportedException(SAXException): ... diff --git a/mypy/typeshed/stdlib/xml/sax/expatreader.pyi b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi new file mode 100644 index 0000000000000..3f9573a25f9aa --- /dev/null +++ b/mypy/typeshed/stdlib/xml/sax/expatreader.pyi @@ -0,0 +1,78 @@ +import sys +from _typeshed import ReadableBuffer +from collections.abc import Mapping +from typing import Any, Final, Literal, overload +from typing_extensions import TypeAlias +from xml.sax import _Source, xmlreader +from xml.sax.handler import _ContentHandlerProtocol + +if sys.version_info >= (3, 10): + from xml.sax.handler import LexicalHandler + +_BoolType: TypeAlias = Literal[0, 1] | bool + +version: Final[str] +AttributesImpl = xmlreader.AttributesImpl +AttributesNSImpl = xmlreader.AttributesNSImpl + +class _ClosedParser: + ErrorColumnNumber: int + ErrorLineNumber: int + +class ExpatLocator(xmlreader.Locator): + def __init__(self, parser: ExpatParser) -> None: ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... + +class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator): + def __init__(self, namespaceHandling: _BoolType = 0, bufsize: int = 65516) -> None: ... + def parse(self, source: xmlreader.InputSource | _Source) -> None: ... + def prepareParser(self, source: xmlreader.InputSource) -> None: ... + def setContentHandler(self, handler: _ContentHandlerProtocol) -> None: ... + def getFeature(self, name: str) -> _BoolType: ... + def setFeature(self, name: str, state: _BoolType) -> None: ... + if sys.version_info >= (3, 10): + @overload + def getProperty(self, name: Literal["http://xml.org/sax/properties/lexical-handler"]) -> LexicalHandler | None: ... + + @overload + def getProperty(self, name: Literal["http://www.python.org/sax/properties/interning-dict"]) -> dict[str, Any] | None: ... + @overload + def getProperty(self, name: Literal["http://xml.org/sax/properties/xml-string"]) -> bytes | None: ... + @overload + def getProperty(self, name: str) -> object: ... + if sys.version_info >= (3, 10): + @overload + def setProperty(self, name: Literal["http://xml.org/sax/properties/lexical-handler"], value: LexicalHandler) -> None: ... + + @overload + def setProperty( + self, name: Literal["http://www.python.org/sax/properties/interning-dict"], value: dict[str, Any] + ) -> None: ... + @overload + def setProperty(self, name: str, value: object) -> None: ... + def feed(self, data: str | ReadableBuffer, isFinal: bool = False) -> None: ... + def flush(self) -> None: ... + def close(self) -> None: ... + def reset(self) -> None: ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... + def start_element(self, name: str, attrs: Mapping[str, str]) -> None: ... + def end_element(self, name: str) -> None: ... + def start_element_ns(self, name: str, attrs: Mapping[str, str]) -> None: ... + def end_element_ns(self, name: str) -> None: ... + def processing_instruction(self, target: str, data: str) -> None: ... + def character_data(self, data: str) -> None: ... + def start_namespace_decl(self, prefix: str | None, uri: str) -> None: ... + def end_namespace_decl(self, prefix: str | None) -> None: ... + def start_doctype_decl(self, name: str, sysid: str | None, pubid: str | None, has_internal_subset: bool) -> None: ... + def unparsed_entity_decl(self, name: str, base: str | None, sysid: str, pubid: str | None, notation_name: str) -> None: ... + def notation_decl(self, name: str, base: str | None, sysid: str, pubid: str | None) -> None: ... + def external_entity_ref(self, context: str, base: str | None, sysid: str, pubid: str | None) -> int: ... + def skipped_entity_handler(self, name: str, is_pe: bool) -> None: ... + +def create_parser(namespaceHandling: int = 0, bufsize: int = 65516) -> ExpatParser: ... diff --git a/mypy/typeshed/stdlib/xml/sax/handler.pyi b/mypy/typeshed/stdlib/xml/sax/handler.pyi index 7b7c69048efd1..5ecbfa6f1272c 100644 --- a/mypy/typeshed/stdlib/xml/sax/handler.pyi +++ b/mypy/typeshed/stdlib/xml/sax/handler.pyi @@ -1,14 +1,36 @@ import sys -from typing import NoReturn +from typing import Final, NoReturn, Protocol, type_check_only from xml.sax import xmlreader -version: str +version: Final[str] + +@type_check_only +class _ErrorHandlerProtocol(Protocol): # noqa: Y046 # Protocol is not used + def error(self, exception: BaseException) -> NoReturn: ... + def fatalError(self, exception: BaseException) -> NoReturn: ... + def warning(self, exception: BaseException) -> None: ... class ErrorHandler: def error(self, exception: BaseException) -> NoReturn: ... def fatalError(self, exception: BaseException) -> NoReturn: ... def warning(self, exception: BaseException) -> None: ... +@type_check_only +class _ContentHandlerProtocol(Protocol): # noqa: Y046 # Protocol is not used + def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... + def startDocument(self) -> None: ... + def endDocument(self) -> None: ... + def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... + def endPrefixMapping(self, prefix: str | None) -> None: ... + def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... + def endElement(self, name: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... + def characters(self, content: str) -> None: ... + def ignorableWhitespace(self, whitespace: str) -> None: ... + def processingInstruction(self, target: str, data: str) -> None: ... + def skippedEntity(self, name: str) -> None: ... + class ContentHandler: def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... def startDocument(self) -> None: ... @@ -17,39 +39,48 @@ class ContentHandler: def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... - def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... - def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... def characters(self, content: str) -> None: ... def ignorableWhitespace(self, whitespace: str) -> None: ... def processingInstruction(self, target: str, data: str) -> None: ... def skippedEntity(self, name: str) -> None: ... +@type_check_only +class _DTDHandlerProtocol(Protocol): # noqa: Y046 # Protocol is not used + def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ... + def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ... + class DTDHandler: - def notationDecl(self, name, publicId, systemId): ... - def unparsedEntityDecl(self, name, publicId, systemId, ndata): ... + def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ... + def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ... + +@type_check_only +class _EntityResolverProtocol(Protocol): # noqa: Y046 # Protocol is not used + def resolveEntity(self, publicId: str | None, systemId: str) -> str: ... class EntityResolver: - def resolveEntity(self, publicId, systemId): ... - -feature_namespaces: str -feature_namespace_prefixes: str -feature_string_interning: str -feature_validation: str -feature_external_ges: str -feature_external_pes: str -all_features: list[str] -property_lexical_handler: str -property_declaration_handler: str -property_dom_node: str -property_xml_string: str -property_encoding: str -property_interning_dict: str -all_properties: list[str] + def resolveEntity(self, publicId: str | None, systemId: str) -> str: ... + +feature_namespaces: Final = "http://xml.org/sax/features/namespaces" +feature_namespace_prefixes: Final = "http://xml.org/sax/features/namespace-prefixes" +feature_string_interning: Final = "http://xml.org/sax/features/string-interning" +feature_validation: Final = "http://xml.org/sax/features/validation" +feature_external_ges: Final[str] # too long string +feature_external_pes: Final[str] # too long string +all_features: Final[list[str]] +property_lexical_handler: Final = "http://xml.org/sax/properties/lexical-handler" +property_declaration_handler: Final = "http://xml.org/sax/properties/declaration-handler" +property_dom_node: Final = "http://xml.org/sax/properties/dom-node" +property_xml_string: Final = "http://xml.org/sax/properties/xml-string" +property_encoding: Final = "http://www.python.org/sax/properties/encoding" +property_interning_dict: Final[str] # too long string +all_properties: Final[list[str]] if sys.version_info >= (3, 10): class LexicalHandler: - def comment(self, content: str) -> object: ... - def startDTD(self, name: str, public_id: str | None, system_id: str | None) -> object: ... - def endDTD(self) -> object: ... - def startCDATA(self) -> object: ... - def endCDATA(self) -> object: ... + def comment(self, content: str) -> None: ... + def startDTD(self, name: str, public_id: str | None, system_id: str | None) -> None: ... + def endDTD(self) -> None: ... + def startCDATA(self) -> None: ... + def endCDATA(self) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi index 528f359639471..a29588faae2ae 100644 --- a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi +++ b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi @@ -2,6 +2,7 @@ from _typeshed import SupportsWrite from codecs import StreamReaderWriter, StreamWriter from collections.abc import Mapping from io import RawIOBase, TextIOBase +from typing import Literal, NoReturn from xml.sax import _Source, handler, xmlreader def escape(data: str, entities: Mapping[str, str] = {}) -> str: ... @@ -15,23 +16,26 @@ class XMLGenerator(handler.ContentHandler): encoding: str = "iso-8859-1", short_empty_elements: bool = False, ) -> None: ... + def _qname(self, name: tuple[str | None, str]) -> str: ... def startDocument(self) -> None: ... def endDocument(self) -> None: ... def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ... def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... - def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... - def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... def characters(self, content: str) -> None: ... def ignorableWhitespace(self, content: str) -> None: ... def processingInstruction(self, target: str, data: str) -> None: ... class XMLFilterBase(xmlreader.XMLReader): def __init__(self, parent: xmlreader.XMLReader | None = None) -> None: ... - def error(self, exception): ... - def fatalError(self, exception): ... - def warning(self, exception): ... + # ErrorHandler methods + def error(self, exception: BaseException) -> NoReturn: ... + def fatalError(self, exception: BaseException) -> NoReturn: ... + def warning(self, exception: BaseException) -> None: ... + # ContentHandler methods def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ... def startDocument(self) -> None: ... def endDocument(self) -> None: ... @@ -39,22 +43,26 @@ class XMLFilterBase(xmlreader.XMLReader): def endPrefixMapping(self, prefix: str | None) -> None: ... def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... - def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... - def endElementNS(self, name: tuple[str, str], qname: str) -> None: ... + def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ... def characters(self, content: str) -> None: ... def ignorableWhitespace(self, chars: str) -> None: ... def processingInstruction(self, target: str, data: str) -> None: ... def skippedEntity(self, name: str) -> None: ... - def notationDecl(self, name, publicId, systemId): ... - def unparsedEntityDecl(self, name, publicId, systemId, ndata): ... - def resolveEntity(self, publicId, systemId): ... - def parse(self, source: _Source) -> None: ... - def setLocale(self, locale): ... - def getFeature(self, name: str) -> object: ... - def setFeature(self, name: str, state: object) -> None: ... + # DTDHandler methods + def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ... + def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ... + # EntityResolver methods + def resolveEntity(self, publicId: str | None, systemId: str) -> str: ... + # XMLReader methods + def parse(self, source: xmlreader.InputSource | _Source) -> None: ... + def setLocale(self, locale: str) -> None: ... + def getFeature(self, name: str) -> Literal[1, 0] | bool: ... + def setFeature(self, name: str, state: Literal[1, 0] | bool) -> None: ... def getProperty(self, name: str) -> object: ... def setProperty(self, name: str, value: object) -> None: ... - def getParent(self) -> xmlreader.XMLReader: ... + # XMLFilter methods + def getParent(self) -> xmlreader.XMLReader | None: ... def setParent(self, parent: xmlreader.XMLReader) -> None: ... -def prepare_input_source(source, base=""): ... +def prepare_input_source(source: xmlreader.InputSource | _Source, base: str = "") -> xmlreader.InputSource: ... diff --git a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi index 2ccbc95bbef02..e7d04ddeadb80 100644 --- a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi +++ b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi @@ -1,87 +1,90 @@ +from _typeshed import ReadableBuffer from collections.abc import Mapping -from typing import overload +from typing import Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias -from xml.sax.handler import ContentHandler, DTDHandler, EntityResolver, ErrorHandler +from xml.sax import _Source, _SupportsReadClose +from xml.sax.handler import _ContentHandlerProtocol, _DTDHandlerProtocol, _EntityResolverProtocol, _ErrorHandlerProtocol class XMLReader: - def parse(self, source): ... - def getContentHandler(self) -> ContentHandler: ... - def setContentHandler(self, handler: ContentHandler) -> None: ... - def getDTDHandler(self) -> DTDHandler: ... - def setDTDHandler(self, handler: DTDHandler) -> None: ... - def getEntityResolver(self) -> EntityResolver: ... - def setEntityResolver(self, resolver: EntityResolver) -> None: ... - def getErrorHandler(self) -> ErrorHandler: ... - def setErrorHandler(self, handler: ErrorHandler) -> None: ... - def setLocale(self, locale): ... - def getFeature(self, name: str) -> object: ... - def setFeature(self, name: str, state: object) -> None: ... + def parse(self, source: InputSource | _Source) -> None: ... + def getContentHandler(self) -> _ContentHandlerProtocol: ... + def setContentHandler(self, handler: _ContentHandlerProtocol) -> None: ... + def getDTDHandler(self) -> _DTDHandlerProtocol: ... + def setDTDHandler(self, handler: _DTDHandlerProtocol) -> None: ... + def getEntityResolver(self) -> _EntityResolverProtocol: ... + def setEntityResolver(self, resolver: _EntityResolverProtocol) -> None: ... + def getErrorHandler(self) -> _ErrorHandlerProtocol: ... + def setErrorHandler(self, handler: _ErrorHandlerProtocol) -> None: ... + def setLocale(self, locale: str) -> None: ... + def getFeature(self, name: str) -> Literal[0, 1] | bool: ... + def setFeature(self, name: str, state: Literal[0, 1] | bool) -> None: ... def getProperty(self, name: str) -> object: ... def setProperty(self, name: str, value: object) -> None: ... class IncrementalParser(XMLReader): def __init__(self, bufsize: int = 65536) -> None: ... - def parse(self, source): ... - def feed(self, data): ... - def prepareParser(self, source): ... - def close(self): ... - def reset(self): ... + def parse(self, source: InputSource | _Source) -> None: ... + def feed(self, data: str | ReadableBuffer) -> None: ... + def prepareParser(self, source: InputSource) -> None: ... + def close(self) -> None: ... + def reset(self) -> None: ... class Locator: - def getColumnNumber(self): ... - def getLineNumber(self): ... - def getPublicId(self): ... - def getSystemId(self): ... + def getColumnNumber(self) -> int | None: ... + def getLineNumber(self) -> int | None: ... + def getPublicId(self) -> str | None: ... + def getSystemId(self) -> str | None: ... class InputSource: def __init__(self, system_id: str | None = None) -> None: ... - def setPublicId(self, public_id): ... - def getPublicId(self): ... - def setSystemId(self, system_id): ... - def getSystemId(self): ... - def setEncoding(self, encoding): ... - def getEncoding(self): ... - def setByteStream(self, bytefile): ... - def getByteStream(self): ... - def setCharacterStream(self, charfile): ... - def getCharacterStream(self): ... + def setPublicId(self, public_id: str | None) -> None: ... + def getPublicId(self) -> str | None: ... + def setSystemId(self, system_id: str | None) -> None: ... + def getSystemId(self) -> str | None: ... + def setEncoding(self, encoding: str | None) -> None: ... + def getEncoding(self) -> str | None: ... + def setByteStream(self, bytefile: _SupportsReadClose[bytes] | None) -> None: ... + def getByteStream(self) -> _SupportsReadClose[bytes] | None: ... + def setCharacterStream(self, charfile: _SupportsReadClose[str] | None) -> None: ... + def getCharacterStream(self) -> _SupportsReadClose[str] | None: ... -class AttributesImpl: - def __init__(self, attrs: Mapping[str, str]) -> None: ... +_AttrKey = TypeVar("_AttrKey", default=str) + +class AttributesImpl(Generic[_AttrKey]): + def __init__(self, attrs: Mapping[_AttrKey, str]) -> None: ... def getLength(self) -> int: ... def getType(self, name: str) -> str: ... - def getValue(self, name: str) -> str: ... + def getValue(self, name: _AttrKey) -> str: ... def getValueByQName(self, name: str) -> str: ... - def getNameByQName(self, name: str) -> str: ... - def getQNameByName(self, name: str) -> str: ... - def getNames(self) -> list[str]: ... + def getNameByQName(self, name: str) -> _AttrKey: ... + def getQNameByName(self, name: _AttrKey) -> str: ... + def getNames(self) -> list[_AttrKey]: ... def getQNames(self) -> list[str]: ... def __len__(self) -> int: ... - def __getitem__(self, name: str) -> str: ... - def keys(self) -> list[str]: ... - def __contains__(self, name: str) -> bool: ... + def __getitem__(self, name: _AttrKey) -> str: ... + def keys(self) -> list[_AttrKey]: ... + def __contains__(self, name: _AttrKey) -> bool: ... @overload - def get(self, name: str, alternative: None = None) -> str | None: ... + def get(self, name: _AttrKey, alternative: None = None) -> str | None: ... @overload - def get(self, name: str, alternative: str) -> str: ... + def get(self, name: _AttrKey, alternative: str) -> str: ... def copy(self) -> Self: ... - def items(self) -> list[tuple[str, str]]: ... + def items(self) -> list[tuple[_AttrKey, str]]: ... def values(self) -> list[str]: ... _NSName: TypeAlias = tuple[str | None, str] -class AttributesNSImpl(AttributesImpl): +class AttributesNSImpl(AttributesImpl[_NSName]): def __init__(self, attrs: Mapping[_NSName, str], qnames: Mapping[_NSName, str]) -> None: ... - def getType(self, name: _NSName) -> str: ... # type: ignore[override] - def getValue(self, name: _NSName) -> str: ... # type: ignore[override] - def getNameByQName(self, name: str) -> _NSName: ... # type: ignore[override] - def getQNameByName(self, name: _NSName) -> str: ... # type: ignore[override] - def getNames(self) -> list[_NSName]: ... # type: ignore[override] - def __getitem__(self, name: _NSName) -> str: ... # type: ignore[override] - def keys(self) -> list[_NSName]: ... # type: ignore[override] - def __contains__(self, name: _NSName) -> bool: ... # type: ignore[override] - @overload # type: ignore[override] + def getValue(self, name: _NSName) -> str: ... + def getNameByQName(self, name: str) -> _NSName: ... + def getQNameByName(self, name: _NSName) -> str: ... + def getNames(self) -> list[_NSName]: ... + def __getitem__(self, name: _NSName) -> str: ... + def keys(self) -> list[_NSName]: ... + def __contains__(self, name: _NSName) -> bool: ... + @overload def get(self, name: _NSName, alternative: None = None) -> str | None: ... @overload def get(self, name: _NSName, alternative: str) -> str: ... - def items(self) -> list[tuple[_NSName, str]]: ... # type: ignore[override] + def items(self) -> list[tuple[_NSName, str]]: ... diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index d254102acc553..42420ee85848f 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -6,9 +6,10 @@ from collections.abc import Callable, Iterable, Mapping from datetime import datetime from io import BytesIO from types import TracebackType -from typing import Any, Final, Literal, Protocol, overload +from typing import Any, ClassVar, Final, Literal, Protocol, overload, type_check_only from typing_extensions import Self, TypeAlias +@type_check_only class _SupportsTimeTuple(Protocol): def timetuple(self) -> time.struct_time: ... @@ -76,6 +77,7 @@ def _strftime(value: _XMLDate) -> str: ... # undocumented class DateTime: value: str # undocumented def __init__(self, value: int | str | datetime | time.struct_time | tuple[int, ...] = 0) -> None: ... + __hash__: ClassVar[None] # type: ignore[assignment] def __lt__(self, other: _DateTimeComparable) -> bool: ... def __le__(self, other: _DateTimeComparable) -> bool: ... def __gt__(self, other: _DateTimeComparable) -> bool: ... @@ -95,6 +97,7 @@ class Binary: def decode(self, data: ReadableBuffer) -> None: ... def encode(self, out: SupportsWrite[str]) -> None: ... def __eq__(self, other: object) -> bool: ... + __hash__: ClassVar[None] # type: ignore[assignment] def _binary(data: ReadableBuffer) -> Binary: ... # undocumented @@ -108,8 +111,7 @@ class ExpatParser: # undocumented _WriteCallback: TypeAlias = Callable[[str], object] class Marshaller: - # TODO: Replace 'Any' with some kind of binding - dispatch: dict[type[Any], Callable[[Marshaller, Any, _WriteCallback], None]] + dispatch: dict[type[_Marshallable] | Literal["_arbitrary_instance"], Callable[[Marshaller, Any, _WriteCallback], None]] memo: dict[Any, None] data: None encoding: str | None @@ -200,7 +202,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..286aaf980fbf5 100644 --- a/mypy/typeshed/stdlib/xmlrpc/server.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/server.pyi @@ -1,30 +1,37 @@ 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 +from typing import Any, ClassVar, Protocol, type_check_only from typing_extensions import TypeAlias from xmlrpc.client import Fault, _Marshallable # The dispatch accepts anywhere from 0 to N arguments, no easy way to allow this in mypy +@type_check_only class _DispatchArity0(Protocol): def __call__(self) -> _Marshallable: ... +@type_check_only class _DispatchArity1(Protocol): def __call__(self, arg1: _Marshallable, /) -> _Marshallable: ... +@type_check_only class _DispatchArity2(Protocol): def __call__(self, arg1: _Marshallable, arg2: _Marshallable, /) -> _Marshallable: ... +@type_check_only class _DispatchArity3(Protocol): def __call__(self, arg1: _Marshallable, arg2: _Marshallable, arg3: _Marshallable, /) -> _Marshallable: ... +@type_check_only class _DispatchArity4(Protocol): def __call__( self, arg1: _Marshallable, arg2: _Marshallable, arg3: _Marshallable, arg4: _Marshallable, / ) -> _Marshallable: ... +@type_check_only class _DispatchArityN(Protocol): def __call__(self, *args: _Marshallable) -> _Marshallable: ... @@ -48,8 +55,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/xxlimited.pyi b/mypy/typeshed/stdlib/xxlimited.pyi index 6bae87a8db2a2..78a50b85f405a 100644 --- a/mypy/typeshed/stdlib/xxlimited.pyi +++ b/mypy/typeshed/stdlib/xxlimited.pyi @@ -1,5 +1,5 @@ import sys -from typing import Any, final +from typing import Any, ClassVar, final class Str(str): ... @@ -17,6 +17,8 @@ if sys.version_info >= (3, 10): else: class error(Exception): ... - class Null: ... + + class Null: + __hash__: ClassVar[None] # type: ignore[assignment] def roj(b: Any, /) -> None: ... diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index 5b8f02f61bce6..e573d04dba051 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, Final, Literal, Protocol, overload +from typing import IO, Final, Literal, Protocol, overload, type_check_only from typing_extensions import Self, TypeAlias __all__ = [ @@ -24,13 +24,15 @@ __all__ = [ "LargeZipFile", ] +if sys.version_info >= (3, 14): + __all__ += ["ZIP_ZSTANDARD"] + # TODO: use TypeAlias for these two when mypy bugs are fixed # https://github.com/python/mypy/issues/16581 _DateTuple = tuple[int, int, int, int, int, int] # noqa: Y026 _ZipFileMode = Literal["r", "w", "x", "a"] # noqa: Y026 _ReadWriteMode: TypeAlias = Literal["r", "w"] -_ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] class BadZipFile(Exception): ... @@ -39,6 +41,7 @@ error = BadZipfile class LargeZipFile(Exception): ... +@type_check_only class _ZipStream(Protocol): def read(self, n: int, /) -> bytes: ... # The following methods are optional: @@ -47,11 +50,13 @@ class _ZipStream(Protocol): # def seek(self, n: int, /) -> object: ... # Stream shape as required by _EndRecData() and _EndRecData64(). +@type_check_only class _SupportsReadSeekTell(Protocol): def read(self, n: int = ..., /) -> bytes: ... def seek(self, cookie: int, whence: int, /) -> object: ... def tell(self) -> int: ... +@type_check_only class _ClosableZipStream(_ZipStream, Protocol): def close(self) -> object: ... @@ -91,18 +96,23 @@ class ZipExtFile(io.BufferedIOBase): def read1(self, n: int | None) -> bytes: ... # type: ignore[override] def seek(self, offset: int, whence: int = 0) -> int: ... +@type_check_only class _Writer(Protocol): def write(self, s: str, /) -> object: ... +@type_check_only class _ZipReadable(Protocol): def seek(self, offset: int, whence: int = 0, /) -> int: ... def read(self, n: int = -1, /) -> bytes: ... +@type_check_only class _ZipTellable(Protocol): def tell(self) -> int: ... +@type_check_only class _ZipReadableTellable(_ZipReadable, _ZipTellable, Protocol): ... +@type_check_only class _ZipWritable(Protocol): def flush(self) -> None: ... def close(self) -> None: ... @@ -151,7 +161,7 @@ class ZipFile: def __init__( self, file: StrPath | _ZipWritable, - mode: Literal["w", "x"] = ..., + mode: Literal["w", "x"], compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, @@ -163,7 +173,7 @@ class ZipFile: def __init__( self, file: StrPath | _ZipReadableTellable, - mode: Literal["a"] = ..., + mode: Literal["a"], compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, @@ -198,7 +208,7 @@ class ZipFile: def __init__( self, file: StrPath | _ZipWritable, - mode: Literal["w", "x"] = ..., + mode: Literal["w", "x"], compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, @@ -209,7 +219,7 @@ class ZipFile: def __init__( self, file: StrPath | _ZipReadableTellable, - mode: Literal["a"] = ..., + mode: Literal["a"], compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, @@ -262,6 +272,29 @@ class PyZipFile(ZipFile): def writepy(self, pathname: str, basename: str = "", filterfunc: Callable[[str], bool] | None = None) -> None: ... class ZipInfo: + __slots__ = ( + "orig_filename", + "filename", + "date_time", + "compress_type", + "compress_level", + "comment", + "extra", + "create_system", + "create_version", + "extract_version", + "reserved", + "flag_bits", + "volume", + "internal_attr", + "external_attr", + "header_offset", + "CRC", + "compress_size", + "file_size", + "_raw_time", + "_end_offset", + ) filename: str date_time: _DateTuple compress_type: int @@ -321,25 +354,20 @@ else: @property def stem(self) -> str: ... - if sys.version_info >= (3, 9): - @overload - def open( - self, - mode: Literal["r", "w"] = "r", - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool = ..., - write_through: bool = ..., - *, - pwd: bytes | None = None, - ) -> TextIOWrapper: ... - @overload - def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... - else: - def open( - self, mode: _ReadWriteBinaryMode = "r", pwd: bytes | None = None, *, force_zip64: bool = False - ) -> IO[bytes]: ... + @overload + def open( + self, + mode: Literal["r", "w"] = "r", + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = ..., + write_through: bool = ..., + *, + pwd: bytes | None = None, + ) -> TextIOWrapper: ... + @overload + def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... if sys.version_info >= (3, 10): def iterdir(self) -> Iterator[Self]: ... @@ -362,23 +390,26 @@ else: def joinpath(self, *other: StrPath) -> Path: ... else: def joinpath(self, add: StrPath) -> Path: ... # undocumented - if sys.version_info >= (3, 12): - def glob(self, pattern: str) -> Iterator[Self]: ... - def rglob(self, pattern: str) -> Iterator[Self]: ... - def is_symlink(self) -> Literal[False]: ... - def relative_to(self, other: Path, *extra: StrPath) -> str: ... - def match(self, path_pattern: str) -> bool: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... def __truediv__(self, add: StrPath) -> Path: ... def is_zipfile(filename: StrOrBytesPath | _SupportsReadSeekTell) -> bool: ... -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] + +ZIP_STORED: Final = 0 +ZIP_DEFLATED: Final = 8 +ZIP_BZIP2: Final = 12 +ZIP_LZMA: Final = 14 +if sys.version_info >= (3, 14): + ZIP_ZSTANDARD: Final = 93 + +DEFAULT_VERSION: Final[int] +ZIP64_VERSION: Final[int] +BZIP2_VERSION: Final[int] +LZMA_VERSION: Final[int] +if sys.version_info >= (3, 14): + ZSTANDARD_VERSION: Final[int] +MAX_EXTRACT_VERSION: Final[int] diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi deleted file mode 100644 index 933acf2c4803c..0000000000000 --- a/mypy/typeshed/stdlib/zipfile/_path.pyi +++ /dev/null @@ -1,101 +0,0 @@ -import sys -from _typeshed import StrPath -from collections.abc import Iterator, Sequence -from io import TextIOWrapper -from os import PathLike -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): - class InitializedState: - def __init__(self, *args: object, **kwargs: object) -> None: ... - def __getstate__(self) -> tuple[list[object], dict[object, object]]: ... - def __setstate__(self, state: Sequence[tuple[list[object], dict[object, object]]]) -> None: ... - - class CompleteDirs(InitializedState, ZipFile): - def resolve_dir(self, name: str) -> str: ... - @overload - @classmethod - def make(cls, source: ZipFile) -> CompleteDirs: ... - @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: ... - @property - def parent(self) -> PathLike[str]: ... # undocumented - if sys.version_info >= (3, 10): - @property - def filename(self) -> PathLike[str]: ... # undocumented - if sys.version_info >= (3, 11): - @property - def suffix(self) -> str: ... - @property - def suffixes(self) -> list[str]: ... - @property - def stem(self) -> str: ... - - if sys.version_info >= (3, 9): - @overload - def open( - self, - mode: Literal["r", "w"] = "r", - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool = ..., - write_through: bool = ..., - *, - pwd: bytes | None = None, - ) -> TextIOWrapper: ... - @overload - def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... - else: - def open( - self, mode: _ReadWriteBinaryMode = "r", pwd: bytes | None = None, *, force_zip64: bool = False - ) -> IO[bytes]: ... - - if sys.version_info >= (3, 10): - def iterdir(self) -> Iterator[Self]: ... - else: - def iterdir(self) -> Iterator[Path]: ... - - def is_dir(self) -> bool: ... - def is_file(self) -> bool: ... - def exists(self) -> bool: ... - def read_text( - self, - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - line_buffering: bool = ..., - write_through: bool = ..., - ) -> str: ... - def read_bytes(self) -> bytes: ... - if sys.version_info >= (3, 10): - def joinpath(self, *other: StrPath) -> Path: ... - else: - def joinpath(self, add: StrPath) -> Path: ... # undocumented - if sys.version_info >= (3, 12): - def glob(self, pattern: str) -> Iterator[Self]: ... - def rglob(self, pattern: str) -> Iterator[Self]: ... - def is_symlink(self) -> Literal[False]: ... - def relative_to(self, other: Path, *extra: StrPath) -> str: ... - def match(self, path_pattern: str) -> bool: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - - def __truediv__(self, add: StrPath) -> Path: ... diff --git a/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi b/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi new file mode 100644 index 0000000000000..4c7b39ec4c6ca --- /dev/null +++ b/mypy/typeshed/stdlib/zipfile/_path/__init__.pyi @@ -0,0 +1,83 @@ +import sys +from _typeshed import StrPath +from collections.abc import Iterator, Sequence +from io import TextIOWrapper +from os import PathLike +from typing import IO, Literal, TypeVar, overload +from typing_extensions import Self +from zipfile import ZipFile + +_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]]: ... + def __setstate__(self, state: Sequence[tuple[list[object], dict[object, object]]]) -> None: ... + + class CompleteDirs(InitializedState, ZipFile): + def resolve_dir(self, name: str) -> str: ... + @overload + @classmethod + def make(cls, source: ZipFile) -> CompleteDirs: ... + @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: ... + @property + def parent(self) -> PathLike[str]: ... # undocumented + @property + def filename(self) -> PathLike[str]: ... # undocumented + @property + def suffix(self) -> str: ... + @property + def suffixes(self) -> list[str]: ... + @property + def stem(self) -> str: ... + @overload + def open( + self, + mode: Literal["r", "w"] = "r", + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = ..., + write_through: bool = ..., + *, + pwd: bytes | None = None, + ) -> TextIOWrapper: ... + @overload + def open(self, mode: Literal["rb", "wb"], *, pwd: bytes | None = None) -> IO[bytes]: ... + def iterdir(self) -> Iterator[Self]: ... + def is_dir(self) -> bool: ... + def is_file(self) -> bool: ... + def exists(self) -> bool: ... + def read_text( + self, + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + line_buffering: bool = ..., + write_through: bool = ..., + ) -> str: ... + def read_bytes(self) -> bytes: ... + def joinpath(self, *other: StrPath) -> Path: ... + def glob(self, pattern: str) -> Iterator[Self]: ... + def rglob(self, pattern: str) -> Iterator[Self]: ... + def is_symlink(self) -> Literal[False]: ... + def relative_to(self, other: Path, *extra: StrPath) -> str: ... + def match(self, path_pattern: str) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def __truediv__(self, add: StrPath) -> Path: ... diff --git a/mypy/typeshed/stdlib/zipfile/_path/glob.pyi b/mypy/typeshed/stdlib/zipfile/_path/glob.pyi new file mode 100644 index 0000000000000..f6a661be8cdf4 --- /dev/null +++ b/mypy/typeshed/stdlib/zipfile/_path/glob.pyi @@ -0,0 +1,26 @@ +import sys +from collections.abc import Iterator +from re import Match + +if sys.version_info >= (3, 13): + class Translator: + if sys.platform == "win32": + def __init__(self, seps: str = "\\/") -> None: ... + else: + def __init__(self, seps: str = "/") -> None: ... + + def translate(self, pattern: str) -> str: ... + def extend(self, pattern: str) -> str: ... + def match_dirs(self, pattern: str) -> str: ... + def translate_core(self, pattern: str) -> str: ... + def replace(self, match: Match[str]) -> str: ... + def restrict_rglob(self, pattern: str) -> None: ... + def star_not_empty(self, pattern: str) -> str: ... + +else: + def translate(pattern: str) -> str: ... + def match_dirs(pattern: str) -> str: ... + def translate_core(pattern: str) -> str: ... + def replace(match: Match[str]) -> str: ... + +def separate(pattern: str) -> Iterator[Match[str]]: ... diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index f53b09e188ebc..22af3c272759b 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -1,14 +1,24 @@ import sys 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 importlib.readers import ZipReader +else: + from importlib.abc import ResourceReader + +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): @@ -17,18 +27,33 @@ class zipimporter: def __init__(self, path: StrOrBytesPath) -> None: ... if sys.version_info < (3, 12): - def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ... # undocumented - def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ... + if sys.version_info >= (3, 10): + @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `find_spec()` instead.") + def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ... + @deprecated("Deprecated since Python 3.10; removed in Python 3.12. Use `find_spec()` instead.") + def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ... + else: + def find_loader(self, fullname: str, path: str | None = None) -> tuple[zipimporter | None, list[str]]: ... + def find_module(self, fullname: str, path: str | None = None) -> zipimporter | None: ... def get_code(self, fullname: str) -> CodeType: ... def get_data(self, pathname: str) -> bytes: ... def get_filename(self, fullname: str) -> str: ... - def get_resource_reader(self, fullname: str) -> ResourceReader | None: ... # undocumented + if sys.version_info >= (3, 14): + def get_resource_reader(self, fullname: str) -> ZipReader: ... # undocumented + elif sys.version_info >= (3, 10): + def get_resource_reader(self, fullname: str) -> ZipReader | None: ... # undocumented + else: + 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: ... - def load_module(self, fullname: str) -> ModuleType: ... if sys.version_info >= (3, 10): + @deprecated("Deprecated since Python 3.10; removed in Python 3.15. Use `exec_module()` instead.") + def load_module(self, fullname: str) -> ModuleType: ... 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: ... + else: + def load_module(self, fullname: str) -> ModuleType: ... diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi index 2f6c406560384..d5998cab90fef 100644 --- a/mypy/typeshed/stdlib/zlib.pyi +++ b/mypy/typeshed/stdlib/zlib.pyi @@ -1,13 +1,14 @@ import sys from _typeshed import ReadableBuffer -from typing import Final +from typing import Any, Final, final, type_check_only +from typing_extensions import Self DEFLATED: Final = 8 -DEF_MEM_LEVEL: int # can change +DEF_MEM_LEVEL: Final[int] DEF_BUF_SIZE: Final = 16384 -MAX_WBITS: int -ZLIB_VERSION: str # can change -ZLIB_RUNTIME_VERSION: str # can change +MAX_WBITS: Final[int] +ZLIB_VERSION: Final[str] +ZLIB_RUNTIME_VERSION: Final[str] Z_NO_COMPRESSION: Final = 0 Z_PARTIAL_FLUSH: Final = 1 Z_BEST_COMPRESSION: Final = 9 @@ -25,19 +26,36 @@ Z_RLE: Final = 3 Z_SYNC_FLUSH: Final = 2 Z_TREES: Final = 6 +if sys.version_info >= (3, 14): + # Available when zlib was built with zlib-ng + ZLIBNG_VERSION: Final[str] + 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..b7433f835f83d 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 +from typing_extensions import Self, disjoint_base +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, +) __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] -class _IOBytes(Protocol): - def read(self, size: int, /) -> bytes: ... - def seek(self, size: int, whence: int = ..., /) -> Any: ... - +@disjoint_base class ZoneInfo(tzinfo): @property def key(self) -> str: ... - def __init__(self, key: str) -> None: ... + def __new__(cls, key: str) -> Self: ... @classmethod def no_cache(cls, key: str) -> Self: ... - @classmethod - def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ... + if sys.version_info >= (3, 12): + @classmethod + def from_file(cls, file_obj: _IOBytes, /, key: str | None = None) -> Self: ... + else: + @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]: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/_common.pyi b/mypy/typeshed/stdlib/zoneinfo/_common.pyi new file mode 100644 index 0000000000000..e6d2d83caac18 --- /dev/null +++ b/mypy/typeshed/stdlib/zoneinfo/_common.pyi @@ -0,0 +1,14 @@ +import io +from typing import Any, Protocol, type_check_only + +@type_check_only +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/typeshed/stubs/librt/METADATA.toml b/mypy/typeshed/stubs/librt/METADATA.toml new file mode 100644 index 0000000000000..a42da251bed52 --- /dev/null +++ b/mypy/typeshed/stubs/librt/METADATA.toml @@ -0,0 +1 @@ +version = "0.2.*" diff --git a/mypy/typeshed/stubs/librt/librt/__init__.pyi b/mypy/typeshed/stubs/librt/librt/__init__.pyi new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mypy/typeshed/stubs/librt/librt/base64.pyi b/mypy/typeshed/stubs/librt/librt/base64.pyi new file mode 100644 index 0000000000000..1cea838505d67 --- /dev/null +++ b/mypy/typeshed/stubs/librt/librt/base64.pyi @@ -0,0 +1,2 @@ +def b64encode(s: bytes) -> bytes: ... +def b64decode(s: bytes | str) -> bytes: ... diff --git a/mypy/typeshed/stubs/librt/librt/internal.pyi b/mypy/typeshed/stubs/librt/librt/internal.pyi new file mode 100644 index 0000000000000..2969ccfbadda7 --- /dev/null +++ b/mypy/typeshed/stubs/librt/librt/internal.pyi @@ -0,0 +1,21 @@ +from mypy_extensions import u8 + +class ReadBuffer: + def __init__(self, source: bytes) -> None: ... + +class WriteBuffer: + def getvalue(self) -> bytes: ... + +def write_bool(data: WriteBuffer, value: bool) -> None: ... +def read_bool(data: ReadBuffer) -> bool: ... +def write_str(data: WriteBuffer, value: str) -> None: ... +def read_str(data: ReadBuffer) -> str: ... +def write_bytes(data: WriteBuffer, value: bytes) -> None: ... +def read_bytes(data: ReadBuffer) -> bytes: ... +def write_float(data: WriteBuffer, value: float) -> None: ... +def read_float(data: ReadBuffer) -> float: ... +def write_int(data: WriteBuffer, value: int) -> None: ... +def read_int(data: ReadBuffer) -> int: ... +def write_tag(data: WriteBuffer, value: u8) -> None: ... +def read_tag(data: ReadBuffer) -> u8: ... +def cache_version() -> u8: ... diff --git a/mypy/typestate.py b/mypy/typestate.py index 0082c55647050..574618668477e 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Dict, Final, Set, Tuple +from typing import Final from typing_extensions import TypeAlias as _TypeAlias from mypy.nodes import VARIANCE_NOT_READY, TypeInfo @@ -16,15 +16,15 @@ MAX_NEGATIVE_CACHE_ENTRIES: Final = 10000 # Represents that the 'left' instance is a subtype of the 'right' instance -SubtypeRelationship: _TypeAlias = Tuple[Instance, Instance] +SubtypeRelationship: _TypeAlias = tuple[Instance, Instance] # A tuple encoding the specific conditions under which we performed the subtype check. # (e.g. did we want a proper subtype? A regular subtype while ignoring variance?) -SubtypeKind: _TypeAlias = Tuple[bool, ...] +SubtypeKind: _TypeAlias = tuple[bool, ...] # A cache that keeps track of whether the given TypeInfo is a part of a particular # subtype relationship -SubtypeCache: _TypeAlias = Dict[TypeInfo, Dict[SubtypeKind, Set[SubtypeRelationship]]] +SubtypeCache: _TypeAlias = dict[TypeInfo, dict[SubtypeKind, set[SubtypeRelationship]]] class TypeState: diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index a28bbf422b612..abd0f6bf3bdfe 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from mypy_extensions import trait @@ -42,47 +42,48 @@ class TypeTraverserVisitor(SyntheticTypeVisitor[None]): # Atomic types - def visit_any(self, t: AnyType) -> None: + def visit_any(self, t: AnyType, /) -> None: pass - def visit_uninhabited_type(self, t: UninhabitedType) -> None: + def visit_uninhabited_type(self, t: UninhabitedType, /) -> None: pass - def visit_none_type(self, t: NoneType) -> None: + def visit_none_type(self, t: NoneType, /) -> None: pass - def visit_erased_type(self, t: ErasedType) -> None: + def visit_erased_type(self, t: ErasedType, /) -> None: pass - def visit_deleted_type(self, t: DeletedType) -> None: + def visit_deleted_type(self, t: DeletedType, /) -> None: pass - def visit_type_var(self, t: TypeVarType) -> None: + def visit_type_var(self, t: TypeVarType, /) -> None: # Note that type variable values and upper bound aren't treated as # components, since they are components of the type variable # definition. We want to traverse everything just once. t.default.accept(self) - def visit_param_spec(self, t: ParamSpecType) -> None: + def visit_param_spec(self, t: ParamSpecType, /) -> None: + # TODO: do we need to traverse prefix here? t.default.accept(self) - def visit_parameters(self, t: Parameters) -> None: - self.traverse_types(t.arg_types) + def visit_parameters(self, t: Parameters, /) -> None: + self.traverse_type_list(t.arg_types) - def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> None: t.default.accept(self) - def visit_literal_type(self, t: LiteralType) -> None: + def visit_literal_type(self, t: LiteralType, /) -> None: t.fallback.accept(self) # Composite types - def visit_instance(self, t: Instance) -> None: - self.traverse_types(t.args) + def visit_instance(self, t: Instance, /) -> None: + self.traverse_type_tuple(t.args) - def visit_callable_type(self, t: CallableType) -> None: + def visit_callable_type(self, t: CallableType, /) -> None: # FIX generics - self.traverse_types(t.arg_types) + self.traverse_type_list(t.arg_types) t.ret_type.accept(self) t.fallback.accept(self) @@ -92,57 +93,67 @@ def visit_callable_type(self, t: CallableType) -> None: if t.type_is is not None: t.type_is.accept(self) - def visit_tuple_type(self, t: TupleType) -> None: - self.traverse_types(t.items) + def visit_tuple_type(self, t: TupleType, /) -> None: + self.traverse_type_list(t.items) t.partial_fallback.accept(self) - def visit_typeddict_type(self, t: TypedDictType) -> None: + def visit_typeddict_type(self, t: TypedDictType, /) -> None: self.traverse_types(t.items.values()) t.fallback.accept(self) - def visit_union_type(self, t: UnionType) -> None: - self.traverse_types(t.items) + def visit_union_type(self, t: UnionType, /) -> None: + self.traverse_type_list(t.items) - def visit_overloaded(self, t: Overloaded) -> None: + def visit_overloaded(self, t: Overloaded, /) -> None: self.traverse_types(t.items) - def visit_type_type(self, t: TypeType) -> None: + def visit_type_type(self, t: TypeType, /) -> None: t.item.accept(self) # Special types (not real types) - def visit_callable_argument(self, t: CallableArgument) -> None: + def visit_callable_argument(self, t: CallableArgument, /) -> None: t.typ.accept(self) - def visit_unbound_type(self, t: UnboundType) -> None: - self.traverse_types(t.args) + def visit_unbound_type(self, t: UnboundType, /) -> None: + self.traverse_type_tuple(t.args) - def visit_type_list(self, t: TypeList) -> None: - self.traverse_types(t.items) + def visit_type_list(self, t: TypeList, /) -> None: + self.traverse_type_list(t.items) - def visit_ellipsis_type(self, t: EllipsisType) -> None: + def visit_ellipsis_type(self, t: EllipsisType, /) -> None: pass - def visit_placeholder_type(self, t: PlaceholderType) -> None: - self.traverse_types(t.args) + def visit_placeholder_type(self, t: PlaceholderType, /) -> None: + self.traverse_type_list(t.args) - def visit_partial_type(self, t: PartialType) -> None: + def visit_partial_type(self, t: PartialType, /) -> None: pass - def visit_raw_expression_type(self, t: RawExpressionType) -> None: + def visit_raw_expression_type(self, t: RawExpressionType, /) -> None: pass - def visit_type_alias_type(self, t: TypeAliasType) -> None: + def visit_type_alias_type(self, t: TypeAliasType, /) -> None: # TODO: sometimes we want to traverse target as well # We need to find a way to indicate explicitly the intent, # maybe make this method abstract (like for TypeTranslator)? - self.traverse_types(t.args) + self.traverse_type_list(t.args) - def visit_unpack_type(self, t: UnpackType) -> None: + def visit_unpack_type(self, t: UnpackType, /) -> None: t.type.accept(self) # Helpers - def traverse_types(self, types: Iterable[Type]) -> None: + def traverse_types(self, types: Iterable[Type], /) -> None: + for typ in types: + typ.accept(self) + + def traverse_type_list(self, types: list[Type], /) -> None: + # Micro-optimization: Specialized for lists + for typ in types: + typ.accept(self) + + def traverse_type_tuple(self, types: tuple[Type, ...], /) -> None: + # Micro-optimization: Specialized for tuples for typ in types: typ.accept(self) diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index 2a9998c10746f..1bf1a59f7d3fb 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -2,17 +2,15 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from mypy.types import ( AnyType, Instance, - ProperType, Type, TypeVarLikeType, TypeVarTupleType, UnpackType, - get_proper_type, split_with_prefix_and_suffix, ) @@ -27,14 +25,6 @@ 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: diff --git a/mypy/util.py b/mypy/util.py index 4b1b918b92e6b..c919ff87f5b02 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 collections.abc import Container, Iterable, Sequence, Sized from importlib import resources as importlib_resources -from typing import IO, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar -from typing_extensions import Literal +from typing import IO, Any, Callable, Final, Literal, TypeVar +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: @@ -25,15 +30,7 @@ T = TypeVar("T") -if sys.version_info >= (3, 9): - TYPESHED_DIR: Final = str(importlib_resources.files("mypy") / "typeshed") -else: - with importlib_resources.path( - "mypy", # mypy-c doesn't support __package__ - "py.typed", # a marker file for type information, we assume typeshed to live in the same dir - ) as _resource: - TYPESHED_DIR = str(_resource.parent / "typeshed") - +TYPESHED_DIR: Final = str(importlib_resources.files("mypy") / "typeshed") ENCODING_RE: Final = re.compile(rb"([ \t\v]*#.*(\r\n?|\n))??[ \t\v]*#.*coding[:=][ \t]*([-\w.]+)") @@ -69,7 +66,7 @@ def is_dunder(name: str, exclude_special: bool = False) -> bool: def is_sunder(name: str) -> bool: - return not is_dunder(name) and name.startswith("_") and name.endswith("_") + return not is_dunder(name) and name.startswith("_") and name.endswith("_") and name != "_" def split_module_names(mod_name: str) -> list[str]: @@ -196,7 +193,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 +409,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: @@ -451,10 +482,10 @@ def get_unique_redefinition_name(name: str, existing: Container[str]) -> str: def check_python_version(program: str) -> None: """Report issues with the Python used to run mypy, dmypy, or stubgen""" # Check for known bad Python versions. - if sys.version_info[:2] < (3, 8): # noqa: UP036 + if sys.version_info[:2] < (3, 9): # noqa: UP036, RUF100 sys.exit( - "Running {name} with Python 3.7 or lower is not supported; " - "please upgrade to 3.8 or newer".format(name=program) + "Running {name} with Python 3.8 or lower is not supported; " + "please upgrade to 3.9 or newer".format(name=program) ) @@ -535,15 +566,20 @@ 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 hash_digest_bytes(data: bytes) -> bytes: + """Compute a hash digest of some data. + + Similar to above but returns a bytes object. + """ + return hashlib.sha1(data).digest() def parse_gray_color(cup: bytes) -> str: """Reproduce a gray color in ANSI escape sequence""" - if sys.platform == "win32": - assert False, "curses is not available on Windows" + assert sys.platform != "win32", "curses is not available on Windows" set_color = "".join([cup[:-1].decode(), "m"]) gray = curses.tparm(set_color.encode("utf-8"), 1, 9).decode() return gray @@ -610,8 +646,7 @@ def initialize_win_colors(self) -> bool: # Windows ANSI escape sequences are only supported on Threshold 2 and above. # we check with an assert at runtime and an if check for mypy, as asserts do not # yet narrow platform - assert sys.platform == "win32" - if sys.platform == "win32": + if sys.platform == "win32": # needed to find win specific sys apis winver = sys.getwindowsversion() if ( winver.major < MINIMUM_WINDOWS_MAJOR_VT100 @@ -633,11 +668,12 @@ def initialize_win_colors(self) -> bool: ) self.initialize_vt100_colors() return True - return False + assert False, "Running not on Windows" def initialize_unix_colors(self) -> bool: """Return True if initialization was successful and we can use colors, False otherwise""" - if sys.platform == "win32" or not CURSES_ENABLED: + is_win = sys.platform == "win32" + if is_win or not CURSES_ENABLED: return False try: # setupterm wants a fd to potentially write an "initialization sequence". @@ -833,6 +869,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 +922,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 8e00b4cce7022..eded284e79415 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.12.0+dev" +__version__ = "1.19.1" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) diff --git a/mypy/visitor.py b/mypy/visitor.py index 340e1af64e00e..e150788ec3c16 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -20,179 +20,183 @@ @mypyc_attr(allow_interpreted_subclasses=True) class ExpressionVisitor(Generic[T]): @abstractmethod - def visit_int_expr(self, o: mypy.nodes.IntExpr) -> T: + def visit_int_expr(self, o: mypy.nodes.IntExpr, /) -> T: pass @abstractmethod - def visit_str_expr(self, o: mypy.nodes.StrExpr) -> T: + def visit_str_expr(self, o: mypy.nodes.StrExpr, /) -> T: pass @abstractmethod - def visit_bytes_expr(self, o: mypy.nodes.BytesExpr) -> T: + def visit_bytes_expr(self, o: mypy.nodes.BytesExpr, /) -> T: pass @abstractmethod - def visit_float_expr(self, o: mypy.nodes.FloatExpr) -> T: + def visit_float_expr(self, o: mypy.nodes.FloatExpr, /) -> T: pass @abstractmethod - def visit_complex_expr(self, o: mypy.nodes.ComplexExpr) -> T: + def visit_complex_expr(self, o: mypy.nodes.ComplexExpr, /) -> T: pass @abstractmethod - def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr) -> T: + def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr, /) -> T: pass @abstractmethod - def visit_star_expr(self, o: mypy.nodes.StarExpr) -> T: + def visit_star_expr(self, o: mypy.nodes.StarExpr, /) -> T: pass @abstractmethod - def visit_name_expr(self, o: mypy.nodes.NameExpr) -> T: + def visit_name_expr(self, o: mypy.nodes.NameExpr, /) -> T: pass @abstractmethod - def visit_member_expr(self, o: mypy.nodes.MemberExpr) -> T: + def visit_member_expr(self, o: mypy.nodes.MemberExpr, /) -> T: pass @abstractmethod - def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr) -> T: + def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr, /) -> T: pass @abstractmethod - def visit_yield_expr(self, o: mypy.nodes.YieldExpr) -> T: + def visit_yield_expr(self, o: mypy.nodes.YieldExpr, /) -> T: pass @abstractmethod - def visit_call_expr(self, o: mypy.nodes.CallExpr) -> T: + def visit_call_expr(self, o: mypy.nodes.CallExpr, /) -> T: pass @abstractmethod - def visit_op_expr(self, o: mypy.nodes.OpExpr) -> T: + def visit_op_expr(self, o: mypy.nodes.OpExpr, /) -> T: pass @abstractmethod - def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> T: + def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr, /) -> T: pass @abstractmethod - def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> T: + def visit_cast_expr(self, o: mypy.nodes.CastExpr, /) -> T: pass @abstractmethod - def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> T: + def visit_type_form_expr(self, o: mypy.nodes.TypeFormExpr, /) -> T: pass @abstractmethod - def visit_reveal_expr(self, o: mypy.nodes.RevealExpr) -> T: + def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr, /) -> T: pass @abstractmethod - def visit_super_expr(self, o: mypy.nodes.SuperExpr) -> T: + def visit_reveal_expr(self, o: mypy.nodes.RevealExpr, /) -> T: pass @abstractmethod - def visit_unary_expr(self, o: mypy.nodes.UnaryExpr) -> T: + def visit_super_expr(self, o: mypy.nodes.SuperExpr, /) -> T: pass @abstractmethod - def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr) -> T: + def visit_unary_expr(self, o: mypy.nodes.UnaryExpr, /) -> T: pass @abstractmethod - def visit_list_expr(self, o: mypy.nodes.ListExpr) -> T: + def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr, /) -> T: pass @abstractmethod - def visit_dict_expr(self, o: mypy.nodes.DictExpr) -> T: + def visit_list_expr(self, o: mypy.nodes.ListExpr, /) -> T: pass @abstractmethod - def visit_tuple_expr(self, o: mypy.nodes.TupleExpr) -> T: + def visit_dict_expr(self, o: mypy.nodes.DictExpr, /) -> T: pass @abstractmethod - def visit_set_expr(self, o: mypy.nodes.SetExpr) -> T: + def visit_tuple_expr(self, o: mypy.nodes.TupleExpr, /) -> T: pass @abstractmethod - def visit_index_expr(self, o: mypy.nodes.IndexExpr) -> T: + def visit_set_expr(self, o: mypy.nodes.SetExpr, /) -> T: pass @abstractmethod - def visit_type_application(self, o: mypy.nodes.TypeApplication) -> T: + def visit_index_expr(self, o: mypy.nodes.IndexExpr, /) -> T: pass @abstractmethod - def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr) -> T: + def visit_type_application(self, o: mypy.nodes.TypeApplication, /) -> T: pass @abstractmethod - def visit_list_comprehension(self, o: mypy.nodes.ListComprehension) -> T: + def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr, /) -> T: pass @abstractmethod - def visit_set_comprehension(self, o: mypy.nodes.SetComprehension) -> T: + def visit_list_comprehension(self, o: mypy.nodes.ListComprehension, /) -> T: pass @abstractmethod - def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension) -> T: + def visit_set_comprehension(self, o: mypy.nodes.SetComprehension, /) -> T: pass @abstractmethod - def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr) -> T: + def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension, /) -> T: pass @abstractmethod - def visit_slice_expr(self, o: mypy.nodes.SliceExpr) -> T: + def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr, /) -> T: pass @abstractmethod - def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr) -> T: + def visit_slice_expr(self, o: mypy.nodes.SliceExpr, /) -> T: pass @abstractmethod - def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr) -> T: + def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr, /) -> T: pass @abstractmethod - def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> T: + def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr, /) -> T: pass @abstractmethod - def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> T: + def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr, /) -> T: pass @abstractmethod - def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> T: + def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr, /) -> T: pass @abstractmethod - def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> T: + def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr, /) -> T: pass @abstractmethod - def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr) -> T: + def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr, /) -> T: pass @abstractmethod - def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr) -> T: + def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr, /) -> T: pass @abstractmethod - def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr) -> T: + def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr, /) -> T: pass @abstractmethod - def visit__promote_expr(self, o: mypy.nodes.PromoteExpr) -> T: + def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr, /) -> T: pass @abstractmethod - def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> T: + def visit__promote_expr(self, o: mypy.nodes.PromoteExpr, /) -> T: pass @abstractmethod - def visit_temp_node(self, o: mypy.nodes.TempNode) -> T: + def visit_await_expr(self, o: mypy.nodes.AwaitExpr, /) -> T: + pass + + @abstractmethod + def visit_temp_node(self, o: mypy.nodes.TempNode, /) -> T: pass @@ -202,115 +206,115 @@ class StatementVisitor(Generic[T]): # Definitions @abstractmethod - def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt) -> T: + def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt, /) -> T: pass @abstractmethod - def visit_for_stmt(self, o: mypy.nodes.ForStmt) -> T: + def visit_for_stmt(self, o: mypy.nodes.ForStmt, /) -> T: pass @abstractmethod - def visit_with_stmt(self, o: mypy.nodes.WithStmt) -> T: + def visit_with_stmt(self, o: mypy.nodes.WithStmt, /) -> T: pass @abstractmethod - def visit_del_stmt(self, o: mypy.nodes.DelStmt) -> T: + def visit_del_stmt(self, o: mypy.nodes.DelStmt, /) -> T: pass @abstractmethod - def visit_func_def(self, o: mypy.nodes.FuncDef) -> T: + def visit_func_def(self, o: mypy.nodes.FuncDef, /) -> T: pass @abstractmethod - def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef) -> T: + def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef, /) -> T: pass @abstractmethod - def visit_class_def(self, o: mypy.nodes.ClassDef) -> T: + def visit_class_def(self, o: mypy.nodes.ClassDef, /) -> T: pass @abstractmethod - def visit_global_decl(self, o: mypy.nodes.GlobalDecl) -> T: + def visit_global_decl(self, o: mypy.nodes.GlobalDecl, /) -> T: pass @abstractmethod - def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl) -> T: + def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl, /) -> T: pass @abstractmethod - def visit_decorator(self, o: mypy.nodes.Decorator) -> T: + def visit_decorator(self, o: mypy.nodes.Decorator, /) -> T: pass # Module structure @abstractmethod - def visit_import(self, o: mypy.nodes.Import) -> T: + def visit_import(self, o: mypy.nodes.Import, /) -> T: pass @abstractmethod - def visit_import_from(self, o: mypy.nodes.ImportFrom) -> T: + def visit_import_from(self, o: mypy.nodes.ImportFrom, /) -> T: pass @abstractmethod - def visit_import_all(self, o: mypy.nodes.ImportAll) -> T: + def visit_import_all(self, o: mypy.nodes.ImportAll, /) -> T: pass # Statements @abstractmethod - def visit_block(self, o: mypy.nodes.Block) -> T: + def visit_block(self, o: mypy.nodes.Block, /) -> T: pass @abstractmethod - def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt) -> T: + def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt, /) -> T: pass @abstractmethod - def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt) -> T: + def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt, /) -> T: pass @abstractmethod - def visit_while_stmt(self, o: mypy.nodes.WhileStmt) -> T: + def visit_while_stmt(self, o: mypy.nodes.WhileStmt, /) -> T: pass @abstractmethod - def visit_return_stmt(self, o: mypy.nodes.ReturnStmt) -> T: + def visit_return_stmt(self, o: mypy.nodes.ReturnStmt, /) -> T: pass @abstractmethod - def visit_assert_stmt(self, o: mypy.nodes.AssertStmt) -> T: + def visit_assert_stmt(self, o: mypy.nodes.AssertStmt, /) -> T: pass @abstractmethod - def visit_if_stmt(self, o: mypy.nodes.IfStmt) -> T: + def visit_if_stmt(self, o: mypy.nodes.IfStmt, /) -> T: pass @abstractmethod - def visit_break_stmt(self, o: mypy.nodes.BreakStmt) -> T: + def visit_break_stmt(self, o: mypy.nodes.BreakStmt, /) -> T: pass @abstractmethod - def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt) -> T: + def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt, /) -> T: pass @abstractmethod - def visit_pass_stmt(self, o: mypy.nodes.PassStmt) -> T: + def visit_pass_stmt(self, o: mypy.nodes.PassStmt, /) -> T: pass @abstractmethod - def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt) -> T: + def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt, /) -> T: pass @abstractmethod - def visit_try_stmt(self, o: mypy.nodes.TryStmt) -> T: + def visit_try_stmt(self, o: mypy.nodes.TryStmt, /) -> T: pass @abstractmethod - def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> T: + def visit_match_stmt(self, o: mypy.nodes.MatchStmt, /) -> T: pass @abstractmethod - def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt, /) -> T: pass @@ -318,35 +322,35 @@ def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: @mypyc_attr(allow_interpreted_subclasses=True) class PatternVisitor(Generic[T]): @abstractmethod - def visit_as_pattern(self, o: mypy.patterns.AsPattern) -> T: + def visit_as_pattern(self, o: mypy.patterns.AsPattern, /) -> T: pass @abstractmethod - def visit_or_pattern(self, o: mypy.patterns.OrPattern) -> T: + def visit_or_pattern(self, o: mypy.patterns.OrPattern, /) -> T: pass @abstractmethod - def visit_value_pattern(self, o: mypy.patterns.ValuePattern) -> T: + def visit_value_pattern(self, o: mypy.patterns.ValuePattern, /) -> T: pass @abstractmethod - def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern) -> T: + def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern, /) -> T: pass @abstractmethod - def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern) -> T: + def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern, /) -> T: pass @abstractmethod - def visit_starred_pattern(self, o: mypy.patterns.StarredPattern) -> T: + def visit_starred_pattern(self, o: mypy.patterns.StarredPattern, /) -> T: pass @abstractmethod - def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern) -> T: + def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern, /) -> T: pass @abstractmethod - def visit_class_pattern(self, o: mypy.patterns.ClassPattern) -> T: + def visit_class_pattern(self, o: mypy.patterns.ClassPattern, /) -> T: pass @@ -356,273 +360,273 @@ class NodeVisitor(Generic[T], ExpressionVisitor[T], StatementVisitor[T], Pattern """Empty base class for parse tree node visitors. The T type argument specifies the return type of the visit - methods. As all methods defined here return None by default, + methods. As all methods defined here raise by default, subclasses do not always need to override all the methods. - - TODO: make the default return value explicit, then turn on - empty body checking in mypy_self_check.ini. """ # Not in superclasses: - def visit_mypy_file(self, o: mypy.nodes.MypyFile) -> T: - pass + def visit_mypy_file(self, o: mypy.nodes.MypyFile, /) -> T: + raise NotImplementedError() # TODO: We have a visit_var method, but no visit_typeinfo or any # other non-Statement SymbolNode (accepting those will raise a # runtime error). Maybe this should be resolved in some direction. - def visit_var(self, o: mypy.nodes.Var) -> T: - pass + def visit_var(self, o: mypy.nodes.Var, /) -> T: + raise NotImplementedError() # Module structure - def visit_import(self, o: mypy.nodes.Import) -> T: - pass + def visit_import(self, o: mypy.nodes.Import, /) -> T: + raise NotImplementedError() - def visit_import_from(self, o: mypy.nodes.ImportFrom) -> T: - pass + def visit_import_from(self, o: mypy.nodes.ImportFrom, /) -> T: + raise NotImplementedError() - def visit_import_all(self, o: mypy.nodes.ImportAll) -> T: - pass + def visit_import_all(self, o: mypy.nodes.ImportAll, /) -> T: + raise NotImplementedError() # Definitions - def visit_func_def(self, o: mypy.nodes.FuncDef) -> T: - pass + def visit_func_def(self, o: mypy.nodes.FuncDef, /) -> T: + raise NotImplementedError() - def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef) -> T: - pass + def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef, /) -> T: + raise NotImplementedError() - def visit_class_def(self, o: mypy.nodes.ClassDef) -> T: - pass + def visit_class_def(self, o: mypy.nodes.ClassDef, /) -> T: + raise NotImplementedError() - def visit_global_decl(self, o: mypy.nodes.GlobalDecl) -> T: - pass + def visit_global_decl(self, o: mypy.nodes.GlobalDecl, /) -> T: + raise NotImplementedError() - def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl) -> T: - pass + def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl, /) -> T: + raise NotImplementedError() - def visit_decorator(self, o: mypy.nodes.Decorator) -> T: - pass + def visit_decorator(self, o: mypy.nodes.Decorator, /) -> T: + raise NotImplementedError() - def visit_type_alias(self, o: mypy.nodes.TypeAlias) -> T: - pass + def visit_type_alias(self, o: mypy.nodes.TypeAlias, /) -> T: + raise NotImplementedError() - def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode) -> T: - pass + def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode, /) -> T: + raise NotImplementedError() # Statements - def visit_block(self, o: mypy.nodes.Block) -> T: - pass + def visit_block(self, o: mypy.nodes.Block, /) -> T: + raise NotImplementedError() - def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt) -> T: - pass + def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt, /) -> T: + raise NotImplementedError() - def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt) -> T: - pass + def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt, /) -> T: + raise NotImplementedError() - def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt) -> T: - pass + def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt, /) -> T: + raise NotImplementedError() - def visit_while_stmt(self, o: mypy.nodes.WhileStmt) -> T: - pass + def visit_while_stmt(self, o: mypy.nodes.WhileStmt, /) -> T: + raise NotImplementedError() - def visit_for_stmt(self, o: mypy.nodes.ForStmt) -> T: - pass + def visit_for_stmt(self, o: mypy.nodes.ForStmt, /) -> T: + raise NotImplementedError() - def visit_return_stmt(self, o: mypy.nodes.ReturnStmt) -> T: - pass + def visit_return_stmt(self, o: mypy.nodes.ReturnStmt, /) -> T: + raise NotImplementedError() - def visit_assert_stmt(self, o: mypy.nodes.AssertStmt) -> T: - pass + def visit_assert_stmt(self, o: mypy.nodes.AssertStmt, /) -> T: + raise NotImplementedError() - def visit_del_stmt(self, o: mypy.nodes.DelStmt) -> T: - pass + def visit_del_stmt(self, o: mypy.nodes.DelStmt, /) -> T: + raise NotImplementedError() - def visit_if_stmt(self, o: mypy.nodes.IfStmt) -> T: - pass + def visit_if_stmt(self, o: mypy.nodes.IfStmt, /) -> T: + raise NotImplementedError() - def visit_break_stmt(self, o: mypy.nodes.BreakStmt) -> T: - pass + def visit_break_stmt(self, o: mypy.nodes.BreakStmt, /) -> T: + raise NotImplementedError() - def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt) -> T: - pass + def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt, /) -> T: + raise NotImplementedError() - def visit_pass_stmt(self, o: mypy.nodes.PassStmt) -> T: - pass + def visit_pass_stmt(self, o: mypy.nodes.PassStmt, /) -> T: + raise NotImplementedError() - def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt) -> T: - pass + def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt, /) -> T: + raise NotImplementedError() - def visit_try_stmt(self, o: mypy.nodes.TryStmt) -> T: - pass + def visit_try_stmt(self, o: mypy.nodes.TryStmt, /) -> T: + raise NotImplementedError() - def visit_with_stmt(self, o: mypy.nodes.WithStmt) -> T: - pass + def visit_with_stmt(self, o: mypy.nodes.WithStmt, /) -> T: + raise NotImplementedError() - def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> T: - pass + def visit_match_stmt(self, o: mypy.nodes.MatchStmt, /) -> T: + raise NotImplementedError() - def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt) -> T: - pass + def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt, /) -> T: + raise NotImplementedError() # Expressions (default no-op implementation) - def visit_int_expr(self, o: mypy.nodes.IntExpr) -> T: - pass + def visit_int_expr(self, o: mypy.nodes.IntExpr, /) -> T: + raise NotImplementedError() - def visit_str_expr(self, o: mypy.nodes.StrExpr) -> T: - pass + def visit_str_expr(self, o: mypy.nodes.StrExpr, /) -> T: + raise NotImplementedError() - def visit_bytes_expr(self, o: mypy.nodes.BytesExpr) -> T: - pass + def visit_bytes_expr(self, o: mypy.nodes.BytesExpr, /) -> T: + raise NotImplementedError() - def visit_float_expr(self, o: mypy.nodes.FloatExpr) -> T: - pass + def visit_float_expr(self, o: mypy.nodes.FloatExpr, /) -> T: + raise NotImplementedError() - def visit_complex_expr(self, o: mypy.nodes.ComplexExpr) -> T: - pass + def visit_complex_expr(self, o: mypy.nodes.ComplexExpr, /) -> T: + raise NotImplementedError() - def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr) -> T: - pass + def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr, /) -> T: + raise NotImplementedError() - def visit_star_expr(self, o: mypy.nodes.StarExpr) -> T: - pass + def visit_star_expr(self, o: mypy.nodes.StarExpr, /) -> T: + raise NotImplementedError() - def visit_name_expr(self, o: mypy.nodes.NameExpr) -> T: - pass + def visit_name_expr(self, o: mypy.nodes.NameExpr, /) -> T: + raise NotImplementedError() - def visit_member_expr(self, o: mypy.nodes.MemberExpr) -> T: - pass + def visit_member_expr(self, o: mypy.nodes.MemberExpr, /) -> T: + raise NotImplementedError() - def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr) -> T: - pass + def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr, /) -> T: + raise NotImplementedError() - def visit_yield_expr(self, o: mypy.nodes.YieldExpr) -> T: - pass + def visit_yield_expr(self, o: mypy.nodes.YieldExpr, /) -> T: + raise NotImplementedError() - def visit_call_expr(self, o: mypy.nodes.CallExpr) -> T: - pass + def visit_call_expr(self, o: mypy.nodes.CallExpr, /) -> T: + raise NotImplementedError() - def visit_op_expr(self, o: mypy.nodes.OpExpr) -> T: - pass + def visit_op_expr(self, o: mypy.nodes.OpExpr, /) -> T: + raise NotImplementedError() - def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> T: - pass + def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr, /) -> T: + raise NotImplementedError() - def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> T: - pass + def visit_cast_expr(self, o: mypy.nodes.CastExpr, /) -> T: + raise NotImplementedError() - def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> T: - pass + def visit_type_form_expr(self, o: mypy.nodes.TypeFormExpr, /) -> T: + raise NotImplementedError() - def visit_reveal_expr(self, o: mypy.nodes.RevealExpr) -> T: - pass + def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr, /) -> T: + raise NotImplementedError() - def visit_super_expr(self, o: mypy.nodes.SuperExpr) -> T: - pass + def visit_reveal_expr(self, o: mypy.nodes.RevealExpr, /) -> T: + raise NotImplementedError() - def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr) -> T: - pass + def visit_super_expr(self, o: mypy.nodes.SuperExpr, /) -> T: + raise NotImplementedError() - def visit_unary_expr(self, o: mypy.nodes.UnaryExpr) -> T: - pass + def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr, /) -> T: + raise NotImplementedError() - def visit_list_expr(self, o: mypy.nodes.ListExpr) -> T: - pass + def visit_unary_expr(self, o: mypy.nodes.UnaryExpr, /) -> T: + raise NotImplementedError() - def visit_dict_expr(self, o: mypy.nodes.DictExpr) -> T: - pass + def visit_list_expr(self, o: mypy.nodes.ListExpr, /) -> T: + raise NotImplementedError() - def visit_tuple_expr(self, o: mypy.nodes.TupleExpr) -> T: - pass + def visit_dict_expr(self, o: mypy.nodes.DictExpr, /) -> T: + raise NotImplementedError() - def visit_set_expr(self, o: mypy.nodes.SetExpr) -> T: - pass + def visit_tuple_expr(self, o: mypy.nodes.TupleExpr, /) -> T: + raise NotImplementedError() - def visit_index_expr(self, o: mypy.nodes.IndexExpr) -> T: - pass + def visit_set_expr(self, o: mypy.nodes.SetExpr, /) -> T: + raise NotImplementedError() - def visit_type_application(self, o: mypy.nodes.TypeApplication) -> T: - pass + def visit_index_expr(self, o: mypy.nodes.IndexExpr, /) -> T: + raise NotImplementedError() - def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr) -> T: - pass + def visit_type_application(self, o: mypy.nodes.TypeApplication, /) -> T: + raise NotImplementedError() - def visit_list_comprehension(self, o: mypy.nodes.ListComprehension) -> T: - pass + def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr, /) -> T: + raise NotImplementedError() - def visit_set_comprehension(self, o: mypy.nodes.SetComprehension) -> T: - pass + def visit_list_comprehension(self, o: mypy.nodes.ListComprehension, /) -> T: + raise NotImplementedError() - def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension) -> T: - pass + def visit_set_comprehension(self, o: mypy.nodes.SetComprehension, /) -> T: + raise NotImplementedError() - def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr) -> T: - pass + def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension, /) -> T: + raise NotImplementedError() - def visit_slice_expr(self, o: mypy.nodes.SliceExpr) -> T: - pass + def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr, /) -> T: + raise NotImplementedError() - def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr) -> T: - pass + def visit_slice_expr(self, o: mypy.nodes.SliceExpr, /) -> T: + raise NotImplementedError() - def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr) -> T: - pass + def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr, /) -> T: + raise NotImplementedError() - def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> T: - pass + def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr, /) -> T: + raise NotImplementedError() - def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> T: - pass + def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr, /) -> T: + raise NotImplementedError() - def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> T: - pass + def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr, /) -> T: + raise NotImplementedError() - def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> T: - pass + def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr, /) -> T: + raise NotImplementedError() - def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr) -> T: - pass + def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr, /) -> T: + raise NotImplementedError() - def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr) -> T: - pass + def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr, /) -> T: + raise NotImplementedError() - def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr) -> T: - pass + def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr, /) -> T: + raise NotImplementedError() - def visit__promote_expr(self, o: mypy.nodes.PromoteExpr) -> T: - pass + def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr, /) -> T: + raise NotImplementedError() - def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> T: - pass + def visit__promote_expr(self, o: mypy.nodes.PromoteExpr, /) -> T: + raise NotImplementedError() - def visit_temp_node(self, o: mypy.nodes.TempNode) -> T: - pass + def visit_await_expr(self, o: mypy.nodes.AwaitExpr, /) -> T: + raise NotImplementedError() + + def visit_temp_node(self, o: mypy.nodes.TempNode, /) -> T: + raise NotImplementedError() # Patterns - def visit_as_pattern(self, o: mypy.patterns.AsPattern) -> T: - pass + def visit_as_pattern(self, o: mypy.patterns.AsPattern, /) -> T: + raise NotImplementedError() - def visit_or_pattern(self, o: mypy.patterns.OrPattern) -> T: - pass + def visit_or_pattern(self, o: mypy.patterns.OrPattern, /) -> T: + raise NotImplementedError() - def visit_value_pattern(self, o: mypy.patterns.ValuePattern) -> T: - pass + def visit_value_pattern(self, o: mypy.patterns.ValuePattern, /) -> T: + raise NotImplementedError() - def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern) -> T: - pass + def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern, /) -> T: + raise NotImplementedError() - def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern) -> T: - pass + def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern, /) -> T: + raise NotImplementedError() - def visit_starred_pattern(self, o: mypy.patterns.StarredPattern) -> T: - pass + def visit_starred_pattern(self, o: mypy.patterns.StarredPattern, /) -> T: + raise NotImplementedError() - def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern) -> T: - pass + def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern, /) -> T: + raise NotImplementedError() - def visit_class_pattern(self, o: mypy.patterns.ClassPattern) -> T: - pass + def visit_class_pattern(self, o: mypy.patterns.ClassPattern, /) -> T: + raise NotImplementedError() 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/mypy_self_check.ini b/mypy_self_check.ini index 7f1f9689a7573..f4f8d2d0e08b2 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -1,17 +1,16 @@ [mypy] strict = True +local_partial_types = True disallow_any_unimported = True show_traceback = True pretty = True always_false = MYPYC plugins = mypy.plugins.proper_plugin -python_version = 3.8 -exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ +python_version = 3.9 +exclude = mypy/typeshed/|mypyc/test-data/ enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True - -[mypy-mypy.visitor] -# See docstring for NodeVisitor for motivation. -disable_error_code = empty-body +warn_unreachable = True +fixed_format_cache = True diff --git a/mypyc/README.md b/mypyc/README.md index cb6cf5bf225c6..720e648757356 100644 --- a/mypyc/README.md +++ b/mypyc/README.md @@ -1,133 +1,12 @@ mypyc: Mypy to Python C Extension Compiler ========================================== -**NOTE: We are in the process of moving the mypyc README to the** -**[mypyc repository](https://github.com/mypyc/mypyc)** +For the mypyc README, refer to the [mypyc repository](https://github.com/mypyc/mypyc). The mypyc +repository also contains the mypyc issue tracker. All mypyc code lives +here in the mypy repository. -**This may be out of date!** +Source code for the mypyc user documentation lives under +[mypyc/doc](./doc). -Mypyc is a compiler that compiles mypy-annotated, statically typed -Python modules into CPython C extensions. Currently our primary focus -is on making mypy faster through compilation -- the default mypy wheels -are compiled with mypyc. Compiled mypy is about 4x faster than -without compilation. - -Mypyc compiles what is essentially a Python language variant using "strict" -semantics. This means (among some other things): - - * Most type annotations are enforced at runtime (raising ``TypeError`` on mismatch) - - * Classes are compiled into extension classes without ``__dict__`` - (much, but not quite, like if they used ``__slots__``) - - * Monkey patching doesn't work - - * Instance attributes won't fall back to class attributes if undefined - - * Also there are still a bunch of bad bugs and unsupported features :) - -Compiled modules can import arbitrary Python modules, and compiled modules -can be used from other Python modules. Typically mypyc is used to only -compile modules that contain performance bottlenecks. - -You can run compiled modules also as normal, interpreted Python -modules, since mypyc targets valid Python code. This means that -all Python developer tools and debuggers can be used. - -macOS Requirements ------------------- - -* macOS Sierra or later - -* Xcode command line tools - -* Python 3.5+ from python.org (other versions are untested) - -Linux Requirements ------------------- - -* A recent enough C/C++ build environment - -* Python 3.5+ - -Windows Requirements --------------------- - -* Windows has been tested with Windows 10 and MSVC 2017. - -* Python 3.5+ - -Quick Start for Contributors ----------------------------- - -First clone the mypy git repository: - - $ git clone https://github.com/python/mypy.git - $ cd mypy - -Optionally create a virtualenv (recommended): - - $ python3 -m venv - $ source /bin/activate - -Then install the dependencies: - - $ python3 -m pip install -r test-requirements.txt - -Now you can run the tests: - - $ pytest -q mypyc - -Look at the [issue tracker](https://github.com/mypyc/mypyc/issues) -for things to work on. Please express your interest in working on an -issue by adding a comment before doing any significant work, since -there is a risk of duplicate work. - -Note that the issue tracker is hosted on the mypyc GitHub project, not -with mypy itself. - -Documentation -------------- - -We have some [developer documentation](doc/dev-intro.md). - -Development Status and Roadmap ------------------------------- - -These are the current planned major milestones: - -1. [DONE] Support a smallish but useful Python subset. Focus on compiling - single modules, while the rest of the program is interpreted and does not - need to be type checked. - -2. [DONE] Support compiling multiple modules as a single compilation unit (or - dynamic linking of compiled modules). Without this inter-module - calls will use slower Python-level objects, wrapper functions and - Python namespaces. - -3. [DONE] Mypyc can compile mypy. - -4. [DONE] Optimize some important performance bottlenecks. - -5. [PARTIALLY DONE] Generate useful errors for code that uses unsupported Python - features instead of crashing or generating bad code. - -6. [DONE] Release a version of mypy that includes a compiled mypy. - -7. - 1. More feature/compatibility work. (100% compatibility with Python is distinctly - an anti-goal, but more than we have now is a good idea.) - 2. [DONE] Support compiling Black, which is a prominent tool that could benefit - and has maintainer buy-in. - (Let us know if you maintain another Python tool or library and are - interested in working with us on this!) - 3. More optimization! Code size reductions in particular are likely to - be valuable and will speed up mypyc compilation. - -8. We'll see! Adventure is out there! - -Future ------- - -We have some ideas for -[future improvements and optimizations](doc/future.md). +Mypyc welcomes new contributors! Refer to our +[developer documentation](./doc/dev-intro.md) for more information. diff --git a/mypyc/__main__.py b/mypyc/__main__.py index a3b9d21bc65a7..9b3973710efac 100644 --- a/mypyc/__main__.py +++ b/mypyc/__main__.py @@ -23,8 +23,15 @@ from setuptools import setup from mypyc.build import mypycify -setup(name='mypyc_output', - ext_modules=mypycify({}, opt_level="{}", debug_level="{}"), +setup( + name='mypyc_output', + ext_modules=mypycify( + {}, + opt_level="{}", + debug_level="{}", + strict_dunder_typing={}, + log_trace={}, + ), ) """ @@ -38,10 +45,18 @@ 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"))) + # If enabled, compiled code writes a sampled log of executed ops (or events) to + # mypyc_trace.txt. + log_trace = bool(int(os.getenv("MYPYC_LOG_TRACE", "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, log_trace + ) + ) # 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/attrdefined.py b/mypyc/analysis/attrdefined.py index 350158246cdb1..1dfd33630f1c0 100644 --- a/mypyc/analysis/attrdefined.py +++ b/mypyc/analysis/attrdefined.py @@ -63,7 +63,7 @@ def foo(self) -> int: from __future__ import annotations -from typing import Final, Set, Tuple +from typing import Final from mypyc.analysis.dataflow import ( CFG, @@ -138,6 +138,7 @@ def analyze_always_defined_attrs_in_class(cl: ClassIR, seen: set[ClassIR]) -> No or cl.builtin_base is not None or cl.children is None or cl.is_serializable() + or cl.has_method("__new__") ): # Give up -- we can't enforce that attributes are always defined. return @@ -176,7 +177,7 @@ def analyze_always_defined_attrs_in_class(cl: ClassIR, seen: set[ClassIR]) -> No m.blocks, self_reg, maybe_defined, dirty ) - mark_attr_initialiation_ops(m.blocks, self_reg, maybe_defined, dirty) + mark_attr_initialization_ops(m.blocks, self_reg, maybe_defined, dirty) # Check if __init__ can run unpredictable code (leak 'self'). any_dirty = False @@ -260,7 +261,7 @@ def find_sometimes_defined_attributes( return attrs -def mark_attr_initialiation_ops( +def mark_attr_initialization_ops( blocks: list[BasicBlock], self_reg: Register, maybe_defined: AnalysisResult[str], @@ -279,13 +280,13 @@ def mark_attr_initialiation_ops( op.mark_as_initializer() -GenAndKill = Tuple[Set[str], Set[str]] +GenAndKill = tuple[set[str], set[str]] def attributes_initialized_by_init_call(op: Call) -> set[str]: """Calculate attributes that are always initialized by a super().__init__ call.""" self_type = op.fn.sig.args[0].type - assert isinstance(self_type, RInstance) + assert isinstance(self_type, RInstance), self_type cl = self_type.class_ir return {a for base in cl.mro for a in base.attributes if base.is_always_defined(a)} @@ -293,7 +294,7 @@ def attributes_initialized_by_init_call(op: Call) -> set[str]: def attributes_maybe_initialized_by_init_call(op: Call) -> set[str]: """Calculate attributes that may be initialized by a super().__init__ call.""" self_type = op.fn.sig.args[0].type - assert isinstance(self_type, RInstance) + assert isinstance(self_type, RInstance), self_type cl = self_type.class_ir return attributes_initialized_by_init_call(op) | cl._sometimes_initialized_attrs @@ -421,7 +422,7 @@ def detect_undefined_bitmap(cl: ClassIR, seen: set[ClassIR]) -> None: return seen.add(cl) for base in cl.base_mro[1:]: - detect_undefined_bitmap(cl, seen) + detect_undefined_bitmap(base, seen) if len(cl.base_mro) > 1: cl.bitmap_attrs.extend(cl.base_mro[1].bitmap_attrs) diff --git a/mypyc/analysis/capsule_deps.py b/mypyc/analysis/capsule_deps.py new file mode 100644 index 0000000000000..ada42ee03f289 --- /dev/null +++ b/mypyc/analysis/capsule_deps.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.ops import CallC, PrimitiveOp + + +def find_implicit_capsule_dependencies(fn: FuncIR) -> set[str] | None: + """Find implicit dependencies on capsules that need to be imported. + + Using primitives or types defined in librt submodules such as "librt.base64" + requires a capsule import. + + Note that a module can depend on a librt module even if it doesn't explicitly + import it, for example via re-exported names or via return types of functions + defined in other modules. + """ + deps: set[str] | None = None + for block in fn.blocks: + for op in block.ops: + # TODO: Also determine implicit type object dependencies (e.g. cast targets) + if isinstance(op, CallC) and op.capsule is not None: + if deps is None: + deps = set() + deps.add(op.capsule) + else: + assert not isinstance(op, PrimitiveOp), "Lowered IR is expected" + return deps diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 9babf860fb311..827c70a0eb4db 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -3,9 +3,9 @@ from __future__ import annotations from abc import abstractmethod -from typing import Dict, Generic, Iterable, Iterator, Set, Tuple, TypeVar +from collections.abc import Iterable, Iterator +from typing import Generic, TypeVar -from mypyc.ir.func_ir import all_values from mypyc.ir.ops import ( Assign, AssignMulti, @@ -17,6 +17,7 @@ Cast, ComparisonOp, ControlOp, + DecRef, Extend, Float, FloatComparisonOp, @@ -25,6 +26,7 @@ GetAttr, GetElementPtr, Goto, + IncRef, InitStatic, Integer, IntOp, @@ -43,12 +45,14 @@ RegisterOp, Return, SetAttr, + SetElement, SetMem, Truncate, TupleGet, TupleSet, Unborrow, Unbox, + Undef, Unreachable, Value, ) @@ -77,12 +81,11 @@ def __str__(self) -> str: return f"exits: {exits}\nsucc: {self.succ}\npred: {self.pred}" -def get_cfg(blocks: list[BasicBlock]) -> CFG: +def get_cfg(blocks: list[BasicBlock], *, use_yields: bool = False) -> CFG: """Calculate basic block control-flow graph. - The result is a dictionary like this: - - basic block index -> (successors blocks, predecesssor blocks) + If use_yields is set, then we treat returns inserted by yields as gotos + instead of exits. """ succ_map = {} pred_map: dict[BasicBlock, list[BasicBlock]] = {} @@ -92,7 +95,10 @@ def get_cfg(blocks: list[BasicBlock]) -> CFG: isinstance(op, ControlOp) for op in block.ops[:-1] ), "Control-flow ops must be at the end of blocks" - succ = list(block.terminator.targets()) + if use_yields and isinstance(block.terminator, Return) and block.terminator.yield_target: + succ = [block.terminator.yield_target] + else: + succ = list(block.terminator.targets()) if not succ: exits.add(block) @@ -155,7 +161,7 @@ def cleanup_cfg(blocks: list[BasicBlock]) -> None: T = TypeVar("T") -AnalysisDict = Dict[Tuple[BasicBlock, int], Set[T]] +AnalysisDict = dict[tuple[BasicBlock, int], set[T]] class AnalysisResult(Generic[T]): @@ -167,7 +173,7 @@ def __str__(self) -> str: return f"before: {self.before}\nafter: {self.after}\n" -GenAndKill = Tuple[Set[T], Set[T]] +GenAndKill = tuple[set[T], set[T]] class BaseAnalysisVisitor(OpVisitor[GenAndKill[T]]): @@ -268,6 +274,9 @@ def visit_load_mem(self, op: LoadMem) -> GenAndKill[T]: def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill[T]: return self.visit_register_op(op) + def visit_set_element(self, op: SetElement) -> GenAndKill[T]: + return self.visit_register_op(op) + def visit_load_address(self, op: LoadAddress) -> GenAndKill[T]: return self.visit_register_op(op) @@ -437,31 +446,10 @@ 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(): - if not isinstance(source, (Integer, Float)): + if not isinstance(source, (Integer, Float, Undef)): result.add(source) return result @@ -495,6 +483,12 @@ def visit_assign_multi(self, op: AssignMulti) -> GenAndKill[Value]: def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]: return non_trivial_sources(op), set() + def visit_inc_ref(self, op: IncRef) -> GenAndKill[Value]: + return set(), set() + + def visit_dec_ref(self, op: DecRef) -> GenAndKill[Value]: + return set(), set() + def analyze_live_regs(blocks: list[BasicBlock], cfg: CFG) -> AnalysisResult[Value]: """Calculate live registers at each CFG location. @@ -563,7 +557,7 @@ def run_analysis( # Set up initial state for worklist algorithm. worklist = list(blocks) if not backward: - worklist = worklist[::-1] # Reverse for a small performance improvement + worklist.reverse() # Reverse for a small performance improvement workset = set(worklist) before: dict[BasicBlock, set[T]] = {} after: dict[BasicBlock, set[T]] = {} diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py index 88737ac208de2..6980c9cee4191 100644 --- a/mypyc/analysis/ircheck.py +++ b/mypyc/analysis/ircheck.py @@ -17,6 +17,7 @@ ControlOp, DecRef, Extend, + Float, FloatComparisonOp, FloatNeg, FloatOp, @@ -42,17 +43,20 @@ Register, Return, SetAttr, + SetElement, SetMem, Truncate, TupleGet, TupleSet, Unborrow, Unbox, + Undef, Unreachable, Value, ) from mypyc.ir.pprint import format_func from mypyc.ir.rtypes import ( + KNOWN_NATIVE_TYPES, RArray, RInstance, RPrimitive, @@ -148,7 +152,7 @@ def check_op_sources_valid(fn: FuncIR) -> list[FnError]: for block in fn.blocks: for op in block.ops: for source in op.sources(): - if isinstance(source, Integer): + if isinstance(source, (Integer, Float, Undef)): pass elif isinstance(source, Op): if source not in valid_ops: @@ -178,7 +182,7 @@ def check_op_sources_valid(fn: FuncIR) -> list[FnError]: set_rprimitive.name, tuple_rprimitive.name, range_rprimitive.name, -} +} | set(KNOWN_NATIVE_TYPES) def can_coerce_to(src: RType, dest: RType) -> bool: @@ -423,6 +427,9 @@ def visit_set_mem(self, op: SetMem) -> None: def visit_get_element_ptr(self, op: GetElementPtr) -> None: pass + def visit_set_element(self, op: SetElement) -> None: + pass + def visit_load_address(self, op: LoadAddress) -> None: pass diff --git a/mypyc/analysis/selfleaks.py b/mypyc/analysis/selfleaks.py index 5d89a9bfc7c66..8f46cbe3312bc 100644 --- a/mypyc/analysis/selfleaks.py +++ b/mypyc/analysis/selfleaks.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Set, Tuple - from mypyc.analysis.dataflow import CFG, MAYBE_ANALYSIS, AnalysisResult, run_analysis from mypyc.ir.ops import ( Assign, @@ -37,6 +35,7 @@ RegisterOp, Return, SetAttr, + SetElement, SetMem, Truncate, TupleGet, @@ -47,7 +46,7 @@ ) from mypyc.ir.rtypes import RInstance -GenAndKill = Tuple[Set[None], Set[None]] +GenAndKill = tuple[set[None], set[None]] CLEAN: GenAndKill = (set(), set()) DIRTY: GenAndKill = ({None}, {None}) @@ -94,7 +93,7 @@ def visit_call(self, op: Call) -> GenAndKill: fn = op.fn if fn.class_name and fn.name == "__init__": self_type = op.fn.sig.args[0].type - assert isinstance(self_type, RInstance) + assert isinstance(self_type, RInstance), self_type cl = self_type.class_ir if not cl.init_self_leak: return CLEAN @@ -183,6 +182,9 @@ def visit_load_mem(self, op: LoadMem) -> GenAndKill: def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill: return CLEAN + def visit_set_element(self, op: SetElement) -> GenAndKill: + return CLEAN + def visit_load_address(self, op: LoadAddress) -> GenAndKill: return CLEAN diff --git a/mypyc/annotate.py b/mypyc/annotate.py new file mode 100644 index 0000000000000..bc282fc3ea6c1 --- /dev/null +++ b/mypyc/annotate.py @@ -0,0 +1,472 @@ +"""Generate source code formatted as HTML, with bottlenecks annotated and highlighted. + +Various heuristics are used to detect common issues that cause slower than +expected performance. +""" + +from __future__ import annotations + +import os.path +import sys +from html import escape +from typing import Final + +from mypy.build import BuildResult +from mypy.nodes import ( + AssignmentStmt, + CallExpr, + ClassDef, + Decorator, + DictionaryComprehension, + Expression, + ForStmt, + FuncDef, + GeneratorExpr, + IndexExpr, + LambdaExpr, + MemberExpr, + MypyFile, + NamedTupleExpr, + NameExpr, + NewTypeExpr, + Node, + OpExpr, + RefExpr, + TupleExpr, + TypedDictExpr, + TypeInfo, + TypeVarExpr, + Var, + WithStmt, +) +from mypy.traverser import TraverserVisitor +from mypy.types import AnyType, Instance, ProperType, Type, TypeOfAny, get_proper_type +from mypy.util import FancyFormatter +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.module_ir import ModuleIR +from mypyc.ir.ops import CallC, LoadLiteral, LoadStatic, Value +from mypyc.irbuild.mapper import Mapper + + +class Annotation: + """HTML annotation for compiled source code""" + + def __init__(self, message: str, priority: int = 1) -> None: + # Message as HTML that describes an issue and/or how to fix it. + # Multiple messages on a line may be concatenated. + self.message = message + # If multiple annotations are generated for a single line, only report + # the highest-priority ones. Some use cases generate multiple annotations, + # and this can be used to reduce verbosity by hiding the lower-priority + # ones. + self.priority = priority + + +op_hints: Final = { + "PyNumber_Add": Annotation('Generic "+" operation.'), + "PyNumber_Subtract": Annotation('Generic "-" operation.'), + "PyNumber_Multiply": Annotation('Generic "*" operation.'), + "PyNumber_TrueDivide": Annotation('Generic "/" operation.'), + "PyNumber_FloorDivide": Annotation('Generic "//" operation.'), + "PyNumber_Positive": Annotation('Generic unary "+" operation.'), + "PyNumber_Negative": Annotation('Generic unary "-" operation.'), + "PyNumber_And": Annotation('Generic "&" operation.'), + "PyNumber_Or": Annotation('Generic "|" operation.'), + "PyNumber_Xor": Annotation('Generic "^" operation.'), + "PyNumber_Lshift": Annotation('Generic "<<" operation.'), + "PyNumber_Rshift": Annotation('Generic ">>" operation.'), + "PyNumber_Invert": Annotation('Generic "~" operation.'), + "PyObject_Call": Annotation("Generic call operation."), + "PyObject_CallObject": Annotation("Generic call operation."), + "PyObject_RichCompare": Annotation("Generic comparison operation."), + "PyObject_GetItem": Annotation("Generic indexing operation."), + "PyObject_SetItem": Annotation("Generic indexed assignment."), +} + +stdlib_hints: Final = { + "functools.partial": Annotation( + '"functools.partial" is inefficient in compiled code.', priority=3 + ), + "itertools.chain": Annotation( + '"itertools.chain" is inefficient in compiled code (hint: replace with for loops).', + priority=3, + ), + "itertools.groupby": Annotation( + '"itertools.groupby" is inefficient in compiled code.', priority=3 + ), + "itertools.islice": Annotation( + '"itertools.islice" is inefficient in compiled code (hint: replace with for loop over index range).', + priority=3, + ), + "copy.deepcopy": Annotation( + '"copy.deepcopy" tends to be slow. Make a shallow copy if possible.', priority=2 + ), +} + +CSS = """\ +.collapsible { + cursor: pointer; +} + +.content { + display: block; + margin-top: 10px; + margin-bottom: 10px; +} + +.hint { + display: inline; + border: 1px solid #ccc; + padding: 5px; +} +""" + +JS = """\ +document.querySelectorAll('.collapsible').forEach(function(collapsible) { + collapsible.addEventListener('click', function() { + const content = this.nextElementSibling; + if (content.style.display === 'none') { + content.style.display = 'block'; + } else { + content.style.display = 'none'; + } + }); +}); +""" + + +class AnnotatedSource: + """Annotations for a single compiled source file.""" + + def __init__(self, path: str, annotations: dict[int, list[Annotation]]) -> None: + self.path = path + self.annotations = annotations + + +def generate_annotated_html( + html_fnam: str, result: BuildResult, modules: dict[str, ModuleIR], mapper: Mapper +) -> None: + annotations = [] + for mod, mod_ir in modules.items(): + path = result.graph[mod].path + tree = result.graph[mod].tree + assert tree is not None + annotations.append( + generate_annotations(path or "", tree, mod_ir, result.types, mapper) + ) + html = generate_html_report(annotations) + with open(html_fnam, "w") as f: + f.write(html) + + formatter = FancyFormatter(sys.stdout, sys.stderr, False) + formatted = formatter.style(os.path.abspath(html_fnam), "none", underline=True, bold=True) + print(f"\nWrote {formatted} -- open in browser to view\n") + + +def generate_annotations( + path: str, tree: MypyFile, ir: ModuleIR, type_map: dict[Expression, Type], mapper: Mapper +) -> AnnotatedSource: + anns = {} + for func_ir in ir.functions: + anns.update(function_annotations(func_ir, tree)) + visitor = ASTAnnotateVisitor(type_map, mapper) + for defn in tree.defs: + defn.accept(visitor) + anns.update(visitor.anns) + for line in visitor.ignored_lines: + if line in anns: + del anns[line] + return AnnotatedSource(path, anns) + + +def function_annotations(func_ir: FuncIR, tree: MypyFile) -> dict[int, list[Annotation]]: + """Generate annotations based on mypyc IR.""" + # TODO: check if func_ir.line is -1 + anns: dict[int, list[Annotation]] = {} + for block in func_ir.blocks: + for op in block.ops: + if isinstance(op, CallC): + name = op.function_name + ann: str | Annotation | None = None + if name == "CPyObject_GetAttr": + attr_name = get_str_literal(op.args[1]) + if attr_name in ("__prepare__", "GeneratorExit", "StopIteration"): + # These attributes are internal to mypyc/CPython, and/or accessed + # implicitly in generated code. The user has little control over + # them. + ann = None + elif attr_name: + ann = f'Get non-native attribute "{attr_name}".' + else: + ann = "Dynamic attribute lookup." + elif name == "PyObject_SetAttr": + attr_name = get_str_literal(op.args[1]) + if attr_name == "__mypyc_attrs__": + # This is set implicitly and can't be avoided. + ann = None + elif attr_name: + ann = f'Set non-native attribute "{attr_name}".' + else: + ann = "Dynamic attribute set." + elif name == "PyObject_VectorcallMethod": + method_name = get_str_literal(op.args[0]) + if method_name: + ann = f'Call non-native method "{method_name}" (it may be defined in a non-native class, or decorated).' + else: + ann = "Dynamic method call." + elif name in op_hints: + ann = op_hints[name] + elif name in ("CPyDict_GetItem", "CPyDict_SetItem"): + if ( + isinstance(op.args[0], LoadStatic) + and isinstance(op.args[1], LoadLiteral) + and func_ir.name != "__top_level__" + ): + load = op.args[0] + name = str(op.args[1].value) + sym = tree.names.get(name) + if ( + sym + and sym.node + and load.namespace == "static" + and load.identifier == "globals" + ): + if sym.node.fullname in stdlib_hints: + ann = stdlib_hints[sym.node.fullname] + elif isinstance(sym.node, Var): + ann = ( + f'Access global "{name}" through namespace ' + + "dictionary (hint: access is faster if you can make it Final)." + ) + else: + ann = f'Access "{name}" through global namespace dictionary.' + if ann: + if isinstance(ann, str): + ann = Annotation(ann) + anns.setdefault(op.line, []).append(ann) + return anns + + +class ASTAnnotateVisitor(TraverserVisitor): + """Generate annotations from mypy AST and inferred types.""" + + def __init__(self, type_map: dict[Expression, Type], mapper: Mapper) -> None: + self.anns: dict[int, list[Annotation]] = {} + self.ignored_lines: set[int] = set() + self.func_depth = 0 + self.type_map = type_map + self.mapper = mapper + + def visit_func_def(self, o: FuncDef, /) -> None: + if self.func_depth > 0: + self.annotate( + o, + "A nested function object is allocated each time statement is executed. " + + "A module-level function would be faster.", + ) + self.func_depth += 1 + super().visit_func_def(o) + self.func_depth -= 1 + + def visit_for_stmt(self, o: ForStmt, /) -> None: + self.check_iteration([o.expr], "For loop") + super().visit_for_stmt(o) + + def visit_dictionary_comprehension(self, o: DictionaryComprehension, /) -> None: + self.check_iteration(o.sequences, "Comprehension") + super().visit_dictionary_comprehension(o) + + def visit_generator_expr(self, o: GeneratorExpr, /) -> None: + self.check_iteration(o.sequences, "Comprehension or generator") + super().visit_generator_expr(o) + + def check_iteration(self, expressions: list[Expression], kind: str) -> None: + for expr in expressions: + typ = self.get_type(expr) + if isinstance(typ, AnyType): + self.annotate(expr, f'{kind} uses generic operations (iterable has type "Any").') + elif isinstance(typ, Instance) and typ.type.fullname in ( + "typing.Iterable", + "typing.Iterator", + "typing.Sequence", + "typing.MutableSequence", + ): + self.annotate( + expr, + f'{kind} uses generic operations (iterable has the abstract type "{typ.type.fullname}").', + ) + + def visit_class_def(self, o: ClassDef, /) -> None: + super().visit_class_def(o) + if self.func_depth == 0: + # Don't complain about base classes at top level + for base in o.base_type_exprs: + self.ignored_lines.add(base.line) + + for s in o.defs.body: + if isinstance(s, AssignmentStmt): + # Don't complain about attribute initializers + self.ignored_lines.add(s.line) + elif isinstance(s, Decorator): + # Don't complain about decorator definitions that generate some + # dynamic operations. This is a bit heavy-handed. + self.ignored_lines.add(s.func.line) + + def visit_with_stmt(self, o: WithStmt, /) -> None: + for expr in o.expr: + if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr): + node = expr.callee.node + if isinstance(node, Decorator): + if any( + isinstance(d, RefExpr) + and d.node + and d.node.fullname == "contextlib.contextmanager" + for d in node.decorators + ): + self.annotate( + expr, + f'"{node.name}" uses @contextmanager, which is slow ' + + "in compiled code. Use a native class with " + + '"__enter__" and "__exit__" methods instead.', + priority=3, + ) + super().visit_with_stmt(o) + + def visit_assignment_stmt(self, o: AssignmentStmt, /) -> None: + special_form = False + if self.func_depth == 0: + analyzed: Expression | None = o.rvalue + if isinstance(o.rvalue, (CallExpr, IndexExpr, OpExpr)): + analyzed = o.rvalue.analyzed + if o.is_alias_def or isinstance( + analyzed, (TypeVarExpr, NamedTupleExpr, TypedDictExpr, NewTypeExpr) + ): + special_form = True + if special_form: + # TODO: Ignore all lines if multi-line + self.ignored_lines.add(o.line) + super().visit_assignment_stmt(o) + + def visit_name_expr(self, o: NameExpr, /) -> None: + if ann := stdlib_hints.get(o.fullname): + self.annotate(o, ann) + + def visit_member_expr(self, o: MemberExpr, /) -> None: + super().visit_member_expr(o) + if ann := stdlib_hints.get(o.fullname): + self.annotate(o, ann) + + def visit_call_expr(self, o: CallExpr, /) -> None: + super().visit_call_expr(o) + if ( + isinstance(o.callee, RefExpr) + and o.callee.fullname == "builtins.isinstance" + and len(o.args) == 2 + ): + arg = o.args[1] + self.check_isinstance_arg(arg) + elif isinstance(o.callee, RefExpr) and isinstance(o.callee.node, TypeInfo): + info = o.callee.node + class_ir = self.mapper.type_to_ir.get(info) + if (class_ir and not class_ir.is_ext_class) or ( + class_ir is None and not info.fullname.startswith("builtins.") + ): + self.annotate( + o, f'Creating an instance of non-native class "{info.name}" ' + "is slow.", 2 + ) + elif class_ir and class_ir.is_augmented: + self.annotate( + o, + f'Class "{info.name}" is only partially native, and ' + + "constructing an instance is slow.", + 2, + ) + elif isinstance(o.callee, RefExpr) and isinstance(o.callee.node, Decorator): + decorator = o.callee.node + if self.mapper.is_native_ref_expr(o.callee): + self.annotate( + o, + f'Calling a decorated function ("{decorator.name}") is inefficient, even if it\'s native.', + 2, + ) + + def check_isinstance_arg(self, arg: Expression) -> None: + if isinstance(arg, RefExpr): + if isinstance(arg.node, TypeInfo) and arg.node.is_protocol: + self.annotate( + arg, f'Expensive isinstance() check against protocol "{arg.node.name}".' + ) + elif isinstance(arg, TupleExpr): + for item in arg.items: + self.check_isinstance_arg(item) + + def visit_lambda_expr(self, o: LambdaExpr, /) -> None: + self.annotate( + o, + "A new object is allocated for lambda each time it is evaluated. " + + "A module-level function would be faster.", + ) + super().visit_lambda_expr(o) + + def annotate(self, o: Node, ann: str | Annotation, priority: int = 1) -> None: + if isinstance(ann, str): + ann = Annotation(ann, priority=priority) + self.anns.setdefault(o.line, []).append(ann) + + def get_type(self, e: Expression) -> ProperType: + t = self.type_map.get(e) + if t: + return get_proper_type(t) + return AnyType(TypeOfAny.unannotated) + + +def get_str_literal(v: Value) -> str | None: + if isinstance(v, LoadLiteral) and isinstance(v.value, str): + return v.value + return None + + +def get_max_prio(anns: list[Annotation]) -> list[Annotation]: + max_prio = max(a.priority for a in anns) + return [a for a in anns if a.priority == max_prio] + + +def generate_html_report(sources: list[AnnotatedSource]) -> str: + html = [] + html.append("\n\n") + html.append(f"") + html.append("\n") + html.append("\n") + for src in sources: + html.append(f"

{src.path}

\n") + html.append("
")
+        src_anns = src.annotations
+        with open(src.path) as f:
+            lines = f.readlines()
+        for i, s in enumerate(lines):
+            s = escape(s)
+            line = i + 1
+            linenum = "%5d" % line
+            if line in src_anns:
+                anns = get_max_prio(src_anns[line])
+                ann_strs = [a.message for a in anns]
+                hint = " ".join(ann_strs)
+                s = colorize_line(linenum, s, hint_html=hint)
+            else:
+                s = linenum + "  " + s
+            html.append(s)
+        html.append("
") + + html.append("") + + html.append("\n") + return "".join(html) + + +def colorize_line(linenum: str, s: str, hint_html: str) -> str: + hint_prefix = " " * len(linenum) + " " + line_span = f'
{linenum} {s}
' + hint_div = f'
{hint_prefix}
{hint_html}
' + return f"{line_span}{hint_div}" diff --git a/mypyc/build.py b/mypyc/build.py index 485803acba46f..69ef6c3bc435d 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -25,21 +25,84 @@ import re import sys import time -from typing import TYPE_CHECKING, Any, Dict, Iterable, NoReturn, Union, cast +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any, NamedTuple, NoReturn, Union, cast +import mypyc.build_setup # noqa: F401 from mypy.build import BuildSource from mypy.errors import CompileError from mypy.fscache import FileSystemCache from mypy.main import process_options from mypy.options import Options from mypy.util import write_junit_xml +from mypyc.annotate import generate_annotated_html from mypyc.codegen import emitmodule -from mypyc.common import RUNTIME_C_FILES, shared_lib_name +from mypyc.common import IS_FREE_THREADED, RUNTIME_C_FILES, shared_lib_name from mypyc.errors import Errors from mypyc.ir.pprint import format_modules from mypyc.namegen import exported_name from mypyc.options import CompilerOptions + +class ModDesc(NamedTuple): + module: str + c_files: list[str] + other_files: list[str] + include_dirs: list[str] + + +LIBRT_MODULES = [ + ModDesc("librt.internal", ["librt_internal.c"], [], []), + ModDesc( + "librt.base64", + [ + "librt_base64.c", + "base64/lib.c", + "base64/codec_choose.c", + "base64/tables/tables.c", + "base64/arch/generic/codec.c", + "base64/arch/ssse3/codec.c", + "base64/arch/sse41/codec.c", + "base64/arch/sse42/codec.c", + "base64/arch/avx/codec.c", + "base64/arch/avx2/codec.c", + "base64/arch/avx512/codec.c", + "base64/arch/neon32/codec.c", + "base64/arch/neon64/codec.c", + ], + [ + "base64/arch/avx/enc_loop_asm.c", + "base64/arch/avx2/enc_loop.c", + "base64/arch/avx2/enc_loop_asm.c", + "base64/arch/avx2/enc_reshuffle.c", + "base64/arch/avx2/enc_translate.c", + "base64/arch/avx2/dec_loop.c", + "base64/arch/avx2/dec_reshuffle.c", + "base64/arch/generic/32/enc_loop.c", + "base64/arch/generic/64/enc_loop.c", + "base64/arch/generic/32/dec_loop.c", + "base64/arch/generic/enc_head.c", + "base64/arch/generic/enc_tail.c", + "base64/arch/generic/dec_head.c", + "base64/arch/generic/dec_tail.c", + "base64/arch/ssse3/dec_reshuffle.c", + "base64/arch/ssse3/dec_loop.c", + "base64/arch/ssse3/enc_loop_asm.c", + "base64/arch/ssse3/enc_translate.c", + "base64/arch/ssse3/enc_reshuffle.c", + "base64/arch/ssse3/enc_loop.c", + "base64/arch/neon64/dec_loop.c", + "base64/arch/neon64/enc_loop_asm.c", + "base64/codecs.h", + "base64/env.h", + "base64/tables/tables.h", + "base64/tables/table_dec_32bit.h", + "base64/tables/table_enc_12bit.h", + ], + ["base64"], + ), +] + try: # Import setuptools so that it monkey-patch overrides distutils import setuptools @@ -87,7 +150,7 @@ def setup_mypycify_vars() -> None: # There has to be a better approach to this. # The vars can contain ints but we only work with str ones - vars = cast(Dict[str, str], sysconfig.get_config_vars()) + vars = cast(dict[str, str], sysconfig.get_config_vars()) if sys.platform == "darwin": # Disable building 32-bit binaries, since we generate too much code # for a 32-bit Mach-O object. There has to be a better way to do this. @@ -174,9 +237,15 @@ def generate_c_extension_shim( cname = "%s.c" % full_module_name.replace(".", os.sep) cpath = os.path.join(dir_name, cname) + if IS_FREE_THREADED: + # We use multi-phase init in free-threaded builds to enable free threading. + shim_name = "module_shim_no_gil_multiphase.tmpl" + else: + shim_name = "module_shim.tmpl" + # We load the C extension shim template from a file. # (So that the file could be reused as a bazel template also.) - with open(os.path.join(include_dir(), "module_shim.tmpl")) as f: + with open(os.path.join(include_dir(), shim_name)) as f: shim_template = f.read() write_file( @@ -240,7 +309,7 @@ def generate_c( print(f"Parsed and typechecked in {t1 - t0:.3f}s") errors = Errors(options) - modules, ctext = emitmodule.compile_modules_to_c( + modules, ctext, mapper = emitmodule.compile_modules_to_c( result, compiler_options=compiler_options, errors=errors, groups=groups ) t2 = time.time() @@ -252,6 +321,9 @@ def generate_c( if compiler_options.verbose: print(f"Compiled to C in {t2 - t1:.3f}s") + if options.mypyc_annotation_file: + generate_annotated_html(options.mypyc_annotation_file, result, modules, mapper) + return ctext, "\n".join(format_modules(modules)) @@ -265,12 +337,12 @@ def build_using_shared_lib( ) -> list[Extension]: """Produce the list of extension modules when a shared library is needed. - This creates one shared library extension module that all of the - others import and then one shim extension module for each - module in the build, that simply calls an initialization function + This creates one shared library extension module that all the + others import, and one shim extension module for each + module in the build. Each shim simply calls an initialization function in the shared library. - The shared library (which lib_name is the name of) is a python + The shared library (which lib_name is the name of) is a Python extension module that exports the real initialization functions in Capsules stored in module attributes. """ @@ -352,6 +424,7 @@ def construct_groups( sources: list[BuildSource], separate: bool | list[tuple[list[str], str | None]], use_shared_lib: bool, + group_name_override: str | None, ) -> emitmodule.Groups: """Compute Groups given the input source list and separate configs. @@ -381,7 +454,10 @@ def construct_groups( # Generate missing names for i, (group, name) in enumerate(groups): if use_shared_lib and not name: - name = group_name([source.module for source in group]) + if group_name_override is not None: + name = group_name_override + else: + name = group_name([source.module for source in group]) groups[i] = (group, name) return groups @@ -427,7 +503,10 @@ def mypyc_build( or always_use_shared_lib ) - groups = construct_groups(mypyc_sources, separate, use_shared_lib) + groups = construct_groups(mypyc_sources, separate, use_shared_lib, compiler_options.group_name) + + if compiler_options.group_name is not None: + assert len(groups) == 1, "If using custom group_name, only one group is expected" # We let the test harness just pass in the c file contents instead # so that it can do a corner-cutting version without full stubs. @@ -447,7 +526,8 @@ def mypyc_build( cfilenames = [] for cfile, ctext in cfiles: cfile = os.path.join(compiler_options.target_dir, cfile) - write_file(cfile, ctext) + if not options.mypyc_skip_c_generation: + write_file(cfile, ctext) if os.path.splitext(cfile)[1] == ".c": cfilenames.append(cfile) @@ -470,6 +550,12 @@ def mypycify( skip_cgen_input: Any | None = None, target_dir: str | None = None, include_runtime_files: bool | None = None, + strict_dunder_typing: bool = False, + group_name: str | None = None, + log_trace: bool = False, + depends_on_librt_internal: bool = False, + install_librt: bool = False, + experimental_features: bool = False, ) -> list[Extension]: """Main entry point to building using mypyc. @@ -495,7 +581,7 @@ def mypycify( separate: Should compiled modules be placed in separate extension modules. If False, all modules are placed in a single shared library. If True, every module is placed in its own library. - Otherwise separate should be a list of + Otherwise, separate should be a list of (file name list, optional shared library name) pairs specifying groups of files that should be placed in the same shared library (while all other modules will be placed in its own library). @@ -509,6 +595,25 @@ 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. + group_name: If set, override the default group name derived from + the hash of module names. This is used for the names of the + output C files and the shared library. This is only supported + if there is a single group. [Experimental] + log_trace: If True, compiled code writes a trace log of events in + mypyc_trace.txt (derived from executed operations). This is + useful for performance analysis, such as analyzing which + primitive ops are used the most and on which lines. + depends_on_librt_internal: This is True only for mypy itself. + install_librt: If True, also build the librt extension modules. Normally, + those are build and published on PyPI separately, but during + tests, we want to use their development versions (i.e. from + current commit). + experimental_features: Enable experimental features (install_librt=True is + also needed if using experimental librt features). These + have no backward compatibility guarantees! """ # Figure out our configuration @@ -519,6 +624,11 @@ def mypycify( separate=separate is not False, target_dir=target_dir, include_runtime_files=include_runtime_files, + strict_dunder_typing=strict_dunder_typing, + group_name=group_name, + log_trace=log_trace, + depends_on_librt_internal=depends_on_librt_internal, + experimental_features=experimental_features, ) # Generate all the actual important C code @@ -559,6 +669,10 @@ def mypycify( # See https://github.com/mypyc/mypyc/issues/956 "-Wno-cpp", ] + if log_trace: + cflags.append("-DMYPYC_LOG_TRACE") + if experimental_features: + cflags.append("-DMYPYC_EXPERIMENTAL") elif compiler.compiler_type == "msvc": # msvc doesn't have levels, '/O2' is full and '/Od' is disable if opt_level == "0": @@ -583,6 +697,10 @@ def mypycify( # that we actually get the compilation speed and memory # use wins that multi-file mode is intended for. cflags += ["/GL-", "/wd9025"] # warning about overriding /GL + if log_trace: + cflags.append("/DMYPYC_LOG_TRACE") + if experimental_features: + cflags.append("/DMYPYC_EXPERIMENTAL") # If configured to (defaults to yes in multi-file mode), copy the # runtime library in. Otherwise it just gets #included to save on @@ -613,4 +731,26 @@ def mypycify( build_single_module(group_sources, cfilenames + shared_cfilenames, cflags) ) + if install_librt: + for name in RUNTIME_C_FILES: + rt_file = os.path.join(build_dir, name) + with open(os.path.join(include_dir(), name), encoding="utf-8") as f: + write_file(rt_file, f.read()) + for mod, file_names, addit_files, includes in LIBRT_MODULES: + for file_name in file_names + addit_files: + rt_file = os.path.join(build_dir, file_name) + with open(os.path.join(include_dir(), file_name), encoding="utf-8") as f: + write_file(rt_file, f.read()) + extensions.append( + get_extension()( + mod, + sources=[ + os.path.join(build_dir, file) for file in file_names + RUNTIME_C_FILES + ], + include_dirs=[include_dir()] + + [os.path.join(include_dir(), d) for d in includes], + extra_compile_args=cflags, + ) + ) + return extensions diff --git a/mypyc/build_setup.py b/mypyc/build_setup.py new file mode 100644 index 0000000000000..a3e7a669abee9 --- /dev/null +++ b/mypyc/build_setup.py @@ -0,0 +1,62 @@ +import platform +import sys + +try: + # Import setuptools so that it monkey-patch overrides distutils + import setuptools # noqa: F401 +except ImportError: + pass + +if sys.version_info >= (3, 12): + # From setuptools' monkeypatch + from distutils import ccompiler # type: ignore[import-not-found] +else: + from distutils import ccompiler + +EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT = { + "unix": { + "base64/arch/ssse3": ["-mssse3"], + "base64/arch/sse41": ["-msse4.1"], + "base64/arch/sse42": ["-msse4.2"], + "base64/arch/avx2": ["-mavx2"], + "base64/arch/avx": ["-mavx"], + }, + "msvc": { + "base64/arch/sse42": ["/arch:SSE4.2"], + "base64/arch/avx2": ["/arch:AVX2"], + "base64/arch/avx": ["/arch:AVX"], + }, +} + +ccompiler.CCompiler.__spawn = ccompiler.CCompiler.spawn # type: ignore[attr-defined] +X86_64 = platform.machine() in ("x86_64", "AMD64", "amd64") + + +def spawn(self, cmd, **kwargs) -> None: # type: ignore[no-untyped-def] + compiler_type: str = self.compiler_type + extra_options = EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT[compiler_type] + new_cmd = list(cmd) + if X86_64 and extra_options is not None: + # filenames are closer to the end of command line + for argument in reversed(new_cmd): + # Check if the matching argument contains a source filename. + if not str(argument).endswith(".c"): + continue + + for path in extra_options.keys(): + if path in str(argument): + if compiler_type == "bcpp": + compiler = new_cmd.pop() + # Borland accepts a source file name at the end, + # insert the options before it + new_cmd.extend(extra_options[path]) + new_cmd.append(compiler) + else: + new_cmd.extend(extra_options[path]) + + # path component is found, no need to search any further + break + self.__spawn(new_cmd, **kwargs) + + +ccompiler.CCompiler.spawn = spawn # type: ignore[method-assign] diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index fce6896e8d116..f2a2271e020ea 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -12,11 +12,11 @@ ATTR_PREFIX, BITMAP_BITS, FAST_ISINSTANCE_MAX_SUBCLASSES, + HAVE_IMMORTAL, NATIVE_PREFIX, REG_PREFIX, STATIC_PREFIX, TYPE_PREFIX, - use_vectorcall, ) from mypyc.ir.class_ir import ClassIR, all_concrete_classes from mypyc.ir.func_ir import FuncDecl @@ -28,17 +28,18 @@ RType, RUnion, int_rprimitive, - is_bit_rprimitive, - is_bool_rprimitive, + is_bool_or_bit_rprimitive, is_bytes_rprimitive, is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_frozenset_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, is_list_rprimitive, + is_native_rprimitive, is_none_rprimitive, is_object_rprimitive, is_optional_type, @@ -195,7 +196,7 @@ def attr(self, name: str) -> str: return ATTR_PREFIX + name def object_annotation(self, obj: object, line: str) -> str: - """Build a C comment with an object's string represention. + """Build a C comment with an object's string representation. If the comment exceeds the line length limit, it's wrapped into a multiline string (with the extra lines indented to be aligned with @@ -397,9 +398,6 @@ def _emit_attr_bitmap_update( if value: self.emit_line("}") - def use_vectorcall(self) -> bool: - return use_vectorcall(self.capi_version) - def emit_undefined_attr_check( self, rtype: RType, @@ -511,8 +509,11 @@ def emit_inc_ref(self, dest: str, rtype: RType, *, rare: bool = False) -> None: for i, item_type in enumerate(rtype.types): self.emit_inc_ref(f"{dest}.f{i}", item_type) elif not rtype.is_unboxed: - # Always inline, since this is a simple op - self.emit_line("CPy_INCREF(%s);" % dest) + # Always inline, since this is a simple but very hot op + if rtype.may_be_immortal or not HAVE_IMMORTAL: + self.emit_line("CPy_INCREF(%s);" % dest) + else: + self.emit_line("CPy_INCREF_NO_IMM(%s);" % dest) # Otherwise assume it's an unboxed, pointerless value and do nothing. def emit_dec_ref( @@ -540,7 +541,10 @@ def emit_dec_ref( self.emit_line(f"CPy_{x}DecRef({dest});") else: # Inlined - self.emit_line(f"CPy_{x}DECREF({dest});") + if rtype.may_be_immortal or not HAVE_IMMORTAL: + self.emit_line(f"CPy_{x}DECREF({dest});") + else: + self.emit_line(f"CPy_{x}DECREF_NO_IMM({dest});") # Otherwise assume it's an unboxed, pointerless value and do nothing. def pretty_name(self, typ: RType) -> str: @@ -606,12 +610,12 @@ def emit_cast( is_list_rprimitive(typ) or is_dict_rprimitive(typ) or is_set_rprimitive(typ) + or is_frozenset_rprimitive(typ) or is_str_rprimitive(typ) or is_range_rprimitive(typ) or is_float_rprimitive(typ) or is_int_rprimitive(typ) - or is_bool_rprimitive(typ) - or is_bit_rprimitive(typ) + or is_bool_or_bit_rprimitive(typ) or is_fixed_width_rtype(typ) ): if declare_dest: @@ -622,6 +626,8 @@ def emit_cast( prefix = "PyDict" elif is_set_rprimitive(typ): prefix = "PySet" + elif is_frozenset_rprimitive(typ): + prefix = "PyFrozenSet" elif is_str_rprimitive(typ): prefix = "PyUnicode" elif is_range_rprimitive(typ): @@ -631,7 +637,7 @@ def emit_cast( elif is_int_rprimitive(typ) or is_fixed_width_rtype(typ): # TODO: Range check for fixed-width types? prefix = "PyLong" - elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ): + elif is_bool_or_bit_rprimitive(typ): prefix = "PyBool" else: assert False, f"unexpected primitive type: {typ}" @@ -706,6 +712,18 @@ def emit_cast( self.emit_line(f"{dest} = {src};") if optional: self.emit_line("}") + elif is_native_rprimitive(typ): + # Native primitive types have type check functions of form "CPy_Check(...)". + if declare_dest: + self.emit_line(f"PyObject *{dest};") + short_name = typ.name.rsplit(".", 1)[-1] + check = f"(CPy{short_name}_Check({src}))" + if likely: + check = f"(likely{check})" + self.emit_arg_check(src, dest, typ, check, optional) + self.emit_lines(f" {dest} = {src};", "else {") + self.emit_cast_error_handler(error, src, dest, typ, raise_exception) + self.emit_line("}") elif isinstance(typ, RUnion): self.emit_union_cast( src, dest, typ, declare_dest, error, optional, src_type, raise_exception @@ -737,7 +755,7 @@ def emit_cast_error_handler( self.emit_traceback(error.source_path, error.module_name, error.traceback_entry) self.emit_line("goto %s;" % error.label) else: - assert isinstance(error, ReturnHandler) + assert isinstance(error, ReturnHandler), error self.emit_line("return %s;" % error.value) def emit_union_cast( @@ -866,7 +884,7 @@ def emit_unbox( elif isinstance(error, GotoHandler): failure = "goto %s;" % error.label else: - assert isinstance(error, ReturnHandler) + assert isinstance(error, ReturnHandler), error failure = "return %s;" % error.value if raise_exception: raise_exc = f'CPy_TypeError("{self.pretty_name(typ)}", {src}); ' @@ -882,7 +900,7 @@ def emit_unbox( self.emit_line("else {") self.emit_line(failure) self.emit_line("}") - elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ): + elif is_bool_or_bit_rprimitive(typ): # Whether we are borrowing or not makes no difference. if declare_dest: self.emit_line(f"char {dest};") @@ -1008,7 +1026,7 @@ def emit_box( if is_int_rprimitive(typ) or is_short_int_rprimitive(typ): # Steal the existing reference if it exists. self.emit_line(f"{declaration}{dest} = CPyTagged_StealAsObject({src});") - elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ): + elif is_bool_or_bit_rprimitive(typ): # N.B: bool is special cased to produce a borrowed value # after boxing, so we don't need to increment the refcount # when this comes directly from a Box op. @@ -1030,17 +1048,21 @@ def emit_box( self.emit_line(f"{declaration}{dest} = PyFloat_FromDouble({src});") elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) - self.emit_line(f"{declaration}{dest} = PyTuple_New({len(typ.types)});") - self.emit_line(f"if (unlikely({dest} == NULL))") - self.emit_line(" CPyError_OutOfMemory();") - # TODO: Fail if dest is None - for i in range(0, len(typ.types)): - if not typ.is_unboxed: - self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {src}.f{i}") - else: - inner_name = self.temp_name() - self.emit_box(f"{src}.f{i}", inner_name, typ.types[i], declare_dest=True) - self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {inner_name});") + if not typ.types: + self.emit_line(f"{declaration}{dest} = CPyTuple_LoadEmptyTupleConstant();") + else: + self.emit_line(f"{declaration}{dest} = PyTuple_New({len(typ.types)});") + self.emit_line(f"if (unlikely({dest} == NULL))") + self.emit_line(" CPyError_OutOfMemory();") + + # TODO: Fail if dest is None + for i in range(len(typ.types)): + if not typ.is_unboxed: + self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {src}.f{i}") + else: + inner_name = self.temp_name() + self.emit_box(f"{src}.f{i}", inner_name, typ.types[i], declare_dest=True) + self.emit_line(f"PyTuple_SET_ITEM({dest}, {i}, {inner_name});") else: assert not typ.is_unboxed # Type is boxed -- trivially just assign. @@ -1108,6 +1130,31 @@ def emit_gc_clear(self, target: str, rtype: RType) -> None: else: assert False, "emit_gc_clear() not implemented for %s" % repr(rtype) + def emit_reuse_clear(self, target: str, rtype: RType) -> None: + """Emit attribute clear before object is added into freelist. + + Assume that 'target' represents a C expression that refers to a + struct member, such as 'self->x'. + + Unlike emit_gc_clear(), initialize attribute value to match a freshly + allocated object. + """ + if isinstance(rtype, RTuple): + for i, item_type in enumerate(rtype.types): + self.emit_reuse_clear(f"{target}.f{i}", item_type) + elif not rtype.is_refcounted: + self.emit_line(f"{target} = {rtype.c_undefined};") + elif isinstance(rtype, RPrimitive) and rtype.name == "builtins.int": + self.emit_line(f"if (CPyTagged_CheckLong({target})) {{") + self.emit_line(f"CPyTagged __tmp = {target};") + self.emit_line(f"{target} = {self.c_undefined_value(rtype)};") + self.emit_line("Py_XDECREF(CPyTagged_LongAsObject(__tmp));") + self.emit_line("} else {") + self.emit_line(f"{target} = {self.c_undefined_value(rtype)};") + self.emit_line("}") + else: + self.emit_gc_clear(target, rtype) + def emit_traceback( self, source_path: str, module_name: str, traceback_entry: tuple[str, int] ) -> None: diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index ad95a1b0f323f..e190d45a2e937 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -2,10 +2,13 @@ from __future__ import annotations -from typing import Callable, Mapping, Tuple +from collections.abc import Mapping +from typing import Callable +from mypy.nodes import ARG_STAR, ARG_STAR2 +from mypyc.codegen.cstring import c_string_initializer from mypyc.codegen.emit import Emitter, HeaderDeclaration, ReturnHandler -from mypyc.codegen.emitfunc import native_function_header +from mypyc.codegen.emitfunc import native_function_doc_initializer, native_function_header from mypyc.codegen.emitwrapper import ( generate_bin_op_wrapper, generate_bool_wrapper, @@ -20,7 +23,13 @@ ) from mypyc.common import BITMAP_BITS, BITMAP_TYPE, NATIVE_PREFIX, PREFIX, REG_PREFIX from mypyc.ir.class_ir import ClassIR, VTableEntries -from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR +from mypyc.ir.func_ir import ( + FUNC_CLASSMETHOD, + FUNC_STATICMETHOD, + FuncDecl, + FuncIR, + get_text_signature, +) from mypyc.ir.rtypes import RTuple, RType, object_rprimitive from mypyc.namegen import NameGenerator from mypyc.sametype import is_same_type @@ -30,8 +39,10 @@ def native_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: return f"{NATIVE_PREFIX}{fn.cname(emitter.names)}" -def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: - return f"{PREFIX}{fn.cname(emitter.names)}" +def dunder_attr_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: + wrapper_fn = cl.get_method(fn.name + "__wrapper") + assert wrapper_fn + return f"{NATIVE_PREFIX}{wrapper_fn.cname(emitter.names)}" # We maintain a table from dunder function names to struct slots they @@ -39,7 +50,7 @@ def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: # and return the function name to stick in the slot. # TODO: Add remaining dunder methods SlotGenerator = Callable[[ClassIR, FuncIR, Emitter], str] -SlotTable = Mapping[str, Tuple[str, SlotGenerator]] +SlotTable = Mapping[str, tuple[str, SlotGenerator]] SLOT_DEFS: SlotTable = { "__init__": ("tp_init", lambda c, t, e: generate_init_for_class(c, t, e)), @@ -50,6 +61,8 @@ def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: "__iter__": ("tp_iter", native_slot), "__hash__": ("tp_hash", generate_hash_wrapper), "__get__": ("tp_descr_get", generate_get_wrapper), + "__getattr__": ("tp_getattro", dunder_attr_slot), + "__setattr__": ("tp_setattro", dunder_attr_slot), } AS_MAPPING_SLOT_DEFS: SlotTable = { @@ -136,12 +149,7 @@ def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: def generate_call_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: - if emitter.use_vectorcall(): - # Use vectorcall wrapper if supported (PEP 590). - return "PyVectorcall_Call" - else: - # On older Pythons use the legacy wrapper. - return wrapper_slot(cl, fn, emitter) + return "PyVectorcall_Call" def slot_key(attr: str) -> str: @@ -194,6 +202,25 @@ def generate_class_type_decl( ) +def generate_class_reuse( + cl: ClassIR, c_emitter: Emitter, external_emitter: Emitter, emitter: Emitter +) -> None: + """Generate a definition of a single-object per-class free "list". + + This speeds up object allocation and freeing when there are many short-lived + objects. + + TODO: Generalize to support a free list with up to N objects. + """ + assert cl.reuse_freed_instance + context = c_emitter.context + name = cl.name_prefix(c_emitter.names) + "_free_instance" + struct_name = cl.struct_name(c_emitter.names) + context.declarations[name] = HeaderDeclaration( + f"CPyThreadLocal {struct_name} *{name};", needs_export=True + ) + + def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: """Generate C code for a class. @@ -202,8 +229,9 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: name = cl.name name_prefix = cl.name_prefix(emitter.names) - setup_name = f"{name_prefix}_setup" + setup_name = emitter.native_function_name(cl.setup) new_name = f"{name_prefix}_new" + finalize_name = f"{name_prefix}_finalize" members_name = f"{name_prefix}_members" getseters_name = f"{name_prefix}_getseters" vtable_name = f"{name_prefix}_vtable" @@ -213,8 +241,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 @@ -226,6 +253,10 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: fields["tp_dealloc"] = f"(destructor){name_prefix}_dealloc" fields["tp_traverse"] = f"(traverseproc){name_prefix}_traverse" fields["tp_clear"] = f"(inquiry){name_prefix}_clear" + # Populate .tp_finalize and generate a finalize method only if __del__ is defined for this class. + del_method = next((e.method for e in cl.vtable_entries if e.name == "__del__"), None) + if del_method: + fields["tp_finalize"] = f"(destructor){finalize_name}" if needs_getseters: fields["tp_getset"] = getseters_name fields["tp_methods"] = methods_name @@ -291,10 +322,8 @@ def emit_line() -> None: fields["tp_basicsize"] = base_size if generate_full: - # Declare setup method that allocates and initializes an object. type is the - # type of the class being initialized, which could be another class if there - # is an interpreted subclass. - emitter.emit_line(f"static PyObject *{setup_name}(PyTypeObject *type);") + assert cl.setup is not None + emitter.emit_line(native_function_header(cl.setup, emitter) + ";") assert cl.ctor is not None emitter.emit_line(native_function_header(cl.ctor, emitter) + ";") @@ -306,7 +335,7 @@ def emit_line() -> None: emit_line() generate_clear_for_class(cl, clear_name, emitter) emit_line() - generate_dealloc_for_class(cl, dealloc_name, clear_name, emitter) + generate_dealloc_for_class(cl, dealloc_name, clear_name, bool(del_method), emitter) emit_line() if cl.allow_interpreted_subclasses: @@ -318,6 +347,9 @@ def emit_line() -> None: shadow_vtable_name = None vtable_name = generate_vtables(cl, vtable_setup_name, vtable_name, emitter, shadow=False) emit_line() + if del_method: + generate_finalize_for_class(del_method, finalize_name, emitter) + emit_line() if needs_getseters: generate_getseter_declarations(cl, emitter) emit_line() @@ -327,13 +359,13 @@ def emit_line() -> None: if cl.is_trait: generate_new_for_trait(cl, new_name, emitter) - generate_methods_table(cl, methods_name, emitter) + generate_methods_table(cl, methods_name, setup_name if generate_full else None, emitter) emit_line() flags = ["Py_TPFLAGS_DEFAULT", "Py_TPFLAGS_HEAPTYPE", "Py_TPFLAGS_BASETYPE"] if generate_full: flags.append("Py_TPFLAGS_HAVE_GC") - if cl.has_method("__call__") and emitter.use_vectorcall(): + if cl.has_method("__call__"): fields["tp_vectorcall_offset"] = "offsetof({}, vectorcall)".format( cl.struct_name(emitter.names) ) @@ -346,6 +378,8 @@ def emit_line() -> None: flags.append("Py_TPFLAGS_MANAGED_DICT") fields["tp_flags"] = " | ".join(flags) + fields["tp_doc"] = f"PyDoc_STR({native_class_doc_initializer(cl)})" + emitter.emit_line(f"static PyTypeObject {emitter.type_struct_name(cl)}_template_ = {{") emitter.emit_line("PyVarObject_HEAD_INIT(NULL, 0)") for field, value in fields.items(): @@ -359,9 +393,7 @@ def emit_line() -> None: emitter.emit_line() if generate_full: - generate_setup_for_class( - cl, setup_name, defaults_fn, vtable_name, shadow_vtable_name, emitter - ) + generate_setup_for_class(cl, defaults_fn, vtable_name, shadow_vtable_name, emitter) emitter.emit_line() generate_constructor_for_class(cl, cl.ctor, init_fn, setup_name, vtable_name, emitter) emitter.emit_line() @@ -378,10 +410,10 @@ def setter_name(cl: ClassIR, attribute: str, names: NameGenerator) -> str: def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: - seen_attrs: set[tuple[str, RType]] = set() + seen_attrs: set[str] = set() lines: list[str] = [] lines += ["typedef struct {", "PyObject_HEAD", "CPyVTableItem *vtable;"] - if cl.has_method("__call__") and emitter.use_vectorcall(): + if cl.has_method("__call__"): lines.append("vectorcallfunc vectorcall;") bitmap_attrs = [] for base in reversed(cl.base_mro): @@ -395,9 +427,11 @@ def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: lines.append(f"{BITMAP_TYPE} {attr};") bitmap_attrs.append(attr) for attr, rtype in base.attributes.items(): - if (attr, rtype) not in seen_attrs: + # Generated class may redefine certain attributes with different + # types in subclasses (this would be unsafe for user-defined classes). + if attr not in seen_attrs: lines.append(f"{emitter.ctype_spaced(rtype)}{emitter.attr(attr)};") - seen_attrs.add((attr, rtype)) + seen_attrs.add(attr) if isinstance(rtype, RTuple): emitter.declare_tuple_struct(rtype) @@ -548,17 +582,32 @@ def generate_vtable( def generate_setup_for_class( cl: ClassIR, - func_name: str, defaults_fn: FuncIR | None, vtable_name: str, shadow_vtable_name: str | None, emitter: Emitter, ) -> None: """Generate a native function that allocates an instance of a class.""" - emitter.emit_line("static PyObject *") - emitter.emit_line(f"{func_name}(PyTypeObject *type)") + emitter.emit_line(native_function_header(cl.setup, emitter)) emitter.emit_line("{") - emitter.emit_line(f"{cl.struct_name(emitter.names)} *self;") + type_arg_name = REG_PREFIX + cl.setup.sig.args[0].name + emitter.emit_line(f"PyTypeObject *type = (PyTypeObject*){type_arg_name};") + struct_name = cl.struct_name(emitter.names) + emitter.emit_line(f"{struct_name} *self;") + + prefix = cl.name_prefix(emitter.names) + if cl.reuse_freed_instance: + # Attempt to use a per-type free list first (a free "list" with up to one object only). + emitter.emit_line(f"if ({prefix}_free_instance != NULL) {{") + emitter.emit_line(f"self = {prefix}_free_instance;") + emitter.emit_line(f"{prefix}_free_instance = NULL;") + emitter.emit_line("Py_SET_REFCNT(self, 1);") + emitter.emit_line("PyObject_GC_Track(self);") + if defaults_fn is not None: + emit_attr_defaults_func_call(defaults_fn, "self", emitter) + emitter.emit_line("return (PyObject *)self;") + emitter.emit_line("}") + emitter.emit_line(f"self = ({cl.struct_name(emitter.names)} *)type->tp_alloc(type, 0);") emitter.emit_line("if (self == NULL)") emitter.emit_line(" return NULL;") @@ -571,11 +620,10 @@ 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;") - if cl.has_method("__call__") and emitter.use_vectorcall(): + emit_clear_bitmaps(cl, emitter) + + if cl.has_method("__call__"): name = cl.method_decl("__call__").cname(emitter.names) emitter.emit_line(f"self->vectorcall = {PREFIX}{name};") @@ -590,19 +638,63 @@ def generate_setup_for_class( # Initialize attributes to default values, if necessary if defaults_fn is not None: - emitter.emit_lines( - "if ({}{}((PyObject *)self) == 0) {{".format( - NATIVE_PREFIX, defaults_fn.cname(emitter.names) - ), - "Py_DECREF(self);", - "return NULL;", - "}", - ) + emit_attr_defaults_func_call(defaults_fn, "self", emitter) emitter.emit_line("return (PyObject *)self;") emitter.emit_line("}") +def emit_clear_bitmaps(cl: ClassIR, emitter: Emitter) -> None: + """Emit C code to clear bitmaps that track if attributes have an assigned value.""" + for i in range(0, len(cl.bitmap_attrs), BITMAP_BITS): + field = emitter.bitmap_field(i) + emitter.emit_line(f"self->{field} = 0;") + + +def emit_attr_defaults_func_call(defaults_fn: FuncIR, self_name: str, emitter: Emitter) -> None: + """Emit C code to initialize attribute defaults by calling defaults_fn. + + The code returns NULL on a raised exception. + """ + emitter.emit_lines( + "if ({}{}((PyObject *){}) == 0) {{".format( + NATIVE_PREFIX, defaults_fn.cname(emitter.names), self_name + ), + "Py_DECREF(self);", + "return NULL;", + "}", + ) + + +def emit_setup_or_dunder_new_call( + cl: ClassIR, + setup_name: str, + type_arg: str, + native_prefix: bool, + new_args: str, + emitter: Emitter, +) -> None: + def emit_null_check() -> None: + emitter.emit_line("if (self == NULL)") + emitter.emit_line(" return NULL;") + + new_fn = cl.get_method("__new__") + if not new_fn: + emitter.emit_line(f"PyObject *self = {setup_name}({type_arg});") + emit_null_check() + return + prefix = emitter.get_group_prefix(new_fn.decl) + NATIVE_PREFIX if native_prefix else PREFIX + all_args = type_arg + if new_args != "": + all_args += ", " + new_args + emitter.emit_line(f"PyObject *self = {prefix}{new_fn.cname(emitter.names)}({all_args});") + emit_null_check() + + # skip __init__ if __new__ returns some other type + emitter.emit_line(f"if (Py_TYPE(self) != {emitter.type_struct_name(cl)})") + emitter.emit_line(" return self;") + + def generate_constructor_for_class( cl: ClassIR, fn: FuncDecl, @@ -614,17 +706,30 @@ def generate_constructor_for_class( """Generate a native function that allocates and initializes an instance of a class.""" emitter.emit_line(f"{native_function_header(fn, emitter)}") emitter.emit_line("{") - emitter.emit_line(f"PyObject *self = {setup_name}({emitter.type_struct_name(cl)});") - emitter.emit_line("if (self == NULL)") - emitter.emit_line(" return NULL;") - args = ", ".join(["self"] + [REG_PREFIX + arg.name for arg in fn.sig.args]) + + fn_args = [REG_PREFIX + arg.name for arg in fn.sig.args] + type_arg = "(PyObject *)" + emitter.type_struct_name(cl) + new_args = ", ".join(fn_args) + + use_wrapper = ( + cl.has_method("__new__") + and len(fn.sig.args) == 2 + and fn.sig.args[0].kind == ARG_STAR + and fn.sig.args[1].kind == ARG_STAR2 + ) + emit_setup_or_dunder_new_call(cl, setup_name, type_arg, not use_wrapper, new_args, emitter) + + args = ", ".join(["self"] + fn_args) if init_fn is not None: + prefix = PREFIX if use_wrapper else NATIVE_PREFIX + cast = "!= NULL ? 0 : -1" if use_wrapper else "" emitter.emit_line( - "char res = {}{}{}({});".format( + "char res = {}{}{}({}){};".format( emitter.get_group_prefix(init_fn.decl), - NATIVE_PREFIX, + prefix, init_fn.cname(emitter.names), args, + cast, ) ) emitter.emit_line("if (res == 2) {") @@ -657,7 +762,7 @@ def generate_init_for_class(cl: ClassIR, init_fn: FuncIR, emitter: Emitter) -> s emitter.emit_line("static int") emitter.emit_line(f"{func_name}(PyObject *self, PyObject *args, PyObject *kwds)") emitter.emit_line("{") - if cl.allow_interpreted_subclasses or cl.builtin_base: + if cl.allow_interpreted_subclasses or cl.builtin_base or cl.has_method("__new__"): emitter.emit_line( "return {}{}(self, args, kwds) != NULL ? 0 : -1;".format( PREFIX, init_fn.cname(emitter.names) @@ -690,15 +795,22 @@ def generate_new_for_class( emitter.emit_line("return NULL;") emitter.emit_line("}") - if not init_fn or cl.allow_interpreted_subclasses or cl.builtin_base or cl.is_serializable(): + type_arg = "(PyObject*)type" + new_args = "args, kwds" + emit_setup_or_dunder_new_call(cl, setup_name, type_arg, False, new_args, emitter) + if ( + not init_fn + or cl.allow_interpreted_subclasses + or cl.builtin_base + or cl.is_serializable() + or cl.has_method("__new__") + ): # Match Python semantics -- __new__ doesn't call __init__. - emitter.emit_line(f"return {setup_name}(type);") + emitter.emit_line("return self;") else: # __new__ of a native class implicitly calls __init__ so that we # can enforce that instances are always properly initialized. This # is needed to support always defined attributes. - emitter.emit_line(f"PyObject *self = {setup_name}(type);") - emitter.emit_lines("if (self == NULL)", " return NULL;") emitter.emit_line( f"PyObject *ret = {PREFIX}{init_fn.cname(emitter.names)}(self, args, kwds);" ) @@ -773,24 +885,94 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N def generate_dealloc_for_class( - cl: ClassIR, dealloc_func_name: str, clear_func_name: str, emitter: Emitter + cl: ClassIR, + dealloc_func_name: str, + clear_func_name: str, + has_tp_finalize: bool, + emitter: Emitter, ) -> None: emitter.emit_line("static void") emitter.emit_line(f"{dealloc_func_name}({cl.struct_name(emitter.names)} *self)") emitter.emit_line("{") + if has_tp_finalize: + emitter.emit_line("PyObject *type, *value, *traceback;") + emitter.emit_line("PyErr_Fetch(&type, &value, &traceback);") + emitter.emit_line("int res = PyObject_CallFinalizerFromDealloc((PyObject *)self);") + # CPython interpreter uses PyErr_WriteUnraisable: https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable + # However, the message is slightly different due to the way mypyc compiles classes. + # CPython interpreter prints: Exception ignored in: + # mypyc prints: Exception ignored in: + emitter.emit_line("if (PyErr_Occurred() != NULL) {") + # Don't untrack instance if error occurred + emitter.emit_line("PyErr_WriteUnraisable((PyObject *)self);") + emitter.emit_line("res = -1;") + emitter.emit_line("}") + emitter.emit_line("PyErr_Restore(type, value, traceback);") + emitter.emit_line("if (res < 0) {") + emitter.emit_line("goto done;") + emitter.emit_line("}") emitter.emit_line("PyObject_GC_UnTrack(self);") + if cl.reuse_freed_instance: + emit_reuse_dealloc(cl, emitter) # The trashcan is needed to handle deep recursive deallocations emitter.emit_line(f"CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})") emitter.emit_line(f"{clear_func_name}(self);") emitter.emit_line("Py_TYPE(self)->tp_free((PyObject *)self);") emitter.emit_line("CPy_TRASHCAN_END(self)") + emitter.emit_line("done: ;") emitter.emit_line("}") -def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: +def emit_reuse_dealloc(cl: ClassIR, emitter: Emitter) -> None: + """Emit code to deallocate object by putting it to per-type free list. + + The free "list" currently can have up to one object. + """ + prefix = cl.name_prefix(emitter.names) + emitter.emit_line(f"if ({prefix}_free_instance == NULL) {{") + emitter.emit_line(f"{prefix}_free_instance = self;") + + # Clear attributes and free referenced objects. + + emit_clear_bitmaps(cl, emitter) + + for base in reversed(cl.base_mro): + for attr, rtype in base.attributes.items(): + emitter.emit_reuse_clear(f"self->{emitter.attr(attr)}", rtype) + + emitter.emit_line("return;") + emitter.emit_line("}") + + +def generate_finalize_for_class( + del_method: FuncIR, finalize_func_name: str, emitter: Emitter +) -> None: + emitter.emit_line("static void") + emitter.emit_line(f"{finalize_func_name}(PyObject *self)") + emitter.emit_line("{") + emitter.emit_line( + "{}{}{}(self);".format( + emitter.get_group_prefix(del_method.decl), + NATIVE_PREFIX, + del_method.cname(emitter.names), + ) + ) + emitter.emit_line("}") + + +def generate_methods_table( + cl: ClassIR, name: str, setup_name: str | None, emitter: Emitter +) -> None: emitter.emit_line(f"static PyMethodDef {name}[] = {{") + if setup_name: + # Store pointer to the setup function so it can be resolved dynamically + # in case of instance creation in __new__. + # CPy_SetupObject expects this method to be the first one in tp_methods. + emitter.emit_line( + f'{{"__internal_mypyc_setup", (PyCFunction){setup_name}, METH_O, NULL}},' + ) for fn in cl.methods.values(): - if fn.decl.is_prop_setter or fn.decl.is_prop_getter: + if fn.decl.is_prop_setter or fn.decl.is_prop_getter or fn.internal: continue emitter.emit_line(f'{{"{fn.name}",') emitter.emit_line(f" (PyCFunction){PREFIX}{fn.cname(emitter.names)},") @@ -800,7 +982,8 @@ def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: elif fn.decl.kind == FUNC_CLASSMETHOD: flags.append("METH_CLASS") - emitter.emit_line(" {}, NULL}},".format(" | ".join(flags))) + doc = native_function_doc_initializer(fn) + emitter.emit_line(" {}, PyDoc_STR({})}},".format(" | ".join(flags), doc)) # Provide a default __getstate__ and __setstate__ if not cl.has_method("__setstate__") and not cl.has_method("__getstate__"): @@ -1058,3 +1241,16 @@ def has_managed_dict(cl: ClassIR, emitter: Emitter) -> bool: and cl.has_dict and cl.builtin_base != "PyBaseExceptionObject" ) + + +def native_class_doc_initializer(cl: ClassIR) -> str: + init_fn = cl.get_method("__init__") + if init_fn is not None: + text_sig = get_text_signature(init_fn, bound=True) + if text_sig is None: + return "NULL" + text_sig = text_sig.replace("__init__", cl.name, 1) + else: + text_sig = f"{cl.name}()" + docstring = f"{text_sig}\n--\n\n" + return c_string_initializer(docstring.encode("ascii", errors="backslashreplace")) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index d945a28d84817..a1e18353693ee 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -5,8 +5,11 @@ from typing import Final from mypyc.analysis.blockfreq import frequently_executed_blocks +from mypyc.codegen.cstring import c_string_initializer from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer from mypyc.common import ( + GENERATOR_ATTRIBUTE_PREFIX, + HAVE_IMMORTAL, MODULE_PREFIX, NATIVE_PREFIX, REG_PREFIX, @@ -15,7 +18,14 @@ TYPE_VAR_PREFIX, ) from mypyc.ir.class_ir import ClassIR -from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD, FuncDecl, FuncIR, all_values +from mypyc.ir.func_ir import ( + FUNC_CLASSMETHOD, + FUNC_STATICMETHOD, + FuncDecl, + FuncIR, + all_values, + get_text_signature, +) from mypyc.ir.ops import ( ERR_FALSE, NAMESPACE_MODULE, @@ -32,6 +42,7 @@ Cast, ComparisonOp, ControlOp, + CString, DecRef, Extend, Float, @@ -60,24 +71,29 @@ Register, Return, SetAttr, + SetElement, SetMem, Truncate, TupleGet, TupleSet, Unborrow, Unbox, + Undef, Unreachable, Value, ) from mypyc.ir.pprint import generate_names_for_ir from mypyc.ir.rtypes import ( RArray, + RInstance, RStruct, RTuple, RType, + is_bool_or_bit_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, + is_none_rprimitive, is_pointer_rprimitive, is_tagged, ) @@ -101,6 +117,14 @@ def native_function_header(fn: FuncDecl, emitter: Emitter) -> str: ) +def native_function_doc_initializer(func: FuncIR) -> str: + text_sig = get_text_signature(func) + if text_sig is None: + return "NULL" + docstring = f"{text_sig}\n--\n\n" + return c_string_initializer(docstring.encode("ascii", errors="backslashreplace")) + + def generate_native_function( fn: FuncIR, emitter: Emitter, source_path: str, module_name: str ) -> None: @@ -139,7 +163,7 @@ def generate_native_function( # eliminated during code generation. for block in fn.blocks: terminator = block.terminator - assert isinstance(terminator, ControlOp) + assert isinstance(terminator, ControlOp), terminator for target in terminator.targets(): is_next_block = target.label == block.label + 1 @@ -148,7 +172,7 @@ def generate_native_function( # generates them will add instructions between the branch and the # next label, causing the label to be wrongly removed. A better # solution would be to change the IR so that it adds a basic block - # inbetween the calls. + # in between the calls. is_problematic_op = isinstance(terminator, Branch) and any( isinstance(s, GetAttr) for s in terminator.sources() ) @@ -205,6 +229,16 @@ def visit_goto(self, op: Goto) -> None: if op.label is not self.next_block: self.emit_line("goto %s;" % self.label(op.label)) + def error_value_check(self, value: Value, compare: str) -> str: + typ = value.type + if isinstance(typ, RTuple): + # TODO: What about empty tuple? + return self.emitter.tuple_undefined_check_cond( + typ, self.reg(value), self.c_error_value, compare + ) + else: + return f"{self.reg(value)} {compare} {self.c_error_value(typ)}" + def visit_branch(self, op: Branch) -> None: true, false = op.true, op.false negated = op.negated @@ -221,15 +255,8 @@ def visit_branch(self, op: Branch) -> None: expr_result = self.reg(op.value) cond = f"{neg}{expr_result}" elif op.op == Branch.IS_ERROR: - typ = op.value.type compare = "!=" if negated else "==" - if isinstance(typ, RTuple): - # TODO: What about empty tuple? - cond = self.emitter.tuple_undefined_check_cond( - typ, self.reg(op.value), self.c_error_value, compare - ) - else: - cond = f"{self.reg(op.value)} {compare} {self.c_error_value(typ)}" + cond = self.error_value_check(op.value, compare) else: assert False, "Invalid branch" @@ -285,7 +312,7 @@ def visit_assign(self, op: Assign) -> None: def visit_assign_multi(self, op: AssignMulti) -> None: typ = op.dest.type - assert isinstance(typ, RArray) + assert isinstance(typ, RArray), typ dest = self.reg(op.dest) # RArray values can only be assigned to once, so we can always # declare them on initialization. @@ -354,6 +381,9 @@ def get_attr_expr(self, obj: str, op: GetAttr | SetAttr, decl_cl: ClassIR) -> st return f"({cast}{obj})->{self.emitter.attr(op.attr)}" def visit_get_attr(self, op: GetAttr) -> None: + if op.allow_error_value: + self.get_attr_with_allow_error_value(op) + return dest = self.reg(op) obj = self.reg(op.obj) rtype = op.class_type @@ -362,20 +392,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) @@ -404,7 +437,9 @@ def visit_get_attr(self, op: GetAttr) -> None: exc_class = "PyExc_AttributeError" self.emitter.emit_line( 'PyErr_SetString({}, "attribute {} of {} undefined");'.format( - exc_class, repr(op.attr), repr(cl.name) + exc_class, + repr(op.attr.removeprefix(GENERATOR_ATTRIBUTE_PREFIX)), + repr(cl.name), ) ) @@ -419,6 +454,28 @@ def visit_get_attr(self, op: GetAttr) -> None: elif not always_defined: self.emitter.emit_line("}") + def get_attr_with_allow_error_value(self, op: GetAttr) -> None: + """Handle GetAttr with allow_error_value=True. + + This allows NULL or other error value without raising AttributeError. + """ + dest = self.reg(op) + obj = self.reg(op.obj) + rtype = op.class_type + cl = rtype.class_ir + attr_rtype, decl_cl = cl.attr_details(op.attr) + + # Direct struct access without NULL check + attr_expr = self.get_attr_expr(obj, op, decl_cl) + self.emitter.emit_line(f"{dest} = {attr_expr};") + + # Only emit inc_ref if not NULL + if attr_rtype.is_refcounted and not op.is_borrowed: + check = self.error_value_check(op, "!=") + self.emitter.emit_line(f"if ({check}) {{") + self.emitter.emit_inc_ref(dest, attr_rtype) + self.emitter.emit_line("}") + def next_branch(self) -> Branch | None: if self.op_index + 1 < len(self.ops): next_op = self.ops[self.op_index + 1] @@ -529,11 +586,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), rtype class_ir = rtype.class_ir - name = op.method method = rtype.class_ir.get_method(name) assert method is not None @@ -547,7 +606,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,11 +626,26 @@ def visit_method_call(self, op: MethodCall) -> None: rtype.struct_name(self.names), mtype, args, - op.method, + name, ) ) def visit_inc_ref(self, op: IncRef) -> None: + if ( + isinstance(op.src, Box) + and (is_none_rprimitive(op.src.src.type) or is_bool_or_bit_rprimitive(op.src.src.type)) + and HAVE_IMMORTAL + ): + # On Python 3.12+, None/True/False are immortal, and we can skip inc ref + return + + if isinstance(op.src, LoadLiteral) and HAVE_IMMORTAL: + value = op.src.value + # We can skip inc ref for immortal literals on Python 3.12+ + if type(value) is int and -5 <= value <= 256: + # Small integers are immortal + return + src = self.reg(op.src) self.emit_inc_ref(src, op.src.type) @@ -583,6 +657,9 @@ def visit_box(self, op: Box) -> None: self.emitter.emit_box(self.reg(op.src), self.reg(op), op.src.type, can_borrow=True) def visit_cast(self, op: Cast) -> None: + if op.is_unchecked and op.is_borrowed: + self.emit_line(f"{self.reg(op)} = {self.reg(op.src)};") + return branch = self.next_branch() handler = None if branch is not None: @@ -722,6 +799,8 @@ def visit_load_mem(self, op: LoadMem) -> None: # TODO: we shouldn't dereference to type that are pointer type so far type = self.ctype(op.type) self.emit_line(f"{dest} = *({type} *){src};") + if not op.is_borrowed: + self.emit_inc_ref(dest, op.type) def visit_set_mem(self, op: SetMem) -> None: dest = self.reg(op.dest) @@ -736,7 +815,7 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None: dest = self.reg(op) src = self.reg(op.src) # TODO: support tuple type - assert isinstance(op.src_type, RStruct) + assert isinstance(op.src_type, RStruct), op.src_type assert op.field in op.src_type.names, "Invalid field name." self.emit_line( "{} = ({})&(({} *){})->{};".format( @@ -744,6 +823,31 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None: ) ) + def visit_set_element(self, op: SetElement) -> None: + dest = self.reg(op) + item = self.reg(op.item) + field = op.field + if isinstance(op.src, Undef): + # First assignment to an undefined struct is trivial. + self.emit_line(f"{dest}.{field} = {item};") + else: + # In the general case create a copy of the struct with a single + # item modified. + # + # TODO: Can we do better if only a subset of fields are initialized? + # TODO: Make this less verbose in the common case + # TODO: Support tuples (or use RStruct for tuples)? + src = self.reg(op.src) + src_type = op.src.type + assert isinstance(src_type, RStruct), src_type + init_items = [] + for n in src_type.names: + if n != field: + init_items.append(f"{src}.{n}") + else: + init_items.append(item) + self.emit_line(f"{dest} = ({self.ctype(src_type)}) {{ {', '.join(init_items)} }};") + def visit_load_address(self, op: LoadAddress) -> None: typ = op.type dest = self.reg(op) @@ -798,6 +902,8 @@ def reg(self, reg: Value) -> str: elif r == "nan": return "NAN" return r + elif isinstance(reg, CString): + return '"' + encode_c_string_literal(reg.value) + '"' else: return self.emitter.reg(reg) @@ -838,7 +944,7 @@ def emit_attribute_error(self, op: Branch, class_name: str, attr: str) -> None: self.source_path.replace("\\", "\\\\"), op.traceback_entry[0], class_name, - attr, + attr.removeprefix(GENERATOR_ATTRIBUTE_PREFIX), op.traceback_entry[1], globals_static, ) @@ -859,3 +965,30 @@ def emit_unsigned_int_cast(self, type: RType) -> str: return "(uint64_t)" else: return "" + + +_translation_table: Final[dict[int, str]] = {} + + +def encode_c_string_literal(b: bytes) -> str: + """Convert bytestring to the C string literal syntax (with necessary escaping). + + For example, b'foo\n' gets converted to 'foo\\n' (note that double quotes are not added). + """ + if not _translation_table: + # Initialize the translation table on the first call. + d = { + ord("\n"): "\\n", + ord("\r"): "\\r", + ord("\t"): "\\t", + ord('"'): '\\"', + ord("\\"): "\\\\", + } + for i in range(256): + if i not in d: + if i < 32 or i >= 127: + d[i] = "\\x%.2x" % i + else: + d[i] = chr(i) + _translation_table.update(str.maketrans(d)) + return b.decode("latin1").translate(_translation_table) diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 1d8708912de5d..8dd9f750a920d 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -1,13 +1,15 @@ """Generate C code for a Python C extension module from Python source code.""" # FIXME: Basically nothing in this file operates on the level of a -# single module and it should be renamed. +# single module and it should be renamed. from __future__ import annotations import json import os -from typing import Iterable, List, Optional, Tuple, TypeVar +import sys +from collections.abc import Iterable +from typing import Optional, TypeVar from mypy.build import ( BuildResult, @@ -24,11 +26,16 @@ 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.analysis.capsule_deps import find_implicit_capsule_dependencies 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 -from mypyc.codegen.emitfunc import generate_native_function, native_function_header +from mypyc.codegen.emitclass import generate_class, generate_class_reuse, generate_class_type_decl +from mypyc.codegen.emitfunc import ( + generate_native_function, + native_function_doc_initializer, + native_function_header, +) from mypyc.codegen.emitwrapper import ( generate_legacy_wrapper_function, generate_wrapper_function, @@ -37,6 +44,7 @@ ) from mypyc.codegen.literals import Literals from mypyc.common import ( + IS_FREE_THREADED, MODULE_PREFIX, PREFIX, RUNTIME_C_FILES, @@ -44,10 +52,8 @@ TYPE_VAR_PREFIX, shared_lib_name, short_id_from_name, - 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 @@ -60,11 +66,13 @@ from mypyc.transform.copy_propagation import do_copy_propagation from mypyc.transform.exceptions import insert_exception_handling from mypyc.transform.flag_elimination import do_flag_elimination +from mypyc.transform.log_trace import insert_event_trace_logging from mypyc.transform.lower import lower_ir from mypyc.transform.refcount import insert_ref_count_opcodes +from mypyc.transform.spill import insert_spills from mypyc.transform.uninit import insert_uninit_checks -# All of the modules being compiled are divided into "groups". A group +# All the modules being compiled are divided into "groups". A group # is a set of modules that are placed into the same shared library. # Two common configurations are that every module is placed in a group # by itself (fully separate compilation) and that every module is @@ -84,11 +92,11 @@ # its modules along with the name of the group. (Which can be None # only if we are compiling only a single group with a single file in it # and not using shared libraries). -Group = Tuple[List[BuildSource], Optional[str]] -Groups = List[Group] +Group = tuple[list[BuildSource], Optional[str]] +Groups = list[Group] # A list of (file name, file contents) pairs. -FileContents = List[Tuple[str, str]] +FileContents = list[tuple[str, str]] class MarkedDeclaration: @@ -154,10 +162,10 @@ 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 + # Check that all the source files are present and as # expected. The main situation where this would come up is the # user deleting the build directory without deleting # .mypy_cache, which we should handle gracefully. @@ -208,8 +216,8 @@ def compile_scc_to_ir( ) -> ModuleIRs: """Compile an SCC into ModuleIRs. - Any modules that this SCC depends on must have either compiled or - loaded from a cache into mapper. + Any modules that this SCC depends on must have either been compiled, + type checked, or loaded from a cache into mapper. Arguments: scc: The list of MypyFiles to compile @@ -229,16 +237,33 @@ def compile_scc_to_ir( if errors.num_errors > 0: return modules + env_user_functions = {} + for module in modules.values(): + for cls in module.classes: + if cls.env_user_function: + env_user_functions[cls.env_user_function] = cls + for module in modules.values(): for fn in module.functions: - # Insert uninit checks. + # Insert checks for uninitialized values. insert_uninit_checks(fn) # Insert exception handling. insert_exception_handling(fn) - # Insert refcount handling. + # Insert reference count handling. insert_ref_count_opcodes(fn) + + if fn in env_user_functions: + insert_spills(fn, env_user_functions[fn]) + + if compiler_options.log_trace: + insert_event_trace_logging(fn, compiler_options) + # Switch to lower abstraction level IR. lower_ir(fn, compiler_options) + # Calculate implicit module dependencies (needed for librt) + capsules = find_implicit_capsule_dependencies(fn) + if capsules is not None: + module.capsules.update(capsules) # Perform optimizations. do_copy_propagation(fn, compiler_options) do_flag_elimination(fn, compiler_options) @@ -260,13 +285,13 @@ def compile_modules_to_ir( # Process the graph by SCC in topological order, like we do in mypy.build for scc in sorted_components(result.graph): - scc_states = [result.graph[id] for id in scc] + scc_states = [result.graph[id] for id in scc.mod_ids] trees = [st.tree for st in scc_states if st.id in mapper.group_map and st.tree] if not trees: continue - fresh = all(id not in result.manager.rechecked_modules for id in scc) + fresh = all(id not in result.manager.rechecked_modules for id in scc.mod_ids) if fresh: load_scc_from_cache(trees, result, mapper, deser_ctx) else: @@ -294,7 +319,10 @@ def compile_ir_to_c( for source in sources } - names = NameGenerator([[source.module for source in sources] for sources, _ in groups]) + names = NameGenerator( + [[source.module for source in sources] for sources, _ in groups], + separate=compiler_options.separate, + ) # Generate C code for each compilation group. Each group will be # compiled into a separate extension module. @@ -318,7 +346,8 @@ def compile_ir_to_c( def get_ir_cache_name(id: str, path: str, options: Options) -> str: meta_path, _, _ = get_cache_names(id, path, options) - return meta_path.replace(".meta.json", ".ir.json") + # Mypy uses JSON cache even with --fixed-format-cache (for now). + return meta_path.replace(".meta.json", ".ir.json").replace(".meta.ff", ".ir.json") def get_state_ir_cache_name(state: State) -> str: @@ -346,7 +375,7 @@ def write_cache( cache are in sync and refer to the same version of the code. This is particularly important if mypyc crashes/errors/is stopped after mypy has written its cache but before mypyc has. - * The hashes of all of the source file outputs for the group + * The hashes of all the source file outputs for the group the module is in. This is so that the module will be recompiled if the source outputs are missing. """ @@ -369,11 +398,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() @@ -398,7 +427,7 @@ def load_scc_from_cache( def compile_modules_to_c( result: BuildResult, compiler_options: CompilerOptions, errors: Errors, groups: Groups -) -> tuple[ModuleIRs, list[FileContents]]: +) -> tuple[ModuleIRs, list[FileContents], Mapper]: """Compile Python module(s) to the source of Python C extension modules. This generates the source code for the "shared library" module @@ -406,7 +435,7 @@ def compile_modules_to_c( Each shared library module provides, for each module in its group, a PyCapsule containing an initialization function. Additionally, it provides a capsule containing an export table of - pointers to all of the group's functions and static variables. + pointers to all the group's functions and static variables. Arguments: result: The BuildResult from the mypy front-end @@ -428,19 +457,19 @@ def compile_modules_to_c( modules = compile_modules_to_ir(result, mapper, compiler_options, errors) if errors.num_errors > 0: - return {}, [] + return {}, [], Mapper({}) ctext = compile_ir_to_c(groups, modules, result, mapper, compiler_options) write_cache(modules, result, group_map, ctext) - return modules, [ctext[name] for _, name in groups] + return modules, [ctext[name] for _, name in groups], mapper def generate_function_declaration(fn: FuncIR, emitter: Emitter) -> None: emitter.context.declarations[emitter.native_function_name(fn.decl)] = HeaderDeclaration( f"{native_function_header(fn.decl, emitter)};", needs_export=True ) - if fn.name != TOP_LEVEL_NAME: + if fn.name != TOP_LEVEL_NAME and not fn.internal: if is_fastcall_supported(fn, emitter.capi_version): emitter.context.declarations[PREFIX + fn.cname(emitter.names)] = HeaderDeclaration( f"{wrapper_function_header(fn, emitter.names)};" @@ -481,7 +510,7 @@ def __init__( The code for a compilation group contains an internal and an external .h file, and then one .c if not in multi_file mode or - one .c file per module if in multi_file mode.) + one .c file per module if in multi_file mode. Arguments: modules: (name, ir) pairs for each module in the group @@ -489,8 +518,7 @@ def __init__( group_name: The name of the group (or None if this is single-module compilation) group_map: A map of modules to their group names names: The name generator for the compilation - multi_file: Whether to put each module in its own source file regardless - of group structure. + compiler_options: Mypyc specific options, including multi_file mode """ self.modules = modules self.source_paths = source_paths @@ -503,6 +531,9 @@ def __init__( self.use_shared_lib = group_name is not None self.compiler_options = compiler_options self.multi_file = compiler_options.multi_file + # Multi-phase init is needed to enable free-threading. In the future we'll + # probably want to enable it always, but we'll wait until it's stable. + self.multi_phase_init = IS_FREE_THREADED @property def group_suffix(self) -> str: @@ -553,7 +584,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: for fn in module.functions: emitter.emit_line() generate_native_function(fn, emitter, self.source_paths[module_name], module_name) - if fn.name != TOP_LEVEL_NAME: + if fn.name != TOP_LEVEL_NAME and not fn.internal: emitter.emit_line() if is_fastcall_supported(fn, emitter.capi_version): generate_wrapper_function( @@ -564,7 +595,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: fn, emitter, self.source_paths[module_name], module_name ) if multi_file: - name = f"__native_{emitter.names.private_name(module_name)}.c" + name = f"__native_{exported_name(module_name)}.c" file_contents.append((name, "".join(emitter.fragments))) # The external header file contains type declarations while @@ -576,10 +607,14 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: ext_declarations.emit_line(f"#define MYPYC_NATIVE{self.group_suffix}_H") ext_declarations.emit_line("#include ") ext_declarations.emit_line("#include ") + if self.compiler_options.depends_on_librt_internal: + ext_declarations.emit_line("#include ") + if any("librt.base64" in mod.capsules for mod in self.modules.values()): + ext_declarations.emit_line("#include ") declarations = Emitter(self.context) - declarations.emit_line(f"#ifndef MYPYC_NATIVE_INTERNAL{self.group_suffix}_H") - declarations.emit_line(f"#define MYPYC_NATIVE_INTERNAL{self.group_suffix}_H") + declarations.emit_line(f"#ifndef MYPYC_LIBRT_INTERNAL{self.group_suffix}_H") + declarations.emit_line(f"#define MYPYC_LIBRT_INTERNAL{self.group_suffix}_H") declarations.emit_line("#include ") declarations.emit_line("#include ") declarations.emit_line(f'#include "__native{self.short_group_suffix}.h"') @@ -591,6 +626,8 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: self.declare_finals(module_name, module.final_names, declarations) for cl in module.classes: generate_class_type_decl(cl, emitter, ext_declarations, declarations) + if cl.reuse_freed_instance: + generate_class_reuse(cl, emitter, ext_declarations, declarations) self.declare_type_vars(module_name, module.type_var_names, declarations) for fn in module.functions: generate_function_declaration(fn, declarations) @@ -614,7 +651,7 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: decls = ext_declarations if declaration.is_type else declarations if not declaration.is_type: decls.emit_lines(f"extern {declaration.decl[0]}", *declaration.decl[1:]) - # If there is a definition, emit it. Otherwise repeat the declaration + # If there is a definition, emit it. Otherwise, repeat the declaration # (without an extern). if declaration.defn: emitter.emit_lines(*declaration.defn) @@ -624,7 +661,8 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: decls.emit_lines(*declaration.decl) if self.group_name: - self.generate_export_table(ext_declarations, emitter) + if self.compiler_options.separate: + self.generate_export_table(ext_declarations, emitter) self.generate_shared_lib_init(emitter) @@ -742,13 +780,13 @@ def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) -> def generate_shared_lib_init(self, emitter: Emitter) -> None: """Generate the init function for a shared library. - A shared library contains all of the actual code for a + A shared library contains all the actual code for a compilation group. The init function is responsible for creating Capsules that wrap pointers to the initialization function of all the real init functions for modules in this shared library as well as - the export table containing all of the exported functions and + the export table containing all the exported functions and values from all the modules. These capsules are stored in attributes of the shared library. @@ -756,57 +794,56 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: assert self.group_name is not None emitter.emit_line() + + short_name = shared_lib_name(self.group_name).split(".")[-1] + emitter.emit_lines( - "PyMODINIT_FUNC PyInit_{}(void)".format( - shared_lib_name(self.group_name).split(".")[-1] - ), + f"static int exec_{short_name}(PyObject *module)", "{", - ( - 'static PyModuleDef def = {{ PyModuleDef_HEAD_INIT, "{}", NULL, -1, NULL, NULL }};'.format( - shared_lib_name(self.group_name) - ) - ), "int res;", "PyObject *capsule;", "PyObject *tmp;", - "static PyObject *module;", - "if (module) {", - "Py_INCREF(module);", - "return module;", - "}", - "module = PyModule_Create(&def);", - "if (!module) {", - "goto fail;", - "}", "", ) - emitter.emit_lines( - 'capsule = PyCapsule_New(&exports, "{}.exports", NULL);'.format( - shared_lib_name(self.group_name) - ), - "if (!capsule) {", - "goto fail;", - "}", - 'res = PyObject_SetAttrString(module, "exports", capsule);', - "Py_DECREF(capsule);", - "if (res < 0) {", - "goto fail;", - "}", - "", - ) + if self.compiler_options.separate: + emitter.emit_lines( + 'capsule = PyCapsule_New(&exports, "{}.exports", NULL);'.format( + shared_lib_name(self.group_name) + ), + "if (!capsule) {", + "goto fail;", + "}", + 'res = PyObject_SetAttrString(module, "exports", capsule);', + "Py_DECREF(capsule);", + "if (res < 0) {", + "goto fail;", + "}", + "", + ) for mod in self.modules: name = exported_name(mod) + if self.multi_phase_init: + capsule_func_prefix = "CPyExec_" + capsule_name_prefix = "exec_" + emitter.emit_line(f"extern int CPyExec_{name}(PyObject *);") + else: + capsule_func_prefix = "CPyInit_" + capsule_name_prefix = "init_" + emitter.emit_line(f"extern PyObject *CPyInit_{name}(void);") emitter.emit_lines( - f"extern PyObject *CPyInit_{name}(void);", - 'capsule = PyCapsule_New((void *)CPyInit_{}, "{}.init_{}", NULL);'.format( - name, shared_lib_name(self.group_name), name + 'capsule = PyCapsule_New((void *){}{}, "{}.{}{}", NULL);'.format( + capsule_func_prefix, + name, + shared_lib_name(self.group_name), + capsule_name_prefix, + name, ), "if (!capsule) {", "goto fail;", "}", - f'res = PyObject_SetAttrString(module, "init_{name}", capsule);', + f'res = PyObject_SetAttrString(module, "{capsule_name_prefix}{name}", capsule);', "Py_DECREF(capsule);", "if (res < 0) {", "goto fail;", @@ -832,7 +869,56 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: "", ) - emitter.emit_lines("return module;", "fail:", "Py_XDECREF(module);", "return NULL;", "}") + emitter.emit_lines("return 0;", "fail:", "return -1;", "}") + + if self.multi_phase_init: + emitter.emit_lines( + f"static PyModuleDef_Slot slots_{short_name}[] = {{", + f"{{Py_mod_exec, exec_{short_name}}},", + "{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},", + "{Py_mod_gil, Py_MOD_GIL_NOT_USED},", + "{0, NULL},", + "};", + ) + + size = 0 if self.multi_phase_init else -1 + emitter.emit_lines( + f"static PyModuleDef module_def_{short_name} = {{", + "PyModuleDef_HEAD_INIT,", + f'.m_name = "{shared_lib_name(self.group_name)}",', + ".m_doc = NULL,", + f".m_size = {size},", + ".m_methods = NULL,", + ) + if self.multi_phase_init: + emitter.emit_line(f".m_slots = slots_{short_name},") + emitter.emit_line("};") + + if self.multi_phase_init: + emitter.emit_lines( + f"PyMODINIT_FUNC PyInit_{short_name}(void) {{", + f"return PyModuleDef_Init(&module_def_{short_name});", + "}", + ) + else: + emitter.emit_lines( + f"PyMODINIT_FUNC PyInit_{short_name}(void) {{", + "static PyObject *module = NULL;", + "if (module) {", + "Py_INCREF(module);", + "return module;", + "}", + f"module = PyModule_Create(&module_def_{short_name});", + "if (!module) {", + "return NULL;", + "}", + f"if (exec_{short_name}(module) < 0) {{", + "Py_DECREF(module);", + "return NULL;", + "}", + "return module;", + "}", + ) def generate_globals_init(self, emitter: Emitter) -> None: emitter.emit_lines( @@ -857,8 +943,43 @@ def generate_globals_init(self, emitter: Emitter) -> None: def generate_module_def(self, emitter: Emitter, module_name: str, module: ModuleIR) -> None: """Emit the PyModuleDef struct for a module and the module init function.""" - # Emit module methods module_prefix = emitter.names.private_name(module_name) + self.emit_module_methods(emitter, module_name, module_prefix, module) + self.emit_module_exec_func(emitter, module_name, module_prefix, module) + + # If using multi-phase init and a shared lib, parts of module definition + # will happen in the shim modules, so we skip some steps here. + if not (self.multi_phase_init and self.use_shared_lib): + if self.multi_phase_init: + self.emit_module_def_slots(emitter, module_prefix, module_name) + self.emit_module_def_struct(emitter, module_name, module_prefix) + self.emit_module_init_func(emitter, module_name, module_prefix) + + def emit_module_def_slots( + self, emitter: Emitter, module_prefix: str, module_name: str + ) -> None: + name = f"{module_prefix}_slots" + exec_name = f"CPyExec_{exported_name(module_name)}" + + emitter.emit_line(f"static PyModuleDef_Slot {name}[] = {{") + emitter.emit_line(f"{{Py_mod_exec, {exec_name}}},") + if sys.version_info >= (3, 12): + # Multiple interpreter support requires not using any C global state, + # which we don't support yet. + emitter.emit_line( + "{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}," + ) + if sys.version_info >= (3, 13): + # Declare support for free-threading to enable experimentation, + # even if we don't properly support it. + emitter.emit_line("{Py_mod_gil, Py_MOD_GIL_NOT_USED},") + emitter.emit_line("{0, NULL},") + emitter.emit_line("};") + + def emit_module_methods( + self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR + ) -> None: + """Emit module methods (the static PyMethodDef table).""" emitter.emit_line(f"static PyMethodDef {module_prefix}module_methods[] = {{") for fn in module.functions: if fn.class_name is not None or fn.name == TOP_LEVEL_NAME: @@ -868,58 +989,65 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module flag = "METH_FASTCALL" else: flag = "METH_VARARGS" + doc = native_function_doc_initializer(fn) emitter.emit_line( ( '{{"{name}", (PyCFunction){prefix}{cname}, {flag} | METH_KEYWORDS, ' - "NULL /* docstring */}}," - ).format(name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag) + "PyDoc_STR({doc}) /* docstring */}}," + ).format( + name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag, doc=doc + ) ) emitter.emit_line("{NULL, NULL, 0, NULL}") emitter.emit_line("};") emitter.emit_line() - # Emit module definition struct + def emit_module_def_struct( + self, emitter: Emitter, module_name: str, module_prefix: str + ) -> None: + """Emit the static module definition struct (PyModuleDef).""" emitter.emit_lines( f"static struct PyModuleDef {module_prefix}module = {{", "PyModuleDef_HEAD_INIT,", f'"{module_name}",', "NULL, /* docstring */", - "-1, /* size of per-interpreter state of the module,", - " or -1 if the module keeps state in global variables. */", - f"{module_prefix}module_methods", - "};", + "0, /* size of per-interpreter state of the module */", + f"{module_prefix}module_methods,", ) - emitter.emit_line() - # Emit module init function. If we are compiling just one module, this - # will be the C API init function. If we are compiling 2+ modules, we - # generate a shared library for the modules and shims that call into - # the shared library, and in this case we use an internal module - # initialized function that will be called by the shim. - if not self.use_shared_lib: - declaration = f"PyMODINIT_FUNC PyInit_{module_name}(void)" + if self.multi_phase_init and not self.use_shared_lib: + slots_name = f"{module_prefix}_slots" + emitter.emit_line(f"{slots_name}, /* m_slots */") else: - declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)" + emitter.emit_line("NULL,") + emitter.emit_line("};") + emitter.emit_line() + + def emit_module_exec_func( + self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR + ) -> None: + """Emit the module exec function. + + If we are compiling just one module, this will be the normal C API + exec function. If we are compiling 2+ modules, we generate a shared + library for the modules and shims that call into the shared + library, and in this case the shared module defines an internal + exec function for each module and these will be called by the shims + via Capsules. + """ + declaration = f"int CPyExec_{exported_name(module_name)}(PyObject *module)" + module_static = self.module_internal_static_name(module_name, emitter) emitter.emit_lines(declaration, "{") + if self.compiler_options.depends_on_librt_internal: + emitter.emit_line("if (import_librt_internal() < 0) {") + emitter.emit_line("return -1;") + emitter.emit_line("}") + if "librt.base64" in module.capsules: + emitter.emit_line("if (import_librt_base64() < 0) {") + emitter.emit_line("return -1;") + emitter.emit_line("}") emitter.emit_line("PyObject* modname = NULL;") - # Store the module reference in a static and return it when necessary. - # This is separate from the *global* reference to the module that will - # be populated when it is imported by a compiled module. We want that - # reference to only be populated when the module has been successfully - # imported, whereas this we want to have to stop a circular import. - module_static = self.module_internal_static_name(module_name, emitter) - - emitter.emit_lines( - f"if ({module_static}) {{", - f"Py_INCREF({module_static});", - f"return {module_static};", - "}", - ) - - emitter.emit_lines( - f"{module_static} = PyModule_Create(&{module_prefix}module);", - f"if (unlikely({module_static} == NULL))", - " goto fail;", - ) + if self.multi_phase_init: + emitter.emit_line(f"{module_static} = module;") emitter.emit_line( f'modname = PyObject_GetAttrString((PyObject *){module_static}, "__name__");' ) @@ -931,6 +1059,12 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module " goto fail;", ) + if self.multi_phase_init: + emitter.emit_lines( + f"if (PyModule_AddFunctions(module, {module_prefix}module_methods) < 0)", + " goto fail;", + ) + # HACK: Manually instantiate generated classes here type_structs: list[str] = [] for cl in module.classes: @@ -942,6 +1076,8 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module "(PyObject *){t}_template, NULL, modname);".format(t=type_struct) ) emitter.emit_lines(f"if (unlikely(!{type_struct}))", " goto fail;") + name_prefix = cl.name_prefix(emitter.names) + emitter.emit_line(f"CPyDef_{name_prefix}_trait_vtable_setup();") emitter.emit_lines("if (CPyGlobalsInit() < 0)", " goto fail;") @@ -949,8 +1085,12 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module emitter.emit_lines("Py_DECREF(modname);") - emitter.emit_line(f"return {module_static};") - emitter.emit_lines("fail:", f"Py_CLEAR({module_static});", "Py_CLEAR(modname);") + emitter.emit_line("return 0;") + emitter.emit_lines("fail:") + if self.multi_phase_init: + emitter.emit_lines(f"{module_static} = NULL;", "Py_CLEAR(modname);") + else: + emitter.emit_lines(f"Py_CLEAR({module_static});", "Py_CLEAR(modname);") for name, typ in module.final_names: static_name = emitter.static_name(name, module_name) emitter.emit_dec_ref(static_name, typ, is_xdec=True) @@ -960,9 +1100,50 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module # so we have to decref them for t in type_structs: emitter.emit_line(f"Py_CLEAR({t});") - emitter.emit_line("return NULL;") + emitter.emit_line("return -1;") emitter.emit_line("}") + def emit_module_init_func( + self, emitter: Emitter, module_name: str, module_prefix: str + ) -> None: + if not self.use_shared_lib: + declaration = f"PyMODINIT_FUNC PyInit_{module_name}(void)" + else: + declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)" + emitter.emit_lines(declaration, "{") + + if self.multi_phase_init: + def_name = f"{module_prefix}module" + emitter.emit_line(f"return PyModuleDef_Init(&{def_name});") + emitter.emit_line("}") + return + + exec_func = f"CPyExec_{exported_name(module_name)}" + + # Store the module reference in a static and return it when necessary. + # This is separate from the *global* reference to the module that will + # be populated when it is imported by a compiled module. We want that + # reference to only be populated when the module has been successfully + # imported, whereas this we want to have to stop a circular import. + module_static = self.module_internal_static_name(module_name, emitter) + + emitter.emit_lines( + f"if ({module_static}) {{", + f"Py_INCREF({module_static});", + f"return {module_static};", + "}", + ) + + emitter.emit_lines( + f"{module_static} = PyModule_Create(&{module_prefix}module);", + f"if (unlikely({module_static} == NULL))", + " goto fail;", + ) + emitter.emit_lines(f"if ({exec_func}({module_static}) != 0)", " goto fail;") + emitter.emit_line(f"return {module_static};") + emitter.emit_lines("fail:", "return NULL;") + emitter.emit_lines("}") + def generate_top_level_call(self, module: ModuleIR, emitter: Emitter) -> None: """Generate call to function representing module top level.""" # Optimization: we tend to put the top level last, so reverse iterate @@ -1026,7 +1207,7 @@ def declare_internal_globals(self, module_name: str, emitter: Emitter) -> None: self.declare_global("PyObject *", static_name) def module_internal_static_name(self, module_name: str, emitter: Emitter) -> str: - return emitter.static_name(module_name + "_internal", None, prefix=MODULE_PREFIX) + return emitter.static_name(module_name + "__internal", None, prefix=MODULE_PREFIX) def declare_module(self, module_name: str, emitter: Emitter) -> None: # We declare two globals for each compiled module: @@ -1075,20 +1256,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") @@ -1120,9 +1287,9 @@ def is_fastcall_supported(fn: FuncIR, capi_version: tuple[int, int]) -> bool: if fn.class_name is not None: if fn.name == "__call__": # We can use vectorcalls (PEP 590) when supported - return use_vectorcall(capi_version) - # TODO: Support fastcall for __init__. - return fn.name != "__init__" + return True + # TODO: Support fastcall for __init__ and __new__. + return fn.name != "__init__" and fn.name != "__new__" return True diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py index 45c6c7a05867f..2e5d7efa4e988 100644 --- a/mypyc/codegen/emitwrapper.py +++ b/mypyc/codegen/emitwrapper.py @@ -12,7 +12,7 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from mypy.nodes import ARG_NAMED, ARG_NAMED_OPT, ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, ArgKind from mypy.operators import op_methods_to_symbols, reverse_op_method_names, reverse_op_methods @@ -24,7 +24,6 @@ NATIVE_PREFIX, PREFIX, bitmap_name, - use_vectorcall, ) from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FUNC_STATICMETHOD, FuncIR, RuntimeArg @@ -62,6 +61,7 @@ def wrapper_function_header(fn: FuncIR, names: NameGenerator) -> str: See comment above for a summary of the arguments. """ + assert not fn.internal return ( "PyObject *{prefix}{name}(" "PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames)" @@ -173,7 +173,7 @@ def generate_wrapper_function( arg_ptrs += [f"&obj_{groups[ARG_STAR2][0].name}" if groups[ARG_STAR2] else "NULL"] arg_ptrs += [f"&obj_{arg.name}" for arg in reordered_args] - if fn.name == "__call__" and use_vectorcall(emitter.capi_version): + if fn.name == "__call__": nargs = "PyVectorcall_NARGS(nargs)" else: nargs = "nargs" @@ -238,7 +238,7 @@ def generate_legacy_wrapper_function( real_args = list(fn.args) if fn.sig.num_bitmap_args: real_args = real_args[: -fn.sig.num_bitmap_args] - if fn.class_name and fn.decl.kind != FUNC_STATICMETHOD: + if fn.class_name and (fn.decl.name == "__new__" or fn.decl.kind != FUNC_STATICMETHOD): arg = real_args.pop(0) emitter.emit_line(f"PyObject *obj_{arg.name} = self;") diff --git a/mypyc/codegen/literals.py b/mypyc/codegen/literals.py index 1f0c3bc6ec7bd..4cd41e0f4d320 100644 --- a/mypyc/codegen/literals.py +++ b/mypyc/codegen/literals.py @@ -1,12 +1,12 @@ from __future__ import annotations -from typing import Final, FrozenSet, Tuple, Union +from typing import Final, Union from typing_extensions import TypeGuard # Supported Python literal types. All tuple / frozenset items must have supported # literal types as well, but we can't represent the type precisely. LiteralValue = Union[ - str, bytes, int, bool, float, complex, Tuple[object, ...], FrozenSet[object], None + str, bytes, int, bool, float, complex, tuple[object, ...], frozenset[object], None ] @@ -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/common.py b/mypyc/common.py index 31567c689c348..2de63c09bb2ce 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -2,7 +2,7 @@ import sys import sysconfig -from typing import Any, Dict, Final +from typing import Any, Final from mypy.util import unnamed_function @@ -15,6 +15,7 @@ MODULE_PREFIX: Final = "CPyModule_" # Cached modules TYPE_VAR_PREFIX: Final = "CPyTypeVar_" # Type variables when using new-style Python 3.12 syntax ATTR_PREFIX: Final = "_" # Attributes +FAST_PREFIX: Final = "__mypyc_fast_" # Optimized methods in non-extension classes ENV_ATTR_NAME: Final = "__mypyc_env__" NEXT_LABEL_ATTR_NAME: Final = "__mypyc_next_label__" @@ -22,6 +23,7 @@ LAMBDA_NAME: Final = "__mypyc_lambda__" PROPSET_PREFIX: Final = "__mypyc_setter__" SELF_NAME: Final = "__mypyc_self__" +GENERATOR_ATTRIBUTE_PREFIX: Final = "__mypyc_generator_attribute__" # Max short int we accept as a literal is based on 32-bit platforms, # so that we can just always emit the same code. @@ -82,8 +84,18 @@ "pythonsupport.c", ] +# Python 3.12 introduced immortal objects, specified via a special reference count +# value. The reference counts of immortal objects are normally not modified, but it's +# not strictly wrong to modify them. See PEP 683 for more information, but note that +# some details in the PEP are out of date. +HAVE_IMMORTAL: Final = sys.version_info >= (3, 12) -JsonDict = Dict[str, Any] +# Are we running on a free-threaded build (GIL disabled)? This implies that +# we are on Python 3.13 or later. +IS_FREE_THREADED: Final = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) + + +JsonDict = dict[str, Any] def shared_lib_name(group_name: str) -> str: @@ -100,16 +112,6 @@ def short_name(name: str) -> str: return name -def use_vectorcall(capi_version: tuple[int, int]) -> bool: - # We can use vectorcalls to make calls on Python 3.8+ (PEP 590). - return capi_version >= (3, 8) - - -def use_method_vectorcall(capi_version: tuple[int, int]) -> bool: - # We can use a dedicated vectorcall API to call methods on Python 3.9+. - return capi_version >= (3, 9) - - def get_id_from_name(name: str, fullname: str, line: int) -> str: """Create a unique id for a function. diff --git a/mypyc/crash.py b/mypyc/crash.py index 19136ea2f1dee..1227aa8978af2 100644 --- a/mypyc/crash.py +++ b/mypyc/crash.py @@ -2,8 +2,9 @@ import sys import traceback +from collections.abc import Iterator from contextlib import contextmanager -from typing import Iterator, NoReturn +from typing import NoReturn @contextmanager 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/dev-intro.md b/mypyc/doc/dev-intro.md index d11df7068e91c..5b248214a3ebc 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -4,6 +4,14 @@ This is a short introduction aimed at anybody who is interested in contributing to mypyc, or anybody who is curious to understand how mypyc works internally. +## Developer Documentation in the Wiki + +We have more mypyc developer documentation in our +[wiki](https://github.com/python/mypy/wiki/Developer-Guides). + +For basic information common to both mypy and mypyc development, refer +to the [mypy wiki home page](https://github.com/python/mypy/wiki). + ## Key Differences from Python Code compiled using mypyc is often much faster than CPython since it @@ -51,11 +59,9 @@ good error message. Here are some major things that aren't yet supported in compiled code: -* Many dunder methods (only some work, such as `__init__` and `__eq__`) +* Some dunder methods (most work though) * Monkey patching compiled functions or classes * General multiple inheritance (a limited form is supported) -* Named tuple defined using the class-based syntax -* Defining protocols We are generally happy to accept contributions that implement new Python features. @@ -73,16 +79,16 @@ compiled code. For example, you may want to do interactive testing or to run benchmarks. This is also handy if you want to inspect the generated C code (see Inspecting Generated C). -Run `mypyc` to compile a module to a C extension using your +Run `python -m mypyc` to compile a module to a C extension using your development version of mypyc: ``` -$ mypyc program.py +$ python -m mypyc program.py ``` This will generate a C extension for `program` in the current working -directory. For example, on a Linux system the generated file may be -called `program.cpython-37m-x86_64-linux-gnu.so`. +directory. For example, on a macOS system the generated file may be +called `program.cpython-313-darwin.so`. Since C extensions can't be run as programs, use `python3 -c` to run the compiled module as a program: @@ -95,7 +101,7 @@ Note that `__name__` in `program.py` will now be `program`, not `__main__`! You can manually delete the C extension to get back to an interpreted -version (this example works on Linux): +version (this example works on macOS or Linux): ``` $ rm program.*.so @@ -114,9 +120,9 @@ extensions) in compiled code. Mypyc will only make compiled code faster. To see a significant speedup, you must make sure that most of the time is spent in compiled -code -- and not in libraries, for example. +code, and not in libraries or I/O. -Mypyc has these passes: +Mypyc has these main passes: * Type check the code using mypy and infer types for variables and expressions. This produces a mypy AST (defined in `mypy.nodes`) and @@ -193,15 +199,19 @@ information. See the test cases in `mypyc/test-data/irbuild-basic.test` for examples of what the IR looks like in a pretty-printed form. -## Testing overview +## Testing Overview Most mypyc test cases are defined in the same format (`.test`) as used for test cases for mypy. Look at mypy developer documentation for a general overview of how things work. Test cases live under `mypyc/test-data/`, and you can run all mypyc tests via `pytest --q mypyc`. If you don't make changes to code under `mypy/`, it's not + 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 @@ -219,12 +229,12 @@ pretty-printed IR into `build/ops.txt`. This is the final IR that includes the output from exception and reference count handling insertion passes. -We also have tests that verify the generate IR +We also have tests that verify the generated IR (`mypyc/test-data/irbuild-*.text`). ## Type-checking Mypyc -`./runtests.py self` type checks mypy and mypyc. This is pretty slow, +`./runtests.py self` type checks mypy and mypyc. This is a little slow, however, since it's using an uncompiled mypy. Installing a released version of mypy using `pip` (which is compiled) @@ -280,12 +290,28 @@ under `mypyc/lib-rt`. ## Inspecting Generated C -It's often useful to inspect the C code genenerate by mypyc to debug +It's often useful to inspect the C code generated by mypyc to debug issues. Mypyc stores the generated C code as `build/__native.c`. Compiled native functions have the prefix `CPyDef_`, while wrapper functions used for calling functions from interpreted Python code have the `CPyPy_` prefix. +When running a test, the first test failure will copy generated C code +into the `.mypyc_test_output` directory. You will see something like +this in the test output: + +``` +... +---------------------------- Captured stderr call ----------------------------- + +Generated files: /Users/me/src/mypy/.mypyc_test_output (for first failure only) + +... +``` + +You can also run pytest with `--mypyc-showc` to display C code on every +test failure. + ## Other Important Limitations All of these limitations will likely be fixed in the future: @@ -307,7 +333,7 @@ number of components at once, insensitive to the particular details of the IR), but there really is no substitute for running code. You can also write tests that test the generated IR, however. -### Tests that compile and run code +### Tests That Compile and Run Code Test cases that compile and run code are located in `mypyc/test-data/run*.test` and the test runner is in @@ -360,7 +386,74 @@ Test cases can also have a `[out]` section, which specifies the expected contents of stdout the test case should produce. New test cases should prefer assert statements to `[out]` sections. -### IR tests +### Adding Debug Prints and Editing Generated C + +Sometimes it's helpful to add some debug prints or other debugging helpers +to the generated C code. You can run mypyc using `--skip-c-gen` to skip the C +generation step, so all manual changes to C files are preserved. Here is +an example of how to use the workflow: + +* Compile some file you want to debug: `python -m mypyc foo.py`. +* Add debug prints to the generated C in `build/__native.c`. +* Run the same compilation command line again, but add `--skip-c-gen`: + `python -m mypyc --skip-c-gen foo.py`. This will only rebuild the + binaries. +* Run the compiled code, including your changes: `python -c 'import foo'`. + You should now see the output from the debug prints you added. + +This can also be helpful if you want to quickly experiment with different +implementation techniques, without having to first figure out how to +modify mypyc to generate the desired C code. + +### Debugging Segfaults + +If you experience a segfault, it's recommended to use a debugger that supports +C, such as gdb or lldb, to look into the segfault. + +If a test case segfaults, you can run tests using the debugger, so +you can inspect the stack. Example of inspecting the C stack when a +test case segfaults (user input after `$` and `(gdb)` prompts): + +``` +$ pytest mypyc -n0 -s --mypyc-debug=gdb -k +... +(gdb) r +... +Program received signal SIGSEGV, Segmentation fault. +... +(gdb) bt +#0 0x00005555556ed1a2 in _PyObject_HashFast (op=0x0) at ./Include/object.h:336 +#1 PyDict_GetItemWithError (op=0x7ffff6c894c0, key=0x0) at Objects/dictobject.c:2394 +... +``` + +You must use `-n0 -s` to enable interactive input to the debugger. +Instead of `gdb`, you can also try `lldb` (especially on macOS). + +To get better C stack tracebacks and more assertions in the Python +runtime, you can build Python in debug mode and use that to run tests, +or to manually run the debugger outside the test framework. + +**Note:** You may need to build Python yourself on macOS, as official +Python builds may not have sufficient entitlements to use a debugger. + +Here are some hints about building a debug version of CPython that may +help (for Ubuntu, macOS is mostly similar except for installing build +dependencies): + +``` +$ sudo apt install gdb build-essential libncursesw5-dev libssl-dev libgdbm-dev libc6-dev libsqlite3-dev libbz2-dev libffi-dev libgdbm-compat-dev +$ +$ cd Python-3.XX.Y +$ ./configure --with-pydebug +$ make -s -j16 +$ ./python -m venv ~/ # Use ./python.exe -m venv ... on macOS +$ source ~//bin/activate +$ cd +$ pip install -r test-requirements.txt +``` + +### IR Tests If the specifics of the generated IR of a change is important (because, for example, you want to make sure a particular optimization @@ -368,7 +461,7 @@ is triggering), you should add a `mypyc.irbuild` test as well. Test cases are located in `mypyc/test-data/irbuild-*.test` and the test driver is in `mypyc.test.test_irbuild`. IR build tests do a direct comparison of the IR output, so try to make the test as targeted as -possible so as to capture only the important details. (Many of our +possible so as to capture only the important details. (Some of our existing IR build tests do not follow this advice, unfortunately!) If you pass the `--update-data` flag to pytest, it will automatically diff --git a/mypyc/doc/dict_operations.rst b/mypyc/doc/dict_operations.rst index e3104172133a6..6858cd33e8a77 100644 --- a/mypyc/doc/dict_operations.rst +++ b/mypyc/doc/dict_operations.rst @@ -50,6 +50,8 @@ Methods * ``d.items()`` * ``d.copy()`` * ``d.clear()`` +* ``d.setdefault(key)`` +* ``d.setdefault(key, value)`` * ``d1.update(d2: dict)`` * ``d.update(x: Iterable)`` diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst index f1d4d05a3a871..b910e3b3c9290 100644 --- a/mypyc/doc/differences_from_python.rst +++ b/mypyc/doc/differences_from_python.rst @@ -107,7 +107,7 @@ performance. integer values. A side effect of this is that the exact runtime type of ``int`` values is lost. For example, consider this simple function:: - def first_int(x: List[int]) -> int: + def first_int(x: list[int]) -> int: return x[0] print(first_int([True])) # Output is 1, instead of True! @@ -316,7 +316,9 @@ non-exhaustive list of what won't work: - Instance ``__annotations__`` is usually not kept - Frames of compiled functions can't be inspected using ``inspect`` - Compiled methods aren't considered methods by ``inspect.ismethod`` -- ``inspect.signature`` chokes on compiled functions +- ``inspect.signature`` chokes on compiled functions with default arguments that + are not simple literals +- ``inspect.iscoroutinefunction`` and ``asyncio.iscoroutinefunction`` will always return False for compiled functions, even those defined with `async def` Profiling hooks and tracing *************************** diff --git a/mypyc/doc/frozenset_operations.rst b/mypyc/doc/frozenset_operations.rst new file mode 100644 index 0000000000000..3d946a8fa9a39 --- /dev/null +++ b/mypyc/doc/frozenset_operations.rst @@ -0,0 +1,29 @@ +.. _frozenset-ops: + +Native frozenset operations +=========================== + +These ``frozenset`` operations have fast, optimized implementations. Other +frozenset operations use generic implementations that are often slower. + +Construction +------------ + +Construct empty frozenset: + +* ``frozenset()`` + +Construct frozenset from iterable: + +* ``frozenset(x: Iterable)`` + + +Operators +--------- + +* ``item in s`` + +Functions +--------- + +* ``len(s: set)`` diff --git a/mypyc/doc/getting_started.rst b/mypyc/doc/getting_started.rst index adc617419ffa3..f85981f08d02d 100644 --- a/mypyc/doc/getting_started.rst +++ b/mypyc/doc/getting_started.rst @@ -38,7 +38,7 @@ Installation ------------ Mypyc is shipped as part of the mypy distribution. Install mypy like -this (you need Python 3.8 or later): +this (you need Python 3.9 or later): .. code-block:: diff --git a/mypyc/doc/index.rst b/mypyc/doc/index.rst index 5b1cc48fab3d1..094e0f8cd9b8e 100644 --- a/mypyc/doc/index.rst +++ b/mypyc/doc/index.rst @@ -36,10 +36,12 @@ generate fast code. bool_operations float_operations str_operations + bytes_operations list_operations dict_operations set_operations tuple_operations + frozenset_operations .. toctree:: :maxdepth: 2 diff --git a/mypyc/doc/list_operations.rst b/mypyc/doc/list_operations.rst index 5993c0a656bda..bb4681266cabf 100644 --- a/mypyc/doc/list_operations.rst +++ b/mypyc/doc/list_operations.rst @@ -32,7 +32,8 @@ Operators * ``lst[n]`` (get item by integer index) * ``lst[n:m]``, ``lst[n:]``, ``lst[:m]``, ``lst[:]`` (slicing) -* ``lst * n``, ``n * lst`` +* ``lst1 + lst2``, ``lst += iter`` +* ``lst * n``, ``n * lst``, ``lst *= n`` * ``obj in lst`` Statements diff --git a/mypyc/doc/native_classes.rst b/mypyc/doc/native_classes.rst index b2935a6f71856..dbcf238b78d57 100644 --- a/mypyc/doc/native_classes.rst +++ b/mypyc/doc/native_classes.rst @@ -48,23 +48,23 @@ can be assigned to (similar to using ``__slots__``):: Inheritance ----------- -Only single inheritance is supported (except for :ref:`traits -`). Most non-native classes can't be used as base -classes. +Only single inheritance is supported from native classes (except for +:ref:`traits `). Most non-native extension classes can't +be used as base classes, but regular Python classes can be used as +base classes unless they use unsupported metaclasses (see below for +more about this). -These non-native classes can be used as base classes of native +These non-native extension classes can be used as base classes of native classes: * ``object`` -* ``dict`` (and ``Dict[k, v]``) +* ``dict`` (and ``dict[k, v]``) * ``BaseException`` * ``Exception`` * ``ValueError`` * ``IndexError`` * ``LookupError`` * ``UserWarning`` -* ``typing.NamedTuple`` -* ``enum.Enum`` By default, a non-native class can't inherit a native class, and you can't inherit from a native class outside the compilation unit that @@ -89,6 +89,15 @@ You need to install ``mypy-extensions`` to use ``@mypyc_attr``: pip install --upgrade mypy-extensions +Additionally, mypyc recognizes these base classes as special, and +understands how they alter the behavior of classes (including native +classes) that subclass them: + +* ``typing.NamedTuple`` +* ``typing.Generic`` +* ``typing.Protocol`` +* ``enum.Enum`` + Class variables --------------- @@ -145,7 +154,8 @@ behavior is too dynamic. You can use these metaclasses, however: .. note:: If a class definition uses an unsupported metaclass, *mypyc - compiles the class into a regular Python class*. + compiles the class into a regular Python class* (non-native + class). Class decorators ---------------- @@ -165,7 +175,63 @@ efficient as pure native classes. .. note:: If a class definition uses an unsupported class decorator, *mypyc - compiles the class into a regular Python class*. + compiles the class into a regular Python class* (non-native class). + +Defining non-native classes +--------------------------- + +You can use the ``@mypy_extensions.mypyc_attr(...)`` class decorator +with an argument ``native_class=False`` to explicitly define normal +Python classes (non-native classes):: + + from mypy_extensions import mypyc_attr + + @mypyc_attr(native_class=False) + class NonNative: + def __init__(self) -> None: + self.attr = 1 + + setattr(NonNative, "extra", 1) # Ok + +This only has an effect in classes compiled using mypyc. Non-native +classes are significantly less efficient than native classes, but they +are sometimes necessary to work around the limitations of native classes. + +Non-native classes can use arbitrary metaclasses and class decorators, +and they support flexible multiple inheritance. Mypyc will still +generate a compile-time error if you try to assign to a method, or an +attribute that is not defined in a class body, since these are static +type errors detected by mypy:: + + o = NonNative() + o.extra = "x" # Static type error: "extra" not defined + +However, these operations still work at runtime, including in modules +that are not compiled using mypyc. You can also use ``setattr`` and +``getattr`` for dynamic access of arbitrary attributes. Expressions +with an ``Any`` type are also not type checked statically, allowing +access to arbitrary attributes:: + + a: Any = o + a.extra = "x" # Ok + + setattr(o, "extra", "y") # Also ok + +Implicit non-native classes +--------------------------- + +If a compiled class uses an unsupported metaclass or an unsupported +class decorator, it will implicitly be a non-native class, as +discussed above. You can still use ``@mypyc_attr(native_class=False)`` +to explicitly mark it as a non-native class. + +Explicit native classes +----------------------- + +You can use ``@mypyc_attr(native_class=True)`` to explicitly declare a +class as a native class. It will be a compile-time error if mypyc +can't compile the class as a native class. You can use this to avoid +accidentally defining implicit non-native classes. Deleting attributes ------------------- diff --git a/mypyc/doc/native_operations.rst b/mypyc/doc/native_operations.rst index 2587e982feac6..3255dbedd98a1 100644 --- a/mypyc/doc/native_operations.rst +++ b/mypyc/doc/native_operations.rst @@ -36,6 +36,7 @@ Functions * ``delattr(obj, name)`` * ``slice(start, stop, step)`` * ``globals()`` +* ``sorted(obj)`` Method decorators ----------------- diff --git a/mypyc/doc/performance_tips_and_tricks.rst b/mypyc/doc/performance_tips_and_tricks.rst index ae0b2950814c7..5b3c1cb42cd7d 100644 --- a/mypyc/doc/performance_tips_and_tricks.rst +++ b/mypyc/doc/performance_tips_and_tricks.rst @@ -57,12 +57,11 @@ here we call ``acme.get_items()``, but it has no type annotation. We can use an explicit type annotation for the variable to which we assign the result:: - from typing import List, Tuple import acme def work() -> None: # Annotate "items" to help mypyc - items: List[Tuple[int, str]] = acme.get_items() + items: list[tuple[int, str]] = acme.get_items() for item in items: ... # Do some work here @@ -140,7 +139,7 @@ Similarly, caching a frequently called method in a local variable can help in CPython, but it can slow things down in compiled code, since the code won't use :ref:`early binding `:: - def squares(n: int) -> List[int]: + def squares(n: int) -> list[int]: a = [] append = a.append # Not a good idea in compiled code! for i in range(n): diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 5420c8af7d31c..4a7aff00f2ad3 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -12,6 +12,8 @@ Construction * String literal * ``str(x: int)`` * ``str(x: object)`` +* ``repr(x: int)`` +* ``repr(x: object)`` Operators --------- @@ -21,15 +23,63 @@ Operators * Slicing (``s[n:m]``, ``s[n:]``, ``s[:m]``) * Comparisons (``==``, ``!=``) * Augmented assignment (``s1 += s2``) +* Containment (``s1 in s2``) + +.. _str-methods: Methods ------- +* ``s.encode()`` +* ``s.encode(encoding: str)`` +* ``s.encode(encoding: str, errors: str)`` * ``s1.endswith(s2: str)`` +* ``s1.endswith(t: tuple[str, ...])`` +* ``s1.find(s2: str)`` +* ``s1.find(s2: str, start: int)`` +* ``s1.find(s2: str, start: int, end: int)`` * ``s.join(x: Iterable)`` +* ``s.lstrip()`` +* ``s.lstrip(chars: str)`` +* ``s.partition(sep: str)`` +* ``s.removeprefix(prefix: str)`` +* ``s.removesuffix(suffix: str)`` * ``s.replace(old: str, new: str)`` * ``s.replace(old: str, new: str, count: int)`` +* ``s1.rfind(s2: str)`` +* ``s1.rfind(s2: str, start: int)`` +* ``s1.rfind(s2: str, start: int, end: int)`` +* ``s.rpartition(sep: str)`` +* ``s.rsplit()`` +* ``s.rsplit(sep: str)`` +* ``s.rsplit(sep: str, maxsplit: int)`` +* ``s.rstrip()`` +* ``s.rstrip(chars: str)`` * ``s.split()`` * ``s.split(sep: str)`` * ``s.split(sep: str, maxsplit: int)`` +* ``s.splitlines()`` +* ``s.splitlines(keepends: bool)`` * ``s1.startswith(s2: str)`` +* ``s1.startswith(t: tuple[str, ...])`` +* ``s.strip()`` +* ``s.strip(chars: 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/doc/tuple_operations.rst b/mypyc/doc/tuple_operations.rst index fca9e63fc2100..4c9da9b894af6 100644 --- a/mypyc/doc/tuple_operations.rst +++ b/mypyc/doc/tuple_operations.rst @@ -21,6 +21,8 @@ Operators * ``tup[n]`` (integer index) * ``tup[n:m]``, ``tup[n:]``, ``tup[:m]`` (slicing) +* ``tup1 + tup2`` +* ``tup * n``, ``n * tup`` Statements ---------- diff --git a/mypyc/doc/using_type_annotations.rst b/mypyc/doc/using_type_annotations.rst index 04c923819d547..dc0b04a974fd6 100644 --- a/mypyc/doc/using_type_annotations.rst +++ b/mypyc/doc/using_type_annotations.rst @@ -37,10 +37,10 @@ implementations: * ``float`` (:ref:`native operations `) * ``bool`` (:ref:`native operations `) * ``str`` (:ref:`native operations `) -* ``List[T]`` (:ref:`native operations `) -* ``Dict[K, V]`` (:ref:`native operations `) -* ``Set[T]`` (:ref:`native operations `) -* ``Tuple[T, ...]`` (variable-length tuple; :ref:`native operations `) +* ``list[T]`` (:ref:`native operations `) +* ``dict[K, V]`` (:ref:`native operations `) +* ``set[T]`` (:ref:`native operations `) +* ``tuple[T, ...]`` (variable-length tuple; :ref:`native operations `) * ``None`` The link after each type lists all supported native, optimized @@ -61,10 +61,10 @@ variable. For example, here we have a runtime type error on the final line of ``example`` (the ``Any`` type means an arbitrary, unchecked value):: - from typing import List, Any + from typing import Any - def example(a: List[Any]) -> None: - b: List[int] = a # No error -- items are not checked + def example(a: list[Any]) -> None: + b: list[int] = a # No error -- items are not checked print(b[0]) # Error here -- got str, but expected int example(["x"]) @@ -126,7 +126,7 @@ Tuple types Fixed-length `tuple types `_ -such as ``Tuple[int, str]`` are represented +such as ``tuple[int, str]`` are represented as :ref:`value types ` when stored in variables, passed as arguments, or returned from functions. Value types are allocated in the low-level machine stack or in CPU registers, as 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..0a56aaf5d1011 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -2,12 +2,12 @@ from __future__ import annotations -from typing import List, NamedTuple +from typing import NamedTuple from mypyc.common import PROPSET_PREFIX, JsonDict -from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature +from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg from mypyc.ir.ops import DeserMaps, Value -from mypyc.ir.rtypes import RInstance, RType, deserialize_type +from mypyc.ir.rtypes import RInstance, RType, deserialize_type, object_rprimitive from mypyc.namegen import NameGenerator, exported_name # Some notes on the vtable layout: Each concrete class has a vtable @@ -76,7 +76,7 @@ class VTableMethod(NamedTuple): shadow_method: FuncIR | None -VTableEntries = List[VTableMethod] +VTableEntries = list[VTableMethod] class ClassIR: @@ -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 @@ -131,6 +133,16 @@ def __init__( self.builtin_base: str | None = None # Default empty constructor self.ctor = FuncDecl(name, None, module_name, FuncSignature([], RInstance(self))) + # Declare setup method that allocates and initializes an object. type is the + # type of the class being initialized, which could be another class if there + # is an interpreted subclass. + # TODO: Make it a regular method and generate its body in IR + self.setup = FuncDecl( + "__mypyc__" + name + "_setup", + None, + module_name, + FuncSignature([RuntimeArg("type", object_rprimitive)], RInstance(self)), + ) # Attributes defined in the class (not inherited) self.attributes: dict[str, RType] = {} # Deletable attributes @@ -178,7 +190,12 @@ def __init__( self.attrs_with_defaults: set[str] = set() # Attributes that are always initialized in __init__ or class body - # (inferred in mypyc.analysis.attrdefined using interprocedural analysis) + # (inferred in mypyc.analysis.attrdefined using interprocedural analysis). + # These can never raise AttributeError when accessed. If an attribute + # is *not* always initialized, we normally use the error value for + # an undefined value. If the attribute byte has an overlapping error value + # (the error_overlap attribute is true for the RType), we use a bitmap + # to track if the attribute is defined instead (see bitmap_attrs). self._always_initialized_attrs: set[str] = set() # Attributes that are sometimes initialized in __init__ @@ -189,17 +206,30 @@ def __init__( # Definedness of these attributes is backed by a bitmap. Index in the list # indicates the bit number. Includes inherited attributes. We need the - # bitmap for types such as native ints that can't have a dedicated error - # value that doesn't overlap a valid value. The bitmap is used if the + # bitmap for types such as native ints (i64 etc.) that can't have a dedicated + # error value that doesn't overlap a valid value. The bitmap is used if the # value of an attribute is the same as the error value. self.bitmap_attrs: list[str] = [] + # If this is a generator environment class, what is the actual method for it + self.env_user_function: FuncIR | None = None + + # If True, keep one freed, cleared instance available for immediate reuse to + # speed up allocations. This helps if many objects are freed quickly, before + # other instances of the same class are allocated. This is effectively a + # per-type free "list" of up to length 1. + self.reuse_freed_instance = False + + # Is this a class inheriting from enum.Enum? Such classes can be special-cased. + self.is_enum = False + def __repr__(self) -> str: return ( "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 +278,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 +378,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, @@ -391,6 +421,9 @@ def serialize(self) -> JsonDict: "_always_initialized_attrs": sorted(self._always_initialized_attrs), "_sometimes_initialized_attrs": sorted(self._sometimes_initialized_attrs), "init_self_leak": self.init_self_leak, + "env_user_function": self.env_user_function.id if self.env_user_function else None, + "reuse_freed_instance": self.reuse_freed_instance, + "is_enum": self.is_enum, } @classmethod @@ -404,6 +437,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"] @@ -442,6 +476,11 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR: ir._always_initialized_attrs = set(data["_always_initialized_attrs"]) ir._sometimes_initialized_attrs = set(data["_sometimes_initialized_attrs"]) ir.init_self_leak = data["init_self_leak"] + ir.env_user_function = ( + ctx.functions[data["env_user_function"]] if data["env_user_function"] else None + ) + ir.reuse_freed_instance = data["reuse_freed_instance"] + ir.is_enum = data["is_enum"] return ir diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index 44847c7bb0b3b..d11fef42feb54 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import Final, Sequence +import inspect +from collections.abc import Sequence +from typing import Final from mypy.nodes import ARG_POS, ArgKind, Block, FuncDef from mypyc.common import BITMAP_BITS, JsonDict, bitmap_name, get_id_from_name, short_id_from_name @@ -10,13 +12,24 @@ Assign, AssignMulti, BasicBlock, + Box, ControlOp, DeserMaps, + Float, + Integer, LoadAddress, + LoadLiteral, Register, + TupleSet, Value, ) -from mypyc.ir.rtypes import RType, bitmap_rprimitive, deserialize_type +from mypyc.ir.rtypes import ( + RType, + bitmap_rprimitive, + deserialize_type, + is_bool_rprimitive, + is_none_rprimitive, +) from mypyc.namegen import NameGenerator @@ -136,9 +149,13 @@ def __init__( module_name: str, sig: FuncSignature, kind: int = FUNC_NORMAL, + *, is_prop_setter: bool = False, is_prop_getter: bool = False, + is_generator: bool = False, + is_coroutine: bool = False, implicit: bool = False, + internal: bool = False, ) -> None: self.name = name self.class_name = class_name @@ -147,6 +164,8 @@ def __init__( self.kind = kind self.is_prop_setter = is_prop_setter self.is_prop_getter = is_prop_getter + self.is_generator = is_generator + self.is_coroutine = is_coroutine if class_name is None: self.bound_sig: FuncSignature | None = None else: @@ -159,6 +178,9 @@ def __init__( # Currently only supported for property getters/setters self.implicit = implicit + # If True, only direct C level calls are supported (no wrapper function) + self.internal = internal + # This is optional because this will be set to the line number when the corresponding # FuncIR is created self._line: int | None = None @@ -202,7 +224,10 @@ def serialize(self) -> JsonDict: "kind": self.kind, "is_prop_setter": self.is_prop_setter, "is_prop_getter": self.is_prop_getter, + "is_generator": self.is_generator, + "is_coroutine": self.is_coroutine, "implicit": self.implicit, + "internal": self.internal, } # TODO: move this to FuncIR? @@ -222,9 +247,12 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncDecl: data["module_name"], FuncSignature.deserialize(data["sig"], ctx), data["kind"], - data["is_prop_setter"], - data["is_prop_getter"], - data["implicit"], + is_prop_setter=data["is_prop_setter"], + is_prop_getter=data["is_prop_getter"], + is_generator=data["is_generator"], + is_coroutine=data["is_coroutine"], + implicit=data["implicit"], + internal=data["internal"], ) @@ -286,6 +314,10 @@ def fullname(self) -> str: def id(self) -> str: return self.decl.id + @property + def internal(self) -> bool: + return self.decl.internal + def cname(self, names: NameGenerator) -> str: return self.decl.cname(names) @@ -368,3 +400,85 @@ def all_values_full(args: list[Register], blocks: list[BasicBlock]) -> list[Valu values.append(op) return values + + +_ARG_KIND_TO_INSPECT: Final = { + ArgKind.ARG_POS: inspect.Parameter.POSITIONAL_OR_KEYWORD, + ArgKind.ARG_OPT: inspect.Parameter.POSITIONAL_OR_KEYWORD, + ArgKind.ARG_STAR: inspect.Parameter.VAR_POSITIONAL, + ArgKind.ARG_NAMED: inspect.Parameter.KEYWORD_ONLY, + ArgKind.ARG_STAR2: inspect.Parameter.VAR_KEYWORD, + ArgKind.ARG_NAMED_OPT: inspect.Parameter.KEYWORD_ONLY, +} + +# Sentinel indicating a value that cannot be represented in a text signature. +_NOT_REPRESENTABLE = object() + + +def get_text_signature(fn: FuncIR, *, bound: bool = False) -> str | None: + """Return a text signature in CPython's internal doc format, or None + if the function's signature cannot be represented. + """ + parameters = [] + mark_self = (fn.class_name is not None) and (fn.decl.kind != FUNC_STATICMETHOD) and not bound + sig = fn.decl.bound_sig if bound and fn.decl.bound_sig is not None else fn.decl.sig + # Pre-scan for end of positional-only parameters. + # This is needed to handle signatures like 'def foo(self, __x)', where mypy + # currently sees 'self' as being positional-or-keyword and '__x' as positional-only. + pos_only_idx = -1 + for idx, arg in enumerate(sig.args): + if arg.pos_only and arg.kind in (ArgKind.ARG_POS, ArgKind.ARG_OPT): + pos_only_idx = idx + for idx, arg in enumerate(sig.args): + if arg.name.startswith(("__bitmap", "__mypyc")): + continue + kind = ( + inspect.Parameter.POSITIONAL_ONLY + if idx <= pos_only_idx + else _ARG_KIND_TO_INSPECT[arg.kind] + ) + default: object = inspect.Parameter.empty + if arg.optional: + default = _find_default_argument(arg.name, fn.blocks) + if default is _NOT_REPRESENTABLE: + # This default argument cannot be represented in a __text_signature__ + return None + + curr_param = inspect.Parameter(arg.name, kind, default=default) + parameters.append(curr_param) + if mark_self: + # Parameter.__init__/Parameter.replace do not accept $ + curr_param._name = f"${arg.name}" # type: ignore[attr-defined] + mark_self = False + return f"{fn.name}{inspect.Signature(parameters)}" + + +def _find_default_argument(name: str, blocks: list[BasicBlock]) -> object: + # Find assignment inserted by gen_arg_defaults. Assumed to be the first assignment. + for block in blocks: + for op in block.ops: + if isinstance(op, Assign) and op.dest.name == name: + return _extract_python_literal(op.src) + return _NOT_REPRESENTABLE + + +def _extract_python_literal(value: Value) -> object: + if isinstance(value, Integer): + if is_none_rprimitive(value.type): + return None + val = value.numeric_value() + if is_bool_rprimitive(value.type): + return bool(val) + return val + elif isinstance(value, Float): + return value.value + elif isinstance(value, LoadLiteral): + return value.value + elif isinstance(value, Box): + return _extract_python_literal(value.src) + elif isinstance(value, TupleSet): + items = tuple(_extract_python_literal(item) for item in value.items) + if any(itm is _NOT_REPRESENTABLE for itm in items): + return _NOT_REPRESENTABLE + return items + return _NOT_REPRESENTABLE diff --git a/mypyc/ir/module_ir.py b/mypyc/ir/module_ir.py index e3b240629edab..5aef414490f9e 100644 --- a/mypyc/ir/module_ir.py +++ b/mypyc/ir/module_ir.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import Dict - from mypyc.common import JsonDict from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncDecl, FuncIR @@ -32,6 +30,8 @@ def __init__( # These are only visible in the module that defined them, so no need # to serialize. self.type_var_names = type_var_names + # Capsules needed by the module, specified via module names such as "librt.base64" + self.capsules: set[str] = set() def serialize(self) -> JsonDict: return { @@ -40,11 +40,12 @@ def serialize(self) -> JsonDict: "functions": [f.serialize() for f in self.functions], "classes": [c.serialize() for c in self.classes], "final_names": [(k, t.serialize()) for k, t in self.final_names], + "capsules": sorted(self.capsules), } @classmethod def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ModuleIR: - return ModuleIR( + module = ModuleIR( data["fullname"], data["imports"], [ctx.functions[FuncDecl.get_id_from_json(f)] for f in data["functions"]], @@ -52,6 +53,8 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ModuleIR: [(k, deserialize_type(t, ctx)) for k, t in data["final_names"]], [], ) + module.capsules = set(data["capsules"]) + return module def deserialize_modules(data: dict[str, JsonDict], ctx: DeserMaps) -> dict[str, ModuleIR]: @@ -91,4 +94,4 @@ def deserialize_modules(data: dict[str, JsonDict], ctx: DeserMaps) -> dict[str, # ModulesIRs should also always be an *OrderedDict*, but if we # declared it that way we would need to put it in quotes everywhere... -ModuleIRs = Dict[str, ModuleIR] +ModuleIRs = dict[str, ModuleIR] diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 896ba3ac091c9..2153d47e68747 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -3,31 +3,47 @@ Opcodes operate on abstract values (Value) in a register machine. Each value has a type (RType). A value can hold various things, such as: -- local variables (Register) +- local variables or temporaries (Register) - intermediate values of expressions (RegisterOp subclasses) - condition flags (true/false) - literals (integer literals, True, False, etc.) + +NOTE: As a convention, we don't create subclasses of concrete Value/Op + subclasses (e.g. you shouldn't define a subclass of Integer, which + is a concrete class). + + If you want to introduce a variant of an existing class, you'd + typically add an attribute (e.g. a flag) to an existing concrete + class to enable the new behavior. Sometimes adding a new abstract + base class is also an option, or just creating a new subclass + without any inheritance relationship (some duplication of code + is preferred over introducing complex implementation inheritance). + + This makes it possible to use isinstance(x, ) checks without worrying about potential subclasses. """ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, Final, Generic, List, NamedTuple, Sequence, TypeVar, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Generic, NamedTuple, TypeVar, Union, final from mypy_extensions import trait from mypyc.ir.rtypes import ( RArray, RInstance, + RStruct, RTuple, RType, RVoid, bit_rprimitive, bool_rprimitive, + cstring_rprimitive, float_rprimitive, int_rprimitive, - is_bit_rprimitive, - is_bool_rprimitive, + is_bool_or_bit_rprimitive, is_int_rprimitive, is_none_rprimitive, is_pointer_rprimitive, @@ -46,6 +62,7 @@ T = TypeVar("T") +@final class BasicBlock: """IR basic block. @@ -141,6 +158,7 @@ def is_void(self) -> bool: return isinstance(self.type, RVoid) +@final class Register(Value): """A Register holds a value of a specific type, and it can be read and mutated. @@ -167,6 +185,7 @@ def __repr__(self) -> str: return f"" +@final class Integer(Value): """Short integer literal. @@ -197,6 +216,7 @@ def numeric_value(self) -> int: return self.value +@final class Float(Value): """Float literal. @@ -211,6 +231,40 @@ def __init__(self, value: float, line: int = -1) -> None: self.line = line +@final +class CString(Value): + """C string literal (zero-terminated). + + You can also include zero values in the value, but then you'll need to track + the length of the string separately. + """ + + def __init__(self, value: bytes, line: int = -1) -> None: + self.value = value + self.type = cstring_rprimitive + self.line = line + + +@final +class Undef(Value): + """An undefined value. + + Use Undef() as the initial value followed by one or more SetElement + ops to initialize a struct. Pseudocode example: + + r0 = set_element undef MyStruct, "field1", f1 + r1 = set_element r0, "field2", f2 + # r1 now has new struct value with two fields set + + Warning: Always initialize undefined values before using them, + as otherwise the values are garbage. You shouldn't expect that + undefined values are zeroed, in particular. + """ + + def __init__(self, rtype: RType) -> None: + self.type = rtype + + class Op(Value): """Abstract base class for all IR operations. @@ -235,6 +289,10 @@ def can_raise(self) -> bool: def sources(self) -> list[Value]: """All the values the op may read.""" + @abstractmethod + def set_sources(self, new: list[Value]) -> None: + """Rewrite the sources of an op""" + def stolen(self) -> list[Value]: """Return arguments that have a reference count stolen by this op""" return [] @@ -252,13 +310,14 @@ def accept(self, visitor: OpVisitor[T]) -> T: class BaseAssign(Op): - """Base class for ops that assign to a register.""" + """Abstract base class for ops that assign to a register.""" def __init__(self, dest: Register, line: int = -1) -> None: super().__init__(line) self.dest = dest +@final class Assign(BaseAssign): """Assign a value to a Register (dest = src).""" @@ -271,6 +330,9 @@ def __init__(self, dest: Register, src: Value, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def stolen(self) -> list[Value]: return [self.src] @@ -278,6 +340,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_assign(self) +@final class AssignMulti(BaseAssign): """Assign multiple values to a Register (dest = src1, src2, ...). @@ -301,6 +364,9 @@ def __init__(self, dest: Register, src: list[Value], line: int = -1) -> None: def sources(self) -> list[Value]: return self.src.copy() + def set_sources(self, new: list[Value]) -> None: + self.src = new[:] + def stolen(self) -> list[Value]: return [] @@ -309,7 +375,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: class ControlOp(Op): - """Control flow operation.""" + """Abstract base class for control flow operations.""" def targets(self) -> Sequence[BasicBlock]: """Get all basic block targets of the control operation.""" @@ -320,6 +386,7 @@ def set_target(self, i: int, new: BasicBlock) -> None: raise AssertionError(f"Invalid set_target({self}, {i})") +@final class Goto(ControlOp): """Unconditional jump.""" @@ -342,10 +409,14 @@ def __repr__(self) -> str: def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_goto(self) +@final class Branch(ControlOp): """Branch based on a value. @@ -402,6 +473,9 @@ def set_target(self, i: int, new: BasicBlock) -> None: def sources(self) -> list[Value]: return [self.value] + def set_sources(self, new: list[Value]) -> None: + (self.value,) = new + def invert(self) -> None: self.negated = not self.negated @@ -409,18 +483,29 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_branch(self) +@final class Return(ControlOp): """Return a value from a function.""" error_kind = ERR_NEVER - def __init__(self, value: Value, line: int = -1) -> None: + def __init__( + self, value: Value, line: int = -1, *, yield_target: BasicBlock | None = None + ) -> None: super().__init__(line) self.value = value + # If this return is created by a yield, keep track of the next + # basic block. This doesn't affect the code we generate but + # can feed into analysis that need to understand the + # *original* CFG. + self.yield_target = yield_target def sources(self) -> list[Value]: return [self.value] + def set_sources(self, new: list[Value]) -> None: + (self.value,) = new + def stolen(self) -> list[Value]: return [self.value] @@ -428,6 +513,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_return(self) +@final class Unreachable(ControlOp): """Mark the end of basic block as unreachable. @@ -452,6 +538,9 @@ def __init__(self, line: int = -1) -> None: def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_unreachable(self) @@ -481,6 +570,7 @@ def can_raise(self) -> bool: return self.error_kind != ERR_NEVER +@final class IncRef(RegisterOp): """Increase reference count (inc_ref src).""" @@ -494,10 +584,14 @@ def __init__(self, src: Value, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_inc_ref(self) +@final class DecRef(RegisterOp): """Decrease reference count and free object if zero (dec_ref src). @@ -519,10 +613,14 @@ def __repr__(self) -> str: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_dec_ref(self) +@final class Call(RegisterOp): """Native call f(arg, ...). @@ -544,10 +642,14 @@ def __init__(self, fn: FuncDecl, args: Sequence[Value], line: int) -> None: def sources(self) -> list[Value]: return list(self.args.copy()) + def set_sources(self, new: list[Value]) -> None: + self.args = new[:] + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_call(self) +@final class MethodCall(RegisterOp): """Native method call obj.method(arg, ...)""" @@ -572,10 +674,14 @@ def __init__(self, obj: Value, method: str, args: list[Value], line: int = -1) - def sources(self) -> list[Value]: return self.args.copy() + [self.obj] + def set_sources(self, new: list[Value]) -> None: + *self.args, self.obj = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_method_call(self) +@final class PrimitiveDescription: """Description of a primitive op. @@ -601,6 +707,8 @@ def __init__( extra_int_constants: list[tuple[int, RType]], priority: int, is_pure: bool, + experimental: bool, + capsule: str | None, ) -> None: # Each primitive much have a distinct name, but otherwise they are arbitrary. self.name: Final = name @@ -623,11 +731,18 @@ def __init__( self.is_pure: Final = is_pure if is_pure: assert error_kind == ERR_NEVER + # Experimental primitives are not used unless mypyc experimental features are + # explicitly enabled + self.experimental = experimental + # Capsule that needs to imported and configured to call the primitive + # (name of the target module, e.g. "librt.base64"). + self.capsule = capsule def __repr__(self) -> str: - return f"" + return f"" +@final class PrimitiveOp(RegisterOp): """A higher-level primitive operation. @@ -650,6 +765,9 @@ def __init__(self, args: list[Value], desc: PrimitiveDescription, line: int = -1 def sources(self) -> list[Value]: return self.args + def set_sources(self, new: list[Value]) -> None: + self.args = new[:] + def stolen(self) -> list[Value]: steals = self.desc.steals if isinstance(steals, list): @@ -662,6 +780,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_primitive_op(self) +@final class LoadErrorValue(RegisterOp): """Load an error value. @@ -685,10 +804,14 @@ def __init__( def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_load_error_value(self) +@final class LoadLiteral(RegisterOp): """Load a Python literal object (dest = 'foo' / b'foo' / ...). @@ -717,34 +840,53 @@ def __init__(self, value: LiteralValue, rtype: RType) -> None: def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_load_literal(self) +@final class GetAttr(RegisterOp): """obj.attr (for a native object)""" error_kind = ERR_MAGIC - def __init__(self, obj: Value, attr: str, line: int, *, borrow: bool = False) -> None: + def __init__( + self, + obj: Value, + attr: str, + line: int, + *, + borrow: bool = False, + allow_error_value: bool = False, + ) -> None: super().__init__(line) self.obj = obj self.attr = attr + self.allow_error_value = allow_error_value assert isinstance(obj.type, RInstance), "Attribute access not supported: %s" % obj.type self.class_type = obj.type attr_type = obj.type.attr_type(attr) self.type = attr_type - if attr_type.error_overlap: + if allow_error_value: + self.error_kind = ERR_NEVER + elif attr_type.error_overlap: self.error_kind = ERR_MAGIC_OVERLAPPING self.is_borrowed = borrow and attr_type.is_refcounted def sources(self) -> list[Value]: return [self.obj] + def set_sources(self, new: list[Value]) -> None: + (self.obj,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_get_attr(self) +@final class SetAttr(RegisterOp): """obj.attr = src (for a native object) @@ -773,6 +915,9 @@ def mark_as_initializer(self) -> None: def sources(self) -> list[Value]: return [self.obj, self.src] + def set_sources(self, new: list[Value]) -> None: + self.obj, self.src = new + def stolen(self) -> list[Value]: return [self.src] @@ -793,6 +938,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: NAMESPACE_TYPE_VAR: Final = "typevar" +@final class LoadStatic(RegisterOp): """Load a static name (name :: static). @@ -826,10 +972,14 @@ def __init__( def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_load_static(self) +@final class InitStatic(RegisterOp): """static = value :: static @@ -855,10 +1005,14 @@ def __init__( def sources(self) -> list[Value]: return [self.value] + def set_sources(self, new: list[Value]) -> None: + (self.value,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_init_static(self) +@final class TupleSet(RegisterOp): """dest = (reg, ...) (for fixed-length tuple)""" @@ -884,10 +1038,14 @@ def sources(self) -> list[Value]: def stolen(self) -> list[Value]: return self.items.copy() + def set_sources(self, new: list[Value]) -> None: + self.items = new[:] + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_tuple_set(self) +@final class TupleGet(RegisterOp): """Get item of a fixed-length tuple (src[index]).""" @@ -895,20 +1053,31 @@ class TupleGet(RegisterOp): def __init__(self, src: Value, index: int, line: int = -1, *, borrow: bool = False) -> None: super().__init__(line) + assert isinstance( + src.type, RTuple + ), f"TupleGet only operates on tuples, not {type(src.type).__name__}" + src_len = len(src.type.types) self.src = src self.index = index - assert isinstance(src.type, RTuple), "TupleGet only operates on tuples" - assert index >= 0 + if index < 0: + self.index += src_len + assert ( + self.index <= src_len - 1 + ), f"Index out of range.\nsource type: {src.type}\nindex: {index}" self.type = src.type.types[index] self.is_borrowed = borrow def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_tuple_get(self) +@final class Cast(RegisterOp): """cast(type, src) @@ -919,15 +1088,26 @@ class Cast(RegisterOp): error_kind = ERR_MAGIC - def __init__(self, src: Value, typ: RType, line: int, *, borrow: bool = False) -> None: + def __init__( + self, src: Value, typ: RType, line: int, *, borrow: bool = False, unchecked: bool = False + ) -> None: super().__init__(line) self.src = src self.type = typ + # If true, don't incref the result. self.is_borrowed = borrow + # If true, don't perform a runtime type check (only changes the static type of + # the operand). Used when we know that the cast will always succeed. + self.is_unchecked = unchecked + if unchecked: + self.error_kind = ERR_NEVER def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def stolen(self) -> list[Value]: if self.is_borrowed: return [] @@ -937,6 +1117,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_cast(self) +@final class Box(RegisterOp): """box(type, src) @@ -951,16 +1132,15 @@ def __init__(self, src: Value, line: int = -1) -> None: self.src = src self.type = object_rprimitive # When we box None and bool values, we produce a borrowed result - if ( - is_none_rprimitive(self.src.type) - or is_bool_rprimitive(self.src.type) - or is_bit_rprimitive(self.src.type) - ): + if is_none_rprimitive(self.src.type) or is_bool_or_bit_rprimitive(self.src.type): self.is_borrowed = True def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def stolen(self) -> list[Value]: return [self.src] @@ -968,6 +1148,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_box(self) +@final class Unbox(RegisterOp): """unbox(type, src) @@ -987,10 +1168,14 @@ def __init__(self, src: Value, typ: RType, line: int) -> None: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_unbox(self) +@final class RaiseStandardError(RegisterOp): """Raise built-in exception with an optional error string. @@ -1019,14 +1204,18 @@ def __init__(self, class_name: str, value: str | Value | None, line: int) -> Non def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_raise_standard_error(self) # True steals all arguments, False steals none, a list steals those in matching positions -StealsDescription = Union[bool, List[bool]] +StealsDescription = Union[bool, list[bool]] +@final class CallC(RegisterOp): """result = function(arg0, arg1, ...) @@ -1047,6 +1236,8 @@ def __init__( var_arg_idx: int = -1, *, is_pure: bool = False, + returns_null: bool = False, + capsule: str | None = None, ) -> None: self.error_kind = error_kind super().__init__(line) @@ -1061,11 +1252,20 @@ def __init__( # and all the arguments are immutable. Pure functions support # additional optimizations. Pure functions never fail. self.is_pure = is_pure - if is_pure: + # The function might return a null value that does not indicate + # an error. + self.returns_null = returns_null + # A capsule from this module must be imported and initialized before calling this + # function (used for C functions exported from librt). Example value: "librt.base64" + self.capsule = capsule + if is_pure or returns_null: assert error_kind == ERR_NEVER def sources(self) -> list[Value]: - return self.args + return self.args[:] + + def set_sources(self, new: list[Value]) -> None: + self.args = new[:] def stolen(self) -> list[Value]: if isinstance(self.steals, list): @@ -1078,6 +1278,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_call_c(self) +@final class Truncate(RegisterOp): """result = truncate src from src_type to dst_type @@ -1098,6 +1299,9 @@ def __init__(self, src: Value, dst_type: RType, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def stolen(self) -> list[Value]: return [] @@ -1105,6 +1309,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_truncate(self) +@final class Extend(RegisterOp): """result = extend src from src_type to dst_type @@ -1129,6 +1334,9 @@ def __init__(self, src: Value, dst_type: RType, signed: bool, line: int = -1) -> def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def stolen(self) -> list[Value]: return [] @@ -1136,6 +1344,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_extend(self) +@final class LoadGlobal(RegisterOp): """Load a low-level global variable/pointer. @@ -1156,10 +1365,14 @@ def __init__(self, type: RType, identifier: str, line: int = -1, ann: object = N def sources(self) -> list[Value]: return [] + def set_sources(self, new: list[Value]) -> None: + assert not new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_load_global(self) +@final class IntOp(RegisterOp): """Binary arithmetic or bitwise op on integer operands (e.g., r1 = r2 + r3). @@ -1212,6 +1425,9 @@ def __init__(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) def sources(self) -> list[Value]: return [self.lhs, self.rhs] + def set_sources(self, new: list[Value]) -> None: + self.lhs, self.rhs = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_int_op(self) @@ -1221,6 +1437,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: int_op_to_id: Final = {op: op_id for op_id, op in IntOp.op_str.items()} +@final class ComparisonOp(RegisterOp): """Low-level comparison op for integers and pointers. @@ -1275,10 +1492,14 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.lhs, self.rhs] + def set_sources(self, new: list[Value]) -> None: + self.lhs, self.rhs = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_comparison_op(self) +@final class FloatOp(RegisterOp): """Binary float arithmetic op (e.g., r1 = r2 + r3). @@ -1308,6 +1529,9 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.lhs, self.rhs] + def set_sources(self, new: list[Value]) -> None: + (self.lhs, self.rhs) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_float_op(self) @@ -1317,6 +1541,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: float_op_to_id: Final = {op: op_id for op_id, op in FloatOp.op_str.items()} +@final class FloatNeg(RegisterOp): """Float negation op (r1 = -r2).""" @@ -1330,10 +1555,14 @@ def __init__(self, src: Value, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_float_neg(self) +@final class FloatComparisonOp(RegisterOp): """Low-level comparison op for floats.""" @@ -1358,6 +1587,9 @@ def __init__(self, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.lhs, self.rhs] + def set_sources(self, new: list[Value]) -> None: + (self.lhs, self.rhs) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_float_comparison_op(self) @@ -1367,6 +1599,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: float_comparison_op_to_id: Final = {op: op_id for op_id, op in FloatComparisonOp.op_str.items()} +@final class LoadMem(RegisterOp): """Read a memory location: result = *(type *)src. @@ -1377,22 +1610,25 @@ class LoadMem(RegisterOp): error_kind = ERR_NEVER - def __init__(self, type: RType, src: Value, line: int = -1) -> None: + def __init__(self, type: RType, src: Value, line: int = -1, *, borrow: bool = False) -> None: super().__init__(line) self.type = type - # TODO: for now we enforce that the src memory address should be Py_ssize_t - # later we should also support same width unsigned int + # TODO: Support other native integer types assert is_pointer_rprimitive(src.type) self.src = src - self.is_borrowed = True + self.is_borrowed = borrow and type.is_refcounted def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_load_mem(self) +@final class SetMem(Op): """Write to a memory location: *(type *)dest = src @@ -1414,6 +1650,9 @@ def __init__(self, type: RType, dest: Value, src: Value, line: int = -1) -> None def sources(self) -> list[Value]: return [self.src, self.dest] + def set_sources(self, new: list[Value]) -> None: + self.src, self.dest = new + def stolen(self) -> list[Value]: return [self.src] @@ -1421,6 +1660,7 @@ def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_set_mem(self) +@final class GetElementPtr(RegisterOp): """Get the address of a struct element. @@ -1440,10 +1680,47 @@ def __init__(self, src: Value, src_type: RType, field: str, line: int = -1) -> N def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_get_element_ptr(self) +@final +class SetElement(RegisterOp): + """Set the value of a struct element. + + This evaluates to a new struct with the changed value. + + Use together with Undef to initialize a fresh struct value + (see Undef for more details). + """ + + error_kind = ERR_NEVER + + def __init__(self, src: Value, field: str, item: Value, line: int = -1) -> None: + super().__init__(line) + assert isinstance(src.type, RStruct), src.type + self.type = src.type + self.src = src + self.item = item + self.field = field + + def sources(self) -> list[Value]: + return [self.src] + + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + + def stolen(self) -> list[Value]: + return [self.src] + + def accept(self, visitor: OpVisitor[T]) -> T: + return visitor.visit_set_element(self) + + +@final class LoadAddress(RegisterOp): """Get the address of a value: result = (type)&src @@ -1468,10 +1745,17 @@ def sources(self) -> list[Value]: else: return [] + def set_sources(self, new: list[Value]) -> None: + if new: + assert isinstance(new[0], Register) + assert len(new) == 1 + self.src = new[0] + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_load_address(self) +@final class KeepAlive(RegisterOp): """A no-op operation that ensures source values aren't freed. @@ -1512,10 +1796,14 @@ def stolen(self) -> list[Value]: return self.src.copy() return [] + def set_sources(self, new: list[Value]) -> None: + self.src = new[:] + def accept(self, visitor: OpVisitor[T]) -> T: return visitor.visit_keep_alive(self) +@final class Unborrow(RegisterOp): """A no-op op to create a regular reference from a borrowed one. @@ -1531,12 +1819,12 @@ class Unborrow(RegisterOp): # t is a 2-tuple r0 = borrow t[0] r1 = borrow t[1] + keep_alive steal t r2 = unborrow r0 r3 = unborrow r1 - # now (r2, r3) represent the tuple as separate items, and the - # original tuple can be considered dead and available to be - # stolen - keep_alive steal t + # now (r2, r3) represent the tuple as separate items, that are + # managed again. (Note we need to steal before unborrow, to avoid + # refcount briefly touching zero if r2 or r3 are unused.) Be careful with this -- this can easily cause double freeing. """ @@ -1552,6 +1840,9 @@ def __init__(self, src: Value, line: int = -1) -> None: def sources(self) -> list[Value]: return [self.src] + def set_sources(self, new: list[Value]) -> None: + (self.src,) = new + def stolen(self) -> list[Value]: return [] @@ -1701,6 +1992,10 @@ def visit_set_mem(self, op: SetMem) -> T: def visit_get_element_ptr(self, op: GetElementPtr) -> T: raise NotImplementedError + @abstractmethod + def visit_set_element(self, op: SetElement) -> T: + raise NotImplementedError + @abstractmethod def visit_load_address(self, op: LoadAddress) -> T: raise NotImplementedError diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 59ee994f012d1..efefd76d15f07 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -3,7 +3,8 @@ from __future__ import annotations from collections import defaultdict -from typing import Any, Final, Sequence, Union +from collections.abc import Sequence +from typing import Any, Final, Union from mypyc.common import short_name from mypyc.ir.func_ir import FuncIR, all_values_full @@ -20,6 +21,7 @@ Cast, ComparisonOp, ControlOp, + CString, DecRef, Extend, Float, @@ -48,12 +50,14 @@ Register, Return, SetAttr, + SetElement, SetMem, Truncate, TupleGet, TupleSet, Unborrow, Unbox, + Undef, Unreachable, Value, ) @@ -192,7 +196,13 @@ def visit_method_call(self, op: MethodCall) -> str: return s def visit_cast(self, op: Cast) -> str: - return self.format("%r = %scast(%s, %r)", op, self.borrow_prefix(op), op.type, op.src) + if op.is_unchecked: + prefix = "unchecked " + else: + prefix = "" + return self.format( + "%r = %s%scast(%s, %r)", op, prefix, self.borrow_prefix(op), op.type, op.src + ) def visit_box(self, op: Box) -> str: return self.format("%r = box(%s, %r)", op, op.src.type, op.src) @@ -219,19 +229,7 @@ def visit_call_c(self, op: CallC) -> str: return self.format("%r = %s(%s)", op, op.function_name, args_str) def visit_primitive_op(self, op: PrimitiveOp) -> str: - args = [] - arg_index = 0 - type_arg_index = 0 - for arg_type in zip(op.desc.arg_types): - if arg_type: - args.append(self.format("%r", op.args[arg_index])) - arg_index += 1 - else: - assert op.type_args - args.append(self.format("%r", op.type_args[type_arg_index])) - type_arg_index += 1 - - args_str = ", ".join(args) + args_str = ", ".join(self.format("%r", arg) for arg in op.args) if op.is_void: return self.format("%s %s", op.desc.name, args_str) else: @@ -275,7 +273,9 @@ def visit_float_comparison_op(self, op: FloatComparisonOp) -> str: return self.format("%r = %r %s %r", op, op.lhs, op.op_str[op.op], op.rhs) def visit_load_mem(self, op: LoadMem) -> str: - return self.format("%r = load_mem %r :: %t*", op, op.src, op.type) + return self.format( + "%r = %sload_mem %r :: %t*", op, self.borrow_prefix(op), op.src, op.type + ) def visit_set_mem(self, op: SetMem) -> str: return self.format("set_mem %r, %r :: %t*", op.dest, op.src, op.dest_type) @@ -283,6 +283,9 @@ def visit_set_mem(self, op: SetMem) -> str: def visit_get_element_ptr(self, op: GetElementPtr) -> str: return self.format("%r = get_element_ptr %r %s :: %t", op, op.src, op.field, op.src_type) + def visit_set_element(self, op: SetElement) -> str: + return self.format("%r = set_element %r, %s, %r", op, op.src, op.field, op.item) + def visit_load_address(self, op: LoadAddress) -> str: if isinstance(op.src, Register): return self.format("%r = load_address %r", op, op.src) @@ -338,6 +341,10 @@ def format(self, fmt: str, *args: Any) -> str: result.append(str(arg.value)) elif isinstance(arg, Float): result.append(repr(arg.value)) + elif isinstance(arg, CString): + result.append(f"CString({arg.value!r})") + elif isinstance(arg, Undef): + result.append(f"undef {arg.type.name}") else: result.append(self.names[arg]) elif typespec == "d": @@ -494,7 +501,7 @@ def generate_names_for_ir(args: list[Register], blocks: list[BasicBlock]) -> dic continue if isinstance(value, Register) and value.name: name = value.name - elif isinstance(value, (Integer, Float)): + elif isinstance(value, (Integer, Float, Undef)): continue else: name = "r%d" % temp_index diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index fecfaee5ef77e..66b98e5d63983 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -18,15 +18,30 @@ mypyc.irbuild.mapper.Mapper.type_to_rtype converts mypy Types to mypyc RTypes. + +NOTE: As a convention, we don't create subclasses of concrete RType + subclasses (e.g. you shouldn't define a subclass of RTuple, which + is a concrete class). We prefer a flat class hierarchy. + + If you want to introduce a variant of an existing class, you'd + typically add an attribute (e.g. a flag) to an existing concrete + class to enable the new behavior. In rare cases, adding a new + abstract base class could also be an option. Adding a completely + separate class and sharing some functionality using module-level + helper functions may also be reasonable. + + This makes it possible to use isinstance(x, ) checks without worrying about potential subclasses + and avoids most trouble caused by implementation inheritance. """ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar +from typing import TYPE_CHECKING, ClassVar, Final, Generic, TypeVar, final from typing_extensions import TypeGuard -from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name +from mypyc.common import HAVE_IMMORTAL, IS_32_BIT_PLATFORM, PLATFORM_SIZE, JsonDict, short_name from mypyc.namegen import NameGenerator if TYPE_CHECKING: @@ -58,17 +73,35 @@ class RType: # to checking for error value as the return value of a function. # # For example, no i64 value can be reserved for error value, so we - # pick an arbitrary value (e.g. -113) to signal error, but this is - # also a valid non-error value. + # pick an arbitrary value (-113) to signal error, but this is + # also a valid non-error value. The chosen value is rare as a + # normal, non-error value, so most of the time we can avoid calling + # PyErr_Occurred() when checking for errors raised by called + # functions. + # + # This also means that if an attribute with this type might be + # undefined, we can't just rely on the error value to signal this. + # Instead, we add a bitfield to keep track whether attributes with + # "error overlap" have a value. If there is no value, AttributeError + # is raised on attribute read. Parameters with default values also + # use the bitfield trick to indicate whether the caller passed a + # value. (If we can determine that an attribute is "always defined", + # we never raise an AttributeError and don't need the bitfield + # entry.) error_overlap = False @abstractmethod def accept(self, visitor: RTypeVisitor[T]) -> T: - raise NotImplementedError + raise NotImplementedError() def short_name(self) -> str: return short_name(self.name) + @property + @abstractmethod + def may_be_immortal(self) -> bool: + raise NotImplementedError + def __str__(self) -> str: return short_name(self.name) @@ -109,34 +142,35 @@ class RTypeVisitor(Generic[T]): """Generic visitor over RTypes (uses the visitor design pattern).""" @abstractmethod - def visit_rprimitive(self, typ: RPrimitive) -> T: + def visit_rprimitive(self, typ: RPrimitive, /) -> T: raise NotImplementedError @abstractmethod - def visit_rinstance(self, typ: RInstance) -> T: + def visit_rinstance(self, typ: RInstance, /) -> T: raise NotImplementedError @abstractmethod - def visit_runion(self, typ: RUnion) -> T: + def visit_runion(self, typ: RUnion, /) -> T: raise NotImplementedError @abstractmethod - def visit_rtuple(self, typ: RTuple) -> T: + def visit_rtuple(self, typ: RTuple, /) -> T: raise NotImplementedError @abstractmethod - def visit_rstruct(self, typ: RStruct) -> T: + def visit_rstruct(self, typ: RStruct, /) -> T: raise NotImplementedError @abstractmethod - def visit_rarray(self, typ: RArray) -> T: + def visit_rarray(self, typ: RArray, /) -> T: raise NotImplementedError @abstractmethod - def visit_rvoid(self, typ: RVoid) -> T: + def visit_rvoid(self, typ: RVoid, /) -> T: raise NotImplementedError +@final class RVoid(RType): """The void type (no value). @@ -151,10 +185,14 @@ class RVoid(RType): def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rvoid(self) + @property + def may_be_immortal(self) -> bool: + return False + def serialize(self) -> str: return "void" - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RVoid]: return isinstance(other, RVoid) def __hash__(self) -> int: @@ -165,6 +203,7 @@ def __hash__(self) -> int: void_rtype: Final = RVoid() +@final class RPrimitive(RType): """Primitive type such as 'object' or 'int'. @@ -193,6 +232,7 @@ def __init__( ctype: str = "PyObject *", size: int = PLATFORM_SIZE, error_overlap: bool = False, + may_be_immortal: bool = True, ) -> None: RPrimitive.primitive_map[name] = self @@ -204,6 +244,7 @@ def __init__( self._ctype = ctype self.size = size self.error_overlap = error_overlap + self._may_be_immortal = may_be_immortal and HAVE_IMMORTAL if ctype == "CPyTagged": self.c_undefined = "CPY_INT_TAG" elif ctype in ("int16_t", "int32_t", "int64_t"): @@ -213,13 +254,11 @@ def __init__( elif ctype == "CPyPtr": # TODO: Invent an overlapping error value? self.c_undefined = "0" - elif ctype == "PyObject *": - # Boxed types use the null pointer as the error value. + elif ctype.endswith("*"): + # Boxed and pointer types use the null pointer as the error value. self.c_undefined = "NULL" elif ctype == "char": self.c_undefined = "2" - elif ctype in ("PyObject **", "void *"): - self.c_undefined = "NULL" elif ctype == "double": self.c_undefined = "-113.0" elif ctype in ("uint8_t", "uint16_t", "uint32_t", "uint64_t"): @@ -230,13 +269,17 @@ def __init__( def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rprimitive(self) + @property + def may_be_immortal(self) -> bool: + return self._may_be_immortal + def serialize(self) -> str: return self.name def __repr__(self) -> str: return "" % self.name - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RPrimitive]: return isinstance(other, RPrimitive) and other.name == self.name def __hash__(self) -> int: @@ -400,6 +443,10 @@ def __hash__(self) -> int: "c_ptr", is_unboxed=False, is_refcounted=False, ctype="void *" ) +cstring_rprimitive: Final = RPrimitive( + "cstring", is_unboxed=True, is_refcounted=False, ctype="const char *" +) + # The type corresponding to mypyc.common.BITMAP_TYPE bitmap_rprimitive: Final = uint32_rprimitive @@ -433,8 +480,12 @@ def __hash__(self) -> int: "builtins.None", is_unboxed=True, is_refcounted=False, ctype="char", size=1 ) -# Python list object (or an instance of a subclass of list). -list_rprimitive: Final = RPrimitive("builtins.list", is_unboxed=False, is_refcounted=True) +# Python list object (or an instance of a subclass of list). These could be +# immortal, but since this is expected to be very rare, and the immortality checks +# can be pretty expensive for lists, we treat lists as non-immortal. +list_rprimitive: Final = RPrimitive( + "builtins.list", is_unboxed=False, is_refcounted=True, may_be_immortal=False +) # Python dict object (or an instance of a subclass of dict). dict_rprimitive: Final = RPrimitive("builtins.dict", is_unboxed=False, is_refcounted=True) @@ -442,6 +493,11 @@ def __hash__(self) -> int: # Python set object (or an instance of a subclass of set). set_rprimitive: Final = RPrimitive("builtins.set", is_unboxed=False, is_refcounted=True) +# Python frozenset object (or an instance of a subclass of frozenset). +frozenset_rprimitive: Final = RPrimitive( + "builtins.frozenset", is_unboxed=False, is_refcounted=True +) + # Python str object. At the C layer, str is referred to as unicode # (PyUnicode). str_rprimitive: Final = RPrimitive("builtins.str", is_unboxed=False, is_refcounted=True) @@ -456,16 +512,25 @@ def __hash__(self) -> int: # Python range object. range_rprimitive: Final = RPrimitive("builtins.range", is_unboxed=False, is_refcounted=True) +KNOWN_NATIVE_TYPES: Final = { + name: RPrimitive(name, is_unboxed=False, is_refcounted=True) + for name in ["librt.internal.WriteBuffer", "librt.internal.ReadBuffer"] +} -def is_tagged(rtype: RType) -> bool: + +def is_native_rprimitive(rtype: RType) -> bool: + return isinstance(rtype, RPrimitive) and rtype.name in KNOWN_NATIVE_TYPES + + +def is_tagged(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int_rprimitive or rtype is short_int_rprimitive -def is_int_rprimitive(rtype: RType) -> bool: +def is_int_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int_rprimitive -def is_short_int_rprimitive(rtype: RType) -> bool: +def is_short_int_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is short_int_rprimitive @@ -479,7 +544,7 @@ def is_int32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: ) -def is_int64_rprimitive(rtype: RType) -> bool: +def is_int64_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is int64_rprimitive or ( rtype is c_pyssize_t_rprimitive and rtype._ctype == "int64_t" ) @@ -498,73 +563,93 @@ def is_uint8_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is uint8_rprimitive -def is_uint32_rprimitive(rtype: RType) -> bool: +def is_uint32_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is uint32_rprimitive -def is_uint64_rprimitive(rtype: RType) -> bool: +def is_uint64_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is uint64_rprimitive -def is_c_py_ssize_t_rprimitive(rtype: RType) -> bool: +def is_c_py_ssize_t_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is c_pyssize_t_rprimitive -def is_pointer_rprimitive(rtype: RType) -> bool: +def is_pointer_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return rtype is pointer_rprimitive -def is_float_rprimitive(rtype: RType) -> bool: +def is_float_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.float" -def is_bool_rprimitive(rtype: RType) -> bool: +def is_bool_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.bool" -def is_bit_rprimitive(rtype: RType) -> bool: +def is_bit_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "bit" -def is_object_rprimitive(rtype: RType) -> bool: +def is_bool_or_bit_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: + return is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype) + + +def is_object_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.object" -def is_none_rprimitive(rtype: RType) -> bool: +def is_none_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.None" -def is_list_rprimitive(rtype: RType) -> bool: +def is_list_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.list" -def is_dict_rprimitive(rtype: RType) -> bool: +def is_dict_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.dict" -def is_set_rprimitive(rtype: RType) -> bool: +def is_set_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.set" -def is_str_rprimitive(rtype: RType) -> bool: +def is_frozenset_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: + return isinstance(rtype, RPrimitive) and rtype.name == "builtins.frozenset" + + +def is_str_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.str" -def is_bytes_rprimitive(rtype: RType) -> bool: +def is_bytes_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.bytes" -def is_tuple_rprimitive(rtype: RType) -> bool: +def is_tuple_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.tuple" -def is_range_rprimitive(rtype: RType) -> bool: +def is_range_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and rtype.name == "builtins.range" -def is_sequence_rprimitive(rtype: RType) -> bool: +def is_sequence_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: return isinstance(rtype, RPrimitive) and ( - is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype) + is_list_rprimitive(rtype) + or is_tuple_rprimitive(rtype) + or is_str_rprimitive(rtype) + or is_bytes_rprimitive(rtype) + ) + + +def is_immutable_rprimitive(rtype: RType) -> TypeGuard[RPrimitive]: + return ( + is_str_rprimitive(rtype) + or is_bytes_rprimitive(rtype) + or is_tuple_rprimitive(rtype) + or is_frozenset_rprimitive(rtype) ) @@ -609,6 +694,7 @@ def visit_rvoid(self, t: RVoid) -> str: assert False, "rvoid in tuple?" +@final class RTuple(RType): """Fixed-length unboxed tuple (represented as a C struct). @@ -642,13 +728,17 @@ def __init__(self, types: list[RType]) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rtuple(self) + @property + def may_be_immortal(self) -> bool: + return False + def __str__(self) -> str: return "tuple[%s]" % ", ".join(str(typ) for typ in self.types) def __repr__(self) -> str: return "" % ", ".join(repr(typ) for typ in self.types) - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RTuple]: return isinstance(other, RTuple) and self.types == other.types def __hash__(self) -> int: @@ -746,6 +836,7 @@ def compute_aligned_offsets_and_size(types: list[RType]) -> tuple[list[int], int return offsets, final_size +@final class RStruct(RType): """C struct type""" @@ -763,6 +854,10 @@ def __init__(self, name: str, names: list[str], types: list[RType]) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rstruct(self) + @property + def may_be_immortal(self) -> bool: + return False + def __str__(self) -> str: # if not tuple(unnamed structs) return "{}{{{}}}".format( @@ -776,7 +871,7 @@ def __repr__(self) -> str: ", ".join(name + ":" + repr(typ) for name, typ in zip(self.names, self.types)), ) - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RStruct]: return ( isinstance(other, RStruct) and self.name == other.name @@ -795,6 +890,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> RStruct: assert False +@final class RInstance(RType): """Instance of user-defined class (compiled to C extension class). @@ -823,6 +919,10 @@ def __init__(self, class_ir: ClassIR) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rinstance(self) + @property + def may_be_immortal(self) -> bool: + return False + def struct_name(self, names: NameGenerator) -> str: return self.class_ir.struct_name(names) @@ -841,7 +941,7 @@ def attr_type(self, name: str) -> RType: def __repr__(self) -> str: return "" % self.name - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RInstance]: return isinstance(other, RInstance) and other.name == self.name def __hash__(self) -> int: @@ -851,6 +951,7 @@ def serialize(self) -> str: return self.name +@final class RUnion(RType): """union[x, ..., y]""" @@ -883,6 +984,10 @@ def make_simplified_union(items: list[RType]) -> RType: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_runion(self) + @property + def may_be_immortal(self) -> bool: + return any(item.may_be_immortal for item in self.items) + def __repr__(self) -> str: return "" % ", ".join(str(item) for item in self.items) @@ -890,7 +995,7 @@ def __str__(self) -> str: return "union[%s]" % ", ".join(str(item) for item in self.items) # We compare based on the set because order in a union doesn't matter - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RUnion]: return isinstance(other, RUnion) and self.items_set == other.items_set def __hash__(self) -> int: @@ -922,7 +1027,7 @@ def flatten_nested_unions(types: list[RType]) -> list[RType]: def optional_value_type(rtype: RType) -> RType | None: """If rtype is the union of none_rprimitive and another type X, return X. - Otherwise return None. + Otherwise, return None. """ if isinstance(rtype, RUnion) and len(rtype.items) == 2: if rtype.items[0] == none_rprimitive: @@ -932,11 +1037,12 @@ def optional_value_type(rtype: RType) -> RType | None: return None -def is_optional_type(rtype: RType) -> bool: +def is_optional_type(rtype: RType) -> TypeGuard[RUnion]: """Is rtype an optional type with exactly two union items?""" return optional_value_type(rtype) is not None +@final class RArray(RType): """Fixed-length C array type (for example, int[5]). @@ -953,13 +1059,17 @@ def __init__(self, item_type: RType, length: int) -> None: def accept(self, visitor: RTypeVisitor[T]) -> T: return visitor.visit_rarray(self) + @property + def may_be_immortal(self) -> bool: + return False + def __str__(self) -> str: return f"{self.item_type}[{self.length}]" def __repr__(self) -> str: return f"" - def __eq__(self, other: object) -> bool: + def __eq__(self, other: object) -> TypeGuard[RArray]: return ( isinstance(other, RArray) and self.item_type == other.item_type diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index a9e1ce4719537..51a02ed5446d3 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -1,21 +1,13 @@ -"""Builder class used to transform a mypy AST to the IR form. +"""Builder class to transform a mypy AST to the IR form. -The IRBuilder class maintains transformation state and provides access -to various helpers used to implement the transform. - -The top-level transform control logic is in mypyc.irbuild.main. - -mypyc.irbuild.visitor.IRBuilderVisitor is used to dispatch based on mypy -AST node type to code that actually does the bulk of the work. For -example, expressions are transformed in mypyc.irbuild.expression and -functions are transformed in mypyc.irbuild.function. +See the docstring of class IRBuilder for more information. """ from __future__ import annotations +from collections.abc import Iterator, Sequence from contextlib import contextmanager -from typing import Any, Callable, Final, Iterator, Sequence, Union -from typing_extensions import overload +from typing import Any, Callable, Final, Union, overload from mypy.build import Graph from mypy.maptype import map_instance_to_supertype @@ -48,7 +40,6 @@ TypeAlias, TypeInfo, TypeParam, - UnaryExpr, Var, ) from mypy.types import ( @@ -60,13 +51,14 @@ Type, TypedDictType, TypeOfAny, + TypeVarLikeType, UninhabitedType, UnionType, get_proper_type, ) from mypy.util import module_prefix, split_target from mypy.visitor import ExpressionVisitor, StatementVisitor -from mypyc.common import BITMAP_BITS, SELF_NAME, TEMP_ATTR_NAME +from mypyc.common import BITMAP_BITS, GENERATOR_ATTRIBUTE_PREFIX, SELF_NAME, TEMP_ATTR_NAME from mypyc.crash import catch_errors from mypyc.errors import Errors from mypyc.ir.class_ir import ClassIR, NonExtClassInfo @@ -83,7 +75,9 @@ Integer, IntOp, LoadStatic, + MethodCall, Op, + PrimitiveDescription, RaiseStandardError, Register, SetAttr, @@ -97,6 +91,7 @@ RType, RUnion, bitmap_rprimitive, + bytes_rprimitive, c_pyssize_t_rprimitive, dict_rprimitive, int_rprimitive, @@ -110,6 +105,7 @@ object_rprimitive, str_rprimitive, ) +from mypyc.irbuild.constant_fold import constant_fold_expr from mypyc.irbuild.context import FuncInfo, ImplicitClass from mypyc.irbuild.ll_builder import LowLevelIRBuilder from mypyc.irbuild.mapper import Mapper @@ -135,6 +131,7 @@ from mypyc.primitives.list_ops import list_get_item_unsafe_op, list_pop_last, to_list from mypyc.primitives.misc_ops import check_unpack_count_op, get_module_dict_op, import_op from mypyc.primitives.registry import CFunctionDescription, function_ops +from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op # These int binary operations can borrow their operands safely, since the # primitives take this into consideration. @@ -153,6 +150,30 @@ class UnsupportedException(Exception): class IRBuilder: + """Builder class used to construct mypyc IR from a mypy AST. + + The IRBuilder class maintains IR transformation state and provides access + to various helpers used to implement the transform. + + mypyc.irbuild.visitor.IRBuilderVisitor is used to dispatch based on mypy + AST node type to code that actually does the bulk of the work. For + example, expressions are transformed in mypyc.irbuild.expression and + functions are transformed in mypyc.irbuild.function. + + Use the "accept()" method to translate individual mypy AST nodes to IR. + Other methods are used to generate IR for various lower-level operations. + + This class wraps the lower-level LowLevelIRBuilder class, an instance + of which is available through the "builder" attribute. The low-level + builder class doesn't have any knowledge of the mypy AST. Wrappers for + some LowLevelIRBuilder method are provided for convenience, but others + can also be accessed via the "builder" attribute. + + See also: + * The mypyc IR is defined in the mypyc.ir package. + * The top-level IR transform control logic is in mypyc.irbuild.main. + """ + def __init__( self, current_module: str, @@ -381,6 +402,15 @@ 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, + result_type: RType | None = None, + ) -> Value: + return self.builder.primitive_op(desc, args, line, result_type) + 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) @@ -393,6 +423,13 @@ def builtin_len(self, val: Value, line: int) -> Value: def new_tuple(self, items: list[Value], line: int) -> Value: return self.builder.new_tuple(items, line) + def debug_print(self, toprint: str | Value) -> None: + return self.builder.debug_print(toprint) + + def set_immortal_if_free_threaded(self, v: Value, line: int) -> None: + """Make an object immortal on free-threaded builds (to avoid contention).""" + self.builder.set_immortal_if_free_threaded(v, line) + # Helpers for IR building def add_to_non_ext_dict( @@ -400,7 +437,11 @@ 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) + + # It's important that accessing class dictionary items from multiple threads + # doesn't cause contention. + self.builder.set_immortal_if_free_threaded(val, line) def gen_import(self, id: str, line: int) -> None: self.imports[id] = None @@ -431,7 +472,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. @@ -521,8 +562,8 @@ def init_final_static( *, type_override: RType | None = None, ) -> None: - assert isinstance(lvalue, NameExpr) - assert isinstance(lvalue.node, Var) + assert isinstance(lvalue, NameExpr), lvalue + assert isinstance(lvalue.node, Var), lvalue.node if lvalue.node.final_value is None: if class_name is None: name = lvalue.name @@ -594,7 +635,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: @@ -612,7 +653,11 @@ def get_assignment_target( # current environment. if self.fn_info.is_generator: return self.add_var_to_env_class( - symbol, reg_type, self.fn_info.generator_class, reassign=False + symbol, + reg_type, + self.fn_info.generator_class, + reassign=False, + prefix=GENERATOR_ATTRIBUTE_PREFIX, ) # Otherwise define a new local variable. @@ -656,7 +701,12 @@ def get_assignment_target( assert False, "Unsupported lvalue: %r" % lvalue def read( - self, target: Value | AssignmentTarget, line: int = -1, can_borrow: bool = False + self, + target: Value | AssignmentTarget, + line: int = -1, + *, + can_borrow: bool = False, + allow_error_value: bool = False, ) -> Value: if isinstance(target, Value): return target @@ -672,12 +722,25 @@ def read( if isinstance(target, AssignmentTargetAttr): if isinstance(target.obj.type, RInstance) and target.obj.type.class_ir.is_ext_class: borrow = can_borrow and target.can_borrow - return self.add(GetAttr(target.obj, target.attr, line, borrow=borrow)) + return self.add( + GetAttr( + target.obj, + target.attr, + line, + borrow=borrow, + allow_error_value=allow_error_value, + ) + ) else: return self.py_get_attr(target.obj, target.attr, line) assert False, "Unsupported lvalue: %r" % target + def read_nullable_attr(self, obj: Value, attr: str, line: int = -1) -> Value: + """Read an attribute that might have an error value without raising AttributeError.""" + assert isinstance(obj.type, RInstance) and obj.type.class_ir.is_ext_class + return self.add(GetAttr(obj, attr, line, allow_error_value=True)) + def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: int) -> None: if isinstance(target, Register): self.add(Assign(target, self.coerce_rvalue(rvalue_reg, target.type, line))) @@ -686,12 +749,19 @@ def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: i self.add(Assign(target.register, rvalue_reg)) elif isinstance(target, AssignmentTargetAttr): if isinstance(target.obj_type, RInstance): - rvalue_reg = self.coerce_rvalue(rvalue_reg, target.type, line) - self.add(SetAttr(target.obj, target.attr, rvalue_reg, line)) + setattr = target.obj_type.class_ir.get_method("__setattr__") + if setattr: + key = self.load_str(target.attr) + boxed_reg = self.builder.box(rvalue_reg) + call = MethodCall(target.obj, setattr.name, [key, boxed_reg], line) + self.add(call) + else: + rvalue_reg = self.coerce_rvalue(rvalue_reg, target.type, line) + self.add(SetAttr(target.obj, target.attr, rvalue_reg, line)) 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 @@ -737,10 +807,15 @@ def process_sequence_assignment( values = [] for i in range(len(target.items)): item = target.items[i] - index = self.builder.load_int(i) + index: Value if is_list_rprimitive(rvalue.type): - item_value = self.call_c(list_get_item_unsafe_op, [rvalue, index], line) + index = Integer(i, c_pyssize_t_rprimitive) + item_value = self.primitive_op(list_get_item_unsafe_op, [rvalue, index], line) + elif is_tuple_rprimitive(rvalue.type): + index = Integer(i, c_pyssize_t_rprimitive) + item_value = self.call_c(tuple_get_item_unsafe_op, [rvalue, index], line) else: + index = self.builder.load_int(i) item_value = self.builder.gen_method_call( rvalue, "__getitem__", [index], item.type, line ) @@ -768,7 +843,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 +869,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 +888,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 @@ -890,12 +965,8 @@ def maybe_spill_assignable(self, value: Value) -> Register | AssignmentTarget: return reg def extract_int(self, e: Expression) -> int | None: - if isinstance(e, IntExpr): - return e.value - elif isinstance(e, UnaryExpr) and e.op == "-" and isinstance(e.expr, IntExpr): - return -e.expr.value - else: - return None + folded = constant_fold_expr(self, e) + return folded if isinstance(folded, int) else None def get_sequence_type(self, expr: Expression) -> RType: return self.get_sequence_type_from_type(self.types[expr]) @@ -906,49 +977,72 @@ def get_sequence_type_from_type(self, target_type: Type) -> RType: return RUnion.make_simplified_union( [self.get_sequence_type_from_type(item) for item in target_type.items] ) - assert isinstance(target_type, Instance), target_type - if target_type.type.fullname == "builtins.str": - return str_rprimitive - else: - return self.type_to_rtype(target_type.args[0]) + elif isinstance(target_type, Instance): + if target_type.type.fullname == "builtins.str": + return str_rprimitive + elif target_type.type.fullname == "builtins.bytes": + return bytes_rprimitive + try: + return self.type_to_rtype(target_type.args[0]) + except IndexError: + raise ValueError(f"{target_type!r} is not a valid sequence.") from None + # This elif-blocks are needed for iterating over classes derived from NamedTuple. + elif isinstance(target_type, TypeVarLikeType): + return self.get_sequence_type_from_type(target_type.upper_bound) + elif isinstance(target_type, TupleType): + items = target_type.items + assert items, "This function does not support empty tuples" + # Tuple might have elements of different types. + rtypes = set(map(self.mapper.type_to_rtype, items)) + if len(rtypes) == 1: + return rtypes.pop() + else: + return RUnion.make_simplified_union(list(rtypes)) + assert False, target_type def get_dict_base_type(self, expr: Expression) -> list[Instance]: """Find dict type of a dict-like expression. This is useful for dict subclasses like SymbolTable. """ - target_type = get_proper_type(self.types[expr]) + return self.get_dict_base_type_from_type(self.types[expr]) + + def get_dict_base_type_from_type(self, target_type: Type) -> list[Instance]: + target_type = get_proper_type(target_type) if isinstance(target_type, UnionType): - types = [get_proper_type(item) for item in target_type.items] + return [ + inner + for item in target_type.items + for inner in self.get_dict_base_type_from_type(item) + ] + if isinstance(target_type, TypeVarLikeType): + # Match behaviour of self.node_type + # We can only reach this point if `target_type` was a TypeVar(bound=dict[...]) + # or a ParamSpec. + return self.get_dict_base_type_from_type(target_type.upper_bound) + + if isinstance(target_type, TypedDictType): + target_type = target_type.fallback + dict_base = next( + base for base in target_type.type.mro if base.fullname == "typing.Mapping" + ) + elif isinstance(target_type, Instance): + dict_base = next( + base for base in target_type.type.mro if base.fullname == "builtins.dict" + ) else: - types = [target_type] - - dict_types = [] - for t in types: - if isinstance(t, TypedDictType): - t = t.fallback - dict_base = next(base for base in t.type.mro if base.fullname == "typing.Mapping") - else: - assert isinstance(t, Instance), t - dict_base = next(base for base in t.type.mro if base.fullname == "builtins.dict") - dict_types.append(map_instance_to_supertype(t, dict_base)) - return dict_types + assert False, f"Failed to extract dict base from {target_type}" + return [map_instance_to_supertype(target_type, dict_base)] def get_dict_key_type(self, expr: Expression) -> RType: dict_base_types = self.get_dict_base_type(expr) - if len(dict_base_types) == 1: - return self.type_to_rtype(dict_base_types[0].args[0]) - else: - rtypes = [self.type_to_rtype(t.args[0]) for t in dict_base_types] - return RUnion.make_simplified_union(rtypes) + rtypes = [self.type_to_rtype(t.args[0]) for t in dict_base_types] + return RUnion.make_simplified_union(rtypes) def get_dict_value_type(self, expr: Expression) -> RType: dict_base_types = self.get_dict_base_type(expr) - if len(dict_base_types) == 1: - return self.type_to_rtype(dict_base_types[0].args[1]) - else: - rtypes = [self.type_to_rtype(t.args[1]) for t in dict_base_types] - return RUnion.make_simplified_union(rtypes) + rtypes = [self.type_to_rtype(t.args[1]) for t in dict_base_types] + return RUnion.make_simplified_union(rtypes) def get_dict_item_type(self, expr: Expression) -> RType: key_type = self.get_dict_key_type(expr) @@ -979,17 +1073,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 +1145,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 @@ -1093,12 +1183,20 @@ def call_refexpr_with_args( ) def shortcircuit_expr(self, expr: OpExpr) -> Value: + def handle_right() -> Value: + if expr.right_unreachable: + self.builder.add( + RaiseStandardError( + RaiseStandardError.RUNTIME_ERROR, + "mypyc internal error: should be unreachable", + expr.right.line, + ) + ) + return self.builder.none() + return self.accept(expr.right) + return self.builder.shortcircuit_helper( - expr.op, - self.node_type(expr), - lambda: self.accept(expr.left), - lambda: self.accept(expr.right), - expr.line, + expr.op, self.node_type(expr), lambda: self.accept(expr.left), handle_right, expr.line ) # Basic helpers @@ -1126,7 +1224,7 @@ def flatten_classes(self, arg: RefExpr | TupleExpr) -> list[ClassIR] | None: return None return res - def enter(self, fn_info: FuncInfo | str = "") -> None: + def enter(self, fn_info: FuncInfo | str = "", *, ret_type: RType = none_rprimitive) -> None: if isinstance(fn_info, str): fn_info = FuncInfo(name=fn_info) self.builder = LowLevelIRBuilder(self.errors, self.options) @@ -1136,7 +1234,7 @@ def enter(self, fn_info: FuncInfo | str = "") -> None: self.runtime_args.append([]) self.fn_info = fn_info self.fn_infos.append(self.fn_info) - self.ret_types.append(none_rprimitive) + self.ret_types.append(ret_type) if fn_info.is_generator: self.nonlocal_control.append(GeneratorNonlocalControl()) else: @@ -1162,6 +1260,7 @@ def enter_method( ret_type: RType, fn_info: FuncInfo | str = "", self_type: RType | None = None, + internal: bool = False, ) -> Iterator[None]: """Generate IR for a method. @@ -1176,10 +1275,9 @@ def enter_method( self_type: If not None, override default type of the implicit 'self' argument (by default, derive type from class_ir) """ - self.enter(fn_info) + self.enter(fn_info, ret_type=ret_type) self.function_name_stack.append(name) self.class_ir_stack.append(class_ir) - self.ret_types[-1] = ret_type if self_type is None: self_type = RInstance(class_ir) self.add_argument(SELF_NAME, self_type) @@ -1190,7 +1288,7 @@ def enter_method( sig = FuncSignature(args, ret_type) name = self.function_name_stack.pop() class_ir = self.class_ir_stack.pop() - decl = FuncDecl(name, class_ir.name, self.module_name, sig) + decl = FuncDecl(name, class_ir.name, self.module_name, sig, internal=internal) ir = FuncIR(decl, arg_regs, blocks) class_ir.methods[name] = ir class_ir.method_decls[name] = ir.decl @@ -1216,7 +1314,7 @@ def add_local(self, symbol: SymbolNode, typ: RType, is_arg: bool = False) -> Reg Args: is_arg: is this a function argument """ - assert isinstance(symbol, SymbolNode) + assert isinstance(symbol, SymbolNode), symbol reg = Register( typ, remangle_redefinition_name(symbol.name), is_arg=is_arg, line=symbol.line ) @@ -1231,7 +1329,7 @@ def add_local_reg( """Like add_local, but return an assignment target instead of value.""" self.add_local(symbol, typ, is_arg) target = self.symtables[-1][symbol] - assert isinstance(target, AssignmentTargetRegister) + assert isinstance(target, AssignmentTargetRegister), target return target def add_self_to_env(self, cls: ClassIR) -> AssignmentTargetRegister: @@ -1258,12 +1356,20 @@ def node_type(self, node: Expression) -> RType: return self.type_to_rtype(mypy_type) def add_var_to_env_class( - self, var: SymbolNode, rtype: RType, base: FuncInfo | ImplicitClass, reassign: bool = False + self, + var: SymbolNode, + rtype: RType, + base: FuncInfo | ImplicitClass, + reassign: bool = False, + always_defined: bool = False, + prefix: str = "", ) -> AssignmentTarget: # First, define the variable name as an attribute of the environment class, and then # construct a target for that attribute. - name = remangle_redefinition_name(var.name) + name = prefix + remangle_redefinition_name(var.name) self.fn_info.env_class.attributes[name] = rtype + if always_defined: + self.fn_info.env_class.attrs_with_defaults.add(name) attr_target = AssignmentTargetAttr(base.curr_env_reg, name) if reassign: @@ -1302,7 +1408,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)) @@ -1351,6 +1457,10 @@ def add_function(self, func_ir: FuncIR, line: int) -> None: self.function_names.add(name) self.functions.append(func_ir) + def get_current_class_ir(self) -> ClassIR | None: + type_info = self.fn_info.fitem.info + return self.mapper.type_to_ir.get(type_info) + def gen_arg_defaults(builder: IRBuilder) -> None: """Generate blocks for arguments that have default values. @@ -1384,7 +1494,7 @@ def get_default() -> Value: GetAttr(builder.fn_info.callable_class.self_reg, name, arg.line) ) - assert isinstance(target, AssignmentTargetRegister) + assert isinstance(target, AssignmentTargetRegister), target reg = target.register if not reg.type.error_overlap: builder.assign_if_null(target.register, get_default, arg.initializer.line) @@ -1455,3 +1565,30 @@ def create_type_params( builder.init_type_var(tv, type_param.name, line) tvs.append(tv) return tvs + + +def calculate_arg_defaults( + builder: IRBuilder, + fn_info: FuncInfo, + func_reg: Value | None, + symtable: dict[SymbolNode, SymbolTarget], +) -> None: + """Calculate default argument values and store them. + + They are stored in statics for top level functions and in + the function objects for nested functions (while constants are + still stored computed on demand). + """ + fitem = fn_info.fitem + for arg in fitem.arguments: + # Constant values don't get stored but just recomputed + if arg.initializer and not is_constant(arg.initializer): + value = builder.coerce( + builder.accept(arg.initializer), symtable[arg.variable].type, arg.line + ) + if not fn_info.is_nested: + name = fitem.fullname + "." + arg.variable.name + builder.add(InitStatic(value, name, builder.module_name)) + else: + assert func_reg is not None + builder.add(SetAttr(func_reg, arg.variable.name, value, arg.line)) diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py index 599dbb81f7678..bbd1b909afb65 100644 --- a/mypyc/irbuild/callable_class.py +++ b/mypyc/irbuild/callable_class.py @@ -55,7 +55,8 @@ class for the nested function. # Define the actual callable class ClassIR, and set its # environment to point at the previously defined environment # class. - callable_class_ir = ClassIR(name, builder.module_name, is_generated=True) + callable_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True) + callable_class_ir.reuse_freed_instance = True # The functools @wraps decorator attempts to call setattr on # nested functions, so we create a dict for these nested diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 7e0a842b1b41a..324b44b95dc40 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -2,16 +2,17 @@ from __future__ import annotations -import typing_extensions from abc import abstractmethod 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 +27,7 @@ TypeParam, is_class_var, ) -from mypy.types import ENUM_REMOVED_PROPS, Instance, UnboundType, 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 @@ -63,8 +64,9 @@ handle_non_ext_method, load_type, ) +from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME from mypyc.irbuild.util import dataclass_type, get_func_def, is_constant, is_dataclass_decorator -from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op +from mypyc.primitives.dict_ops import dict_new_op, exact_dict_set_item_op from mypyc.primitives.generic_ops import ( iter_op, next_op, @@ -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: @@ -94,6 +97,10 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: This is the main entry point to this module. """ + if cdef.info not in builder.mapper.type_to_ir: + builder.error("Nested class definitions not supported", cdef.line) + return + ir = builder.mapper.type_to_ir[cdef.info] # We do this check here because the base field of parent @@ -129,6 +136,14 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: cls_builder = NonExtClassBuilder(builder, cdef) for stmt in cdef.defs.body: + if ( + isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef)) + and stmt.name == GENERATOR_HELPER_NAME + ): + builder.error( + f'Method name "{stmt.name}" is reserved for mypyc internal use', stmt.line + ) + if isinstance(stmt, OverloadedFuncDef) and stmt.is_property: if isinstance(cls_builder, NonExtClassBuilder): # properties with both getters and setters in non_extension @@ -145,7 +160,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: @@ -245,6 +262,9 @@ def finalize(self, ir: ClassIR) -> None: non_ext_class = load_non_ext_class(self.builder, ir, self.non_ext, self.cdef.line) non_ext_class = load_decorated_class(self.builder, self.cdef, non_ext_class) + # Try to avoid contention when using free threading. + self.builder.set_immortal_if_free_threaded(non_ext_class, self.cdef.line) + # Save the decorated class self.builder.add( InitStatic(non_ext_class, self.cdef.name, self.builder.module_name, NAMESPACE_TYPE) @@ -252,7 +272,7 @@ def finalize(self, ir: ClassIR) -> None: # Add the non-extension class to the dict self.builder.call_c( - dict_set_item_op, + exact_dict_set_item_op, [ self.builder.load_globals_dict(), self.builder.load_str(self.cdef.name), @@ -287,7 +307,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: @@ -372,9 +392,10 @@ def finalize(self, ir: ClassIR) -> None: dec = self.builder.accept( next(d for d in self.cdef.decorators if is_dataclass_decorator(d)) ) + dataclass_type_val = self.builder.load_str(dataclass_type(self.cdef) or "unknown") self.builder.call_c( dataclass_sleight_of_hand, - [dec, self.type_obj, self.non_ext.dict, self.non_ext.anns], + [dec, self.type_obj, self.non_ext.dict, self.non_ext.anns, dataclass_type_val], self.cdef.line, ) @@ -406,7 +427,7 @@ def get_type_annotation(self, stmt: AssignmentStmt) -> TypeInfo | None: type_name = stmt.rvalue.args[index] if isinstance(type_name, NameExpr) and isinstance(type_name.node, TypeInfo): lvalue = stmt.lvalues[0] - assert isinstance(lvalue, NameExpr) + assert isinstance(lvalue, NameExpr), lvalue return type_name.node return None @@ -431,6 +452,11 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: ) # Create the class tp = builder.call_c(pytype_from_template_op, [template, tp_bases, modname], cdef.line) + + # Set type object to be immortal if free threaded, as otherwise reference count contention + # can cause a big performance hit. + builder.set_immortal_if_free_threaded(tp, cdef.line) + # Immediately fix up the trait vtables, before doing anything with the class. ir = builder.mapper.type_to_ir[cdef.info] if not ir.is_trait and not ir.builtin_base: @@ -447,7 +473,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, @@ -462,7 +488,9 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: # Add it to the dict builder.call_c( - dict_set_item_op, [builder.load_globals_dict(), builder.load_str(cdef.name), tp], cdef.line + exact_dict_set_item_op, + [builder.load_globals_dict(), builder.load_str(cdef.name), tp], + cdef.line, ) return tp @@ -478,7 +506,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 +516,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 +536,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 ( @@ -532,29 +560,10 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: # HAX: Mypy internally represents TypedDict classes differently from what # should happen at runtime. Replace with something that works. module = "typing" - if builder.options.capi_version < (3, 9): - name = "TypedDict" - if builder.options.capi_version < (3, 8): - # TypedDict was added to typing in Python 3.8. - module = "typing_extensions" - # TypedDict is not a real type on typing_extensions 4.7.0+ - name = "_TypedDict" - if isinstance(typing_extensions.TypedDict, type): - raise RuntimeError( - "It looks like you may have an old version " - "of typing_extensions installed. " - "typing_extensions>=4.7.0 is required on Python 3.7." - ) - else: - # In Python 3.9 TypedDict is not a real type. - name = "_TypedDict" + name = "_TypedDict" base = builder.get_module_attr(module, name, cdef.line) elif is_named_tuple and cls.fullname == "builtins.tuple": - if builder.options.capi_version < (3, 9): - name = "NamedTuple" - else: - # This was changed in Python 3.9. - name = "_NamedTuple" + name = "_NamedTuple" base = builder.get_module_attr("typing", name, cdef.line) else: cls_module = cls.fullname.rsplit(".", 1)[0] @@ -574,11 +583,11 @@ def find_non_ext_metaclass(builder: IRBuilder, cdef: ClassDef, bases: Value) -> if cdef.metaclass: declared_metaclass = builder.accept(cdef.metaclass) else: - if cdef.info.typeddict_type is not None and builder.options.capi_version >= (3, 9): + if cdef.info.typeddict_type is not None: # In Python 3.9, the metaclass for class-based TypedDict is typing._TypedDictMeta. # We can't easily calculate it generically, so special case it. return builder.get_module_attr("typing", "_TypedDictMeta", cdef.line) - elif cdef.info.is_named_tuple and builder.options.capi_version >= (3, 9): + elif cdef.info.is_named_tuple: # In Python 3.9, the metaclass for class-based NamedTuple is typing.NamedTupleMeta. # We can't easily calculate it generically, so special case it. return builder.get_module_attr("typing", "NamedTupleMeta", cdef.line) @@ -598,7 +607,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 ) @@ -635,7 +644,16 @@ def add_non_ext_class_attr_ann( if get_type_info is not None: type_info = get_type_info(stmt) if type_info: - typ = load_type(builder, type_info, stmt.line) + # NOTE: Using string type information is similar to using + # `from __future__ import annotations` in standard python. + # NOTE: For string types we need to use the fullname since it + # includes the module. If string type doesn't have the module, + # @dataclass will try to get the current module and fail since the + # current module is not in sys.modules. + if builder.current_module == type_info.module_name and stmt.line < type_info.line: + typ = builder.load_str(type_info.fullname) + else: + typ = load_type(builder, type_info, stmt.unanalyzed_type, stmt.line) if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? @@ -651,12 +669,12 @@ def add_non_ext_class_attr_ann( # actually a forward reference due to the __annotations__ future? typ = builder.load_str(stmt.unanalyzed_type.original_str_expr) elif isinstance(ann_type, Instance): - typ = load_type(builder, ann_type.type, stmt.line) + typ = load_type(builder, ann_type.type, stmt.unanalyzed_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.call_c(exact_dict_set_item_op, [non_ext.anns, key, typ], stmt.line) def add_non_ext_class_attr( @@ -677,9 +695,10 @@ def add_non_ext_class_attr( # are final. if ( cdef.info.bases - and cdef.info.bases[0].type.fullname == "enum.Enum" + # Enum class must be the last parent class. + and cdef.info.bases[-1].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)) @@ -756,7 +775,7 @@ def generate_attr_defaults_init( self_var = builder.self() for stmt in default_assignments: lvalue = stmt.lvalues[0] - assert isinstance(lvalue, NameExpr) + assert isinstance(lvalue, NameExpr), lvalue if not stmt.is_final_def and not is_constant(stmt.rvalue): builder.warning("Unsupported default attribute value", stmt.rvalue.line) @@ -798,30 +817,44 @@ 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.builder.unary_not(eqval, line, likely_bool=True), 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( @@ -850,7 +883,7 @@ def load_decorated_class(builder: IRBuilder, cdef: ClassDef, type_obj: Value) -> dec_class = type_obj for d in reversed(decorators): decorator = d.accept(builder.visitor) - assert isinstance(decorator, Value) + assert isinstance(decorator, Value), decorator dec_class = builder.py_call(decorator, [dec_class], dec_class.line) return dec_class @@ -861,7 +894,7 @@ def cache_class_attrs( """Add class attributes to be cached to the global cache.""" typ = builder.load_native_type_object(cdef.info.fullname) for lval, rtype in attrs_to_cache: - assert isinstance(lval, NameExpr) + assert isinstance(lval, NameExpr), lval rval = builder.py_get_attr(typ, lval.name, cdef.line) builder.init_final_static(lval, rval, cdef.name, type_override=rtype) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 12a4b15dd40c8..b1133f95b18ee 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -10,7 +10,7 @@ from __future__ import annotations -from typing import Final, Union +from typing import TYPE_CHECKING, Final, Union from mypy.constant_fold import constant_fold_binary_op, constant_fold_unary_op from mypy.nodes import ( @@ -26,9 +26,11 @@ UnaryExpr, Var, ) -from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.util import bytes_from_str +if TYPE_CHECKING: + from mypyc.irbuild.builder import IRBuilder + # All possible result types of constant folding ConstantValue = Union[int, float, complex, str, bytes] CONST_TYPES: Final = (int, float, complex, str, bytes) diff --git a/mypyc/irbuild/context.py b/mypyc/irbuild/context.py index a740f0b821d94..d5a48bf838c83 100644 --- a/mypyc/irbuild/context.py +++ b/mypyc/irbuild/context.py @@ -95,6 +95,15 @@ def curr_env_reg(self) -> Value: assert self._curr_env_reg is not None return self._curr_env_reg + def can_merge_generator_and_env_classes(self) -> bool: + # In simple cases we can place the environment into the generator class, + # instead of having two separate classes. + if self._generator_class and not self._generator_class.ir.is_final_class: + result = False + else: + result = self.is_generator and not self.is_nested and not self.contains_nested + return result + class ImplicitClass: """Contains information regarding implicitly generated classes. @@ -162,6 +171,11 @@ def __init__(self, ir: ClassIR) -> None: # Holds the arg passed to send self.send_arg_reg: Value | None = None + # Holds the PyObject ** pointer through which return value can be passed + # instead of raising StopIteration(ret_value) (only if not NULL). This + # is used for faster native-to-native calls. + self.stop_iter_value_reg: Value | None = None + # The switch block is used to decide which instruction to go using the value held in the # next-label register. self.switch_block = BasicBlock() diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py index aa223fe20176f..2334b4370103f 100644 --- a/mypyc/irbuild/env_class.py +++ b/mypyc/irbuild/env_class.py @@ -18,7 +18,13 @@ def g() -> int: from __future__ import annotations from mypy.nodes import Argument, FuncDef, SymbolNode, Var -from mypyc.common import BITMAP_BITS, ENV_ATTR_NAME, SELF_NAME, bitmap_name +from mypyc.common import ( + BITMAP_BITS, + ENV_ATTR_NAME, + GENERATOR_ATTRIBUTE_PREFIX, + SELF_NAME, + bitmap_name, +) from mypyc.ir.class_ir import ClassIR from mypyc.ir.ops import Call, GetAttr, SetAttr, Value from mypyc.ir.rtypes import RInstance, bitmap_rprimitive, object_rprimitive @@ -43,8 +49,12 @@ class is generated, the function environment has not yet been containing a nested function. """ env_class = ClassIR( - f"{builder.fn_info.namespaced_name()}_env", builder.module_name, is_generated=True + f"{builder.fn_info.namespaced_name()}_env", + builder.module_name, + is_generated=True, + is_final_class=True, ) + env_class.reuse_freed_instance = True env_class.attributes[SELF_NAME] = RInstance(env_class) if builder.fn_info.is_nested: # If the function is nested, its environment class must contain an environment @@ -56,17 +66,18 @@ class is generated, the function environment has not yet been return env_class -def finalize_env_class(builder: IRBuilder) -> None: +def finalize_env_class(builder: IRBuilder, prefix: str = "") -> None: """Generate, instantiate, and set up the environment of an environment class.""" - instantiate_env_class(builder) + if not builder.fn_info.can_merge_generator_and_env_classes(): + instantiate_env_class(builder) # Iterate through the function arguments and replace local definitions (using registers) # that were previously added to the environment with references to the function's # environment class. if builder.fn_info.is_nested: - add_args_to_env(builder, local=False, base=builder.fn_info.callable_class) + add_args_to_env(builder, local=False, base=builder.fn_info.callable_class, prefix=prefix) else: - add_args_to_env(builder, local=False, base=builder.fn_info) + add_args_to_env(builder, local=False, base=builder.fn_info, prefix=prefix) def instantiate_env_class(builder: IRBuilder) -> Value: @@ -91,7 +102,7 @@ def instantiate_env_class(builder: IRBuilder) -> Value: return curr_env_reg -def load_env_registers(builder: IRBuilder) -> None: +def load_env_registers(builder: IRBuilder, prefix: str = "") -> None: """Load the registers for the current FuncItem being visited. Adds the arguments of the FuncItem to the environment. If the @@ -99,7 +110,7 @@ def load_env_registers(builder: IRBuilder) -> None: loads all of the outer environments of the FuncItem into registers so that they can be used when accessing free variables. """ - add_args_to_env(builder, local=True) + add_args_to_env(builder, local=True, prefix=prefix) fn_info = builder.fn_info fitem = fn_info.fitem @@ -108,7 +119,7 @@ def load_env_registers(builder: IRBuilder) -> None: # If this is a FuncDef, then make sure to load the FuncDef into its own environment # class so that the function can be called recursively. if isinstance(fitem, FuncDef) and fn_info.add_nested_funcs_to_env: - setup_func_for_recursive_call(builder, fitem, fn_info.callable_class) + setup_func_for_recursive_call(builder, fitem, fn_info.callable_class, prefix=prefix) def load_outer_env( @@ -129,8 +140,11 @@ def load_outer_env( assert isinstance(env.type, RInstance), f"{env} must be of type RInstance" for symbol, target in outer_env.items(): - env.type.class_ir.attributes[symbol.name] = target.type - symbol_target = AssignmentTargetAttr(env, symbol.name) + attr_name = symbol.name + if isinstance(target, AssignmentTargetAttr): + attr_name = target.attr + env.type.class_ir.attributes[attr_name] = target.type + symbol_target = AssignmentTargetAttr(env, attr_name) builder.add_target(symbol, symbol_target) return env @@ -173,6 +187,7 @@ def add_args_to_env( local: bool = True, base: FuncInfo | ImplicitClass | None = None, reassign: bool = True, + prefix: str = "", ) -> None: fn_info = builder.fn_info args = fn_info.fitem.arguments @@ -188,10 +203,53 @@ def add_args_to_env( if is_free_variable(builder, arg.variable) or fn_info.is_generator: rtype = builder.type_to_rtype(arg.variable.type) assert base is not None, "base cannot be None for adding nonlocal args" - builder.add_var_to_env_class(arg.variable, rtype, base, reassign=reassign) + builder.add_var_to_env_class( + arg.variable, rtype, base, reassign=reassign, prefix=prefix + ) -def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: ImplicitClass) -> None: +def add_vars_to_env(builder: IRBuilder, prefix: str = "") -> None: + """Add relevant local variables and nested functions to the environment class. + + Add all variables and functions that are declared/defined within current + function and are referenced in functions nested within this one to this + function's environment class so the nested functions can reference + them even if they are declared after the nested function's definition. + Note that this is done before visiting the body of the function. + """ + env_for_func: FuncInfo | ImplicitClass = builder.fn_info + if builder.fn_info.is_generator: + env_for_func = builder.fn_info.generator_class + elif builder.fn_info.is_nested or builder.fn_info.in_non_ext: + env_for_func = builder.fn_info.callable_class + + if builder.fn_info.fitem in builder.free_variables: + # Sort the variables to keep things deterministic + for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name): + if isinstance(var, Var): + rtype = builder.type_to_rtype(var.type) + builder.add_var_to_env_class( + var, rtype, env_for_func, reassign=False, prefix=prefix + ) + + if builder.fn_info.fitem in builder.encapsulating_funcs: + for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]: + if isinstance(nested_fn, FuncDef): + # The return type is 'object' instead of an RInstance of the + # callable class because differently defined functions with + # the same name and signature across conditional blocks + # will generate different callable classes, so the callable + # class that gets instantiated must be generic. + if nested_fn.is_generator: + prefix = GENERATOR_ATTRIBUTE_PREFIX + builder.add_var_to_env_class( + nested_fn, object_rprimitive, env_for_func, reassign=False, prefix=prefix + ) + + +def setup_func_for_recursive_call( + builder: IRBuilder, fdef: FuncDef, base: ImplicitClass, prefix: str = "" +) -> None: """Enable calling a nested function (with a callable class) recursively. Adds the instance of the callable class representing the given @@ -201,7 +259,8 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli """ # First, set the attribute of the environment class so that GetAttr can be called on it. prev_env = builder.fn_infos[-2].env_class - prev_env.attributes[fdef.name] = builder.type_to_rtype(fdef.type) + attr_name = prefix + fdef.name + prev_env.attributes[attr_name] = builder.type_to_rtype(fdef.type) if isinstance(base, GeneratorClass): # If we are dealing with a generator class, then we need to first get the register @@ -213,7 +272,7 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli # Obtain the instance of the callable class representing the FuncDef, and add it to the # current environment. - val = builder.add(GetAttr(prev_env_reg, fdef.name, -1)) + val = builder.add(GetAttr(prev_env_reg, attr_name, -1)) target = builder.add_local_reg(fdef, object_rprimitive) builder.assign(target, val, -1) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 8d7c089e20cd9..2ed347ca1e797 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -7,9 +7,11 @@ from __future__ import annotations import math -from typing import Callable, Sequence +from collections.abc import Sequence +from typing import Callable from mypy.nodes import ( + ARG_NAMED, ARG_POS, LDEF, AssertTypeExpr, @@ -59,6 +61,7 @@ Integer, LoadAddress, LoadLiteral, + PrimitiveDescription, RaiseStandardError, Register, TupleGet, @@ -66,6 +69,7 @@ Value, ) from mypyc.ir.rtypes import ( + RInstance, RTuple, bool_rprimitive, int_rprimitive, @@ -73,6 +77,7 @@ is_int_rprimitive, is_list_rprimitive, is_none_rprimitive, + is_object_rprimitive, object_rprimitive, set_rprimitive, ) @@ -92,13 +97,18 @@ join_formatted_strings, tokenizer_printf_style, ) -from mypyc.irbuild.specialize import apply_function_specialization, apply_method_specialization +from mypyc.irbuild.specialize import ( + apply_function_specialization, + apply_method_specialization, + translate_object_new, + translate_object_setattr, +) from mypyc.primitives.bytes_ops import bytes_slice_op -from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, dict_set_item_op -from mypyc.primitives.generic_ops import iter_op +from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, exact_dict_set_item_op +from mypyc.primitives.generic_ops import iter_op, name_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 @@ -114,9 +124,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: if expr.node is None: builder.add( RaiseStandardError( - RaiseStandardError.RUNTIME_ERROR, - "mypyc internal error: should be unreachable", - expr.line, + RaiseStandardError.NAME_ERROR, f'name "{expr.name}" is not defined', expr.line ) ) return builder.none() @@ -181,7 +189,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 @@ -217,6 +225,18 @@ def transform_member_expr(builder: IRBuilder, expr: MemberExpr) -> Value: obj = builder.accept(expr.expr, can_borrow=can_borrow) rtype = builder.node_type(expr) + if ( + is_object_rprimitive(obj.type) + and expr.name == "__name__" + and builder.options.capi_version >= (3, 11) + ): + return builder.primitive_op(name_op, [obj], expr.line) + + if isinstance(obj.type, RInstance) and expr.name == "__class__": + # A non-native class could override "__class__" using "__getattribute__", so + # only apply to RInstance types. + return builder.primitive_op(type_op, [obj], expr.line) + # Special case: for named tuples transform attribute access to faster index access. typ = get_proper_type(builder.types.get(expr.expr)) if isinstance(typ, TupleType) and typ.partial_fallback.type.is_named_tuple: @@ -276,8 +296,8 @@ def transform_super_expr(builder: IRBuilder, o: SuperExpr) -> Value: # Grab first argument vself: Value = next(iter_env) if builder.fn_info.is_generator: - # grab sixth argument (see comment in translate_super_method_call) - self_targ = list(builder.symtables[-1].values())[6] + # grab seventh argument (see comment in translate_super_method_call) + self_targ = list(builder.symtables[-1].values())[7] vself = builder.read(self_targ, builder.fn_info.fitem.line) elif not ir.is_ext_class: vself = next(iter_env) # second argument is self if non_extension class @@ -355,9 +375,10 @@ 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) + assert isinstance(callee.expr.node, TypeInfo), callee.expr.node ir = builder.mapper.type_to_ir[callee.expr.node] return call_classmethod(builder, ir, expr, callee) elif builder.is_module_member_expr(callee): @@ -456,34 +477,39 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe if callee.name in base.method_decls: break else: - if ( - ir.is_ext_class - and ir.builtin_base is None - and not ir.inherits_python - and callee.name == "__init__" - and len(expr.args) == 0 - ): - # Call translates to object.__init__(self), which is a - # no-op, so omit the call. - return builder.none() + if callee.name == "__new__": + result = translate_object_new(builder, expr, MemberExpr(callee.call, "__new__")) + if result: + return result + elif callee.name == "__setattr__": + result = translate_object_setattr( + builder, expr, MemberExpr(callee.call, "__setattr__") + ) + if result: + return result + if ir.is_ext_class and ir.builtin_base is None and not ir.inherits_python: + if callee.name == "__init__" and len(expr.args) == 0: + # Call translates to object.__init__(self), which is a + # no-op, so omit the call. + return builder.none() return translate_call(builder, expr, callee) decl = base.method_decl(callee.name) arg_values = [builder.accept(arg) for arg in expr.args] arg_kinds, arg_names = expr.arg_kinds.copy(), expr.arg_names.copy() - if decl.kind != FUNC_STATICMETHOD: + if decl.kind != FUNC_STATICMETHOD and decl.name != "__new__": # 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 + # For generator classes, the self target is the 7th value # in the symbol table (which is an ordered dict). This is sort # of ugly, but we can't search by name since the 'self' parameter # could be named anything, and it doesn't get added to the # environment indexes. - self_targ = list(builder.symtables[-1].values())[6] + self_targ = list(builder.symtables[-1].values())[7] vself = builder.read(self_targ, builder.fn_info.fitem.line) arg_values.insert(0, vself) arg_kinds.insert(0, ARG_POS) @@ -532,7 +558,7 @@ def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value: # Special case some int ops to allow borrowing operands. if is_int_rprimitive(ltype) and is_int_rprimitive(rtype): if expr.op == "//": - expr = try_optimize_int_floor_divide(expr) + expr = try_optimize_int_floor_divide(builder, expr) if expr.op in int_borrow_friendly_op: borrow_left = is_borrow_friendly_expr(builder, expr.right) borrow_right = True @@ -545,11 +571,11 @@ def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value: return builder.binary_op(left, right, expr.op, expr.line) -def try_optimize_int_floor_divide(expr: OpExpr) -> OpExpr: +def try_optimize_int_floor_divide(builder: IRBuilder, expr: OpExpr) -> OpExpr: """Replace // with a power of two with a right shift, if possible.""" - if not isinstance(expr.right, IntExpr): + divisor = constant_fold_expr(builder, expr.right) + if not isinstance(divisor, int): return expr - divisor = expr.right.value shift = divisor.bit_length() - 1 if 0 < shift < 28 and divisor == (1 << shift): return OpExpr(">>", expr.left, IntExpr(shift)) @@ -564,8 +590,12 @@ def transform_index_expr(builder: IRBuilder, expr: IndexExpr) -> Value: base = builder.accept(expr.base, can_borrow=can_borrow_base) - if isinstance(base.type, RTuple) and isinstance(index, IntExpr): - return builder.add(TupleGet(base, index.value, expr.line)) + if isinstance(base.type, RTuple): + folded_index = constant_fold_expr(builder, index) + if isinstance(folded_index, int): + length = len(base.type.types) + if -length <= folded_index <= length - 1: + return builder.add(TupleGet(base, folded_index, expr.line)) if isinstance(index, SliceExpr): value = try_gen_slice_op(builder, base, index) @@ -699,72 +729,9 @@ def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value: # x in (...)/[...] # x not in (...)/[...] first_op = e.operators[0] - if ( - first_op in ["in", "not in"] - and len(e.operators) == 1 - and isinstance(e.operands[1], (TupleExpr, ListExpr)) - ): - items = e.operands[1].items - n_items = len(items) - # x in y -> x == y[0] or ... or x == y[n] - # x not in y -> x != y[0] and ... and x != y[n] - # 16 is arbitrarily chosen to limit code size - if 1 < n_items < 16: - if e.operators[0] == "in": - bin_op = "or" - cmp_op = "==" - else: - bin_op = "and" - cmp_op = "!=" - lhs = e.operands[0] - mypy_file = builder.graph["builtins"].tree - assert mypy_file is not None - info = mypy_file.names["bool"].node - assert isinstance(info, TypeInfo) - bool_type = Instance(info, []) - exprs = [] - for item in items: - expr = ComparisonExpr([cmp_op], [lhs, item]) - builder.types[expr] = bool_type - exprs.append(expr) - - or_expr: Expression = exprs.pop(0) - for expr in exprs: - or_expr = OpExpr(bin_op, or_expr, expr) - builder.types[or_expr] = bool_type - return builder.accept(or_expr) - # x in [y]/(y) -> x == y - # x not in [y]/(y) -> x != y - elif n_items == 1: - if e.operators[0] == "in": - cmp_op = "==" - else: - cmp_op = "!=" - e.operators = [cmp_op] - e.operands[1] = items[0] - # x in []/() -> False - # x not in []/() -> True - elif n_items == 0: - if e.operators[0] == "in": - return builder.false() - else: - return builder.true() - - # x in {...} - # x not in {...} - if ( - first_op in ("in", "not in") - and len(e.operators) == 1 - and isinstance(e.operands[1], SetExpr) - ): - set_literal = precompute_set_literal(builder, e.operands[1]) - if set_literal is not None: - lhs = e.operands[0] - result = builder.builder.primitive_op( - set_in_op, [builder.accept(lhs), set_literal], e.line, bool_rprimitive - ) - if first_op == "not in": - return builder.unary_op(result, "not", e.line) + if first_op in ["in", "not in"] and len(e.operators) == 1: + result = try_specialize_in_expr(builder, first_op, e.operands[0], e.operands[1], e.line) + if result is not None: return result if len(e.operators) == 1: @@ -810,6 +777,86 @@ def go(i: int, prev: Value) -> Value: return go(0, builder.accept(e.operands[0])) +def try_specialize_in_expr( + builder: IRBuilder, op: str, lhs: Expression, rhs: Expression, line: int +) -> Value | None: + left: Value | None = None + items: list[Value] | None = None + + if isinstance(rhs, (TupleExpr, ListExpr)): + left = builder.accept(lhs) + items = [builder.accept(item) for item in rhs.items] + elif isinstance(builder.node_type(rhs), RTuple): + left = builder.accept(lhs) + tuple_val = builder.accept(rhs) + assert isinstance(tuple_val.type, RTuple) + items = [builder.add(TupleGet(tuple_val, i)) for i in range(len(tuple_val.type.types))] + + if items is not None: + assert left is not None + n_items = len(items) + # x in y -> x == y[0] or ... or x == y[n] + # x not in y -> x != y[0] and ... and x != y[n] + if n_items > 1: + if op == "in": + cmp_op = "==" + else: + cmp_op = "!=" + out = BasicBlock() + for item in items: + cmp = transform_basic_comparison(builder, cmp_op, left, item, line) + bool_val = builder.builder.bool_value(cmp) + next_block = BasicBlock() + if op == "in": + builder.add_bool_branch(bool_val, out, next_block) + else: + builder.add_bool_branch(bool_val, next_block, out) + builder.activate_block(next_block) + result_reg = Register(bool_rprimitive) + end = BasicBlock() + if op == "in": + values = builder.false(), builder.true() + else: + values = builder.true(), builder.false() + builder.assign(result_reg, values[0], line) + builder.goto(end) + builder.activate_block(out) + builder.assign(result_reg, values[1], line) + builder.goto(end) + builder.activate_block(end) + return result_reg + # x in [y]/(y) -> x == y + # x not in [y]/(y) -> x != y + elif n_items == 1: + if op == "in": + cmp_op = "==" + else: + cmp_op = "!=" + right = items[0] + return transform_basic_comparison(builder, cmp_op, left, right, line) + # x in []/() -> False + # x not in []/() -> True + elif n_items == 0: + if op == "in": + return builder.false() + else: + return builder.true() + + # x in {...} + # x not in {...} + if isinstance(rhs, SetExpr): + set_literal = precompute_set_literal(builder, rhs) + if set_literal is not None: + result = builder.builder.primitive_op( + set_in_op, [builder.accept(lhs), set_literal], line, bool_rprimitive + ) + if op == "not in": + return builder.unary_op(result, "not", line) + return result + + return None + + def translate_is_none(builder: IRBuilder, expr: Expression, negated: bool) -> Value: v = builder.accept(expr, can_borrow=True) return builder.binary_op(v, builder.none_object(), "is not" if negated else "is", expr.line) @@ -953,7 +1000,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 +1024,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 +1046,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 +1075,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.call_c(exact_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 +1092,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..33e4429356414 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -7,29 +7,39 @@ from __future__ import annotations -from typing import Callable, ClassVar +from typing import Callable, ClassVar, cast from mypy.nodes import ( ARG_POS, + BytesExpr, CallExpr, DictionaryComprehension, Expression, GeneratorExpr, + ListExpr, Lvalue, MemberExpr, NameExpr, RefExpr, SetExpr, + StarExpr, + StrExpr, TupleExpr, TypeAlias, + Var, ) +from mypy.types import LiteralType, TupleType, get_proper_type, get_proper_types from mypyc.ir.ops import ( + ERR_NEVER, BasicBlock, Branch, Integer, IntOp, LoadAddress, + LoadErrorValue, + LoadLiteral, LoadMem, + MethodCall, RaiseStandardError, Register, TupleGet, @@ -37,21 +47,27 @@ Value, ) from mypyc.ir.rtypes import ( + RInstance, RTuple, RType, bool_rprimitive, + c_pyssize_t_rprimitive, int_rprimitive, is_dict_rprimitive, is_fixed_width_rtype, + is_immutable_rprimitive, is_list_rprimitive, is_sequence_rprimitive, is_short_int_rprimitive, is_str_rprimitive, is_tuple_rprimitive, + object_pointer_rprimitive, + object_rprimitive, pointer_rprimitive, short_int_rprimitive, ) from mypyc.irbuild.builder import IRBuilder +from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME from mypyc.irbuild.targets import AssignmentTarget, AssignmentTargetTuple from mypyc.primitives.dict_ops import ( dict_check_size_op, @@ -62,12 +78,14 @@ dict_next_value_op, dict_value_iter_op, ) -from mypyc.primitives.exc_ops import no_err_occurred_op +from mypyc.primitives.exc_ops import no_err_occurred_op, propagate_if_error_op from mypyc.primitives.generic_ops import aiter_op, anext_op, iter_op, next_op from mypyc.primitives.list_ops import list_append_op, list_get_item_unsafe_op, new_list_set_item_op from mypyc.primitives.misc_ops import stop_async_iteration_op from mypyc.primitives.registry import CFunctionDescription from mypyc.primitives.set_ops import set_add_op +from mypyc.primitives.str_ops import str_get_item_unsafe_op +from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op GenFunc = Callable[[], None] @@ -141,6 +159,7 @@ def for_loop_helper_with_index( expr_reg: Value, body_insts: Callable[[Value], None], line: int, + length: Value, ) -> None: """Generate IR for a sequence iteration. @@ -153,7 +172,7 @@ def for_loop_helper_with_index( body_insts: a function that generates the body of the loop. It needs a index as parameter. """ - assert is_sequence_rprimitive(expr_reg.type) + assert is_sequence_rprimitive(expr_reg.type), (expr_reg, expr_reg.type) target_type = builder.get_sequence_type(expr) body_block = BasicBlock() @@ -162,11 +181,16 @@ def for_loop_helper_with_index( condition_block = BasicBlock() for_gen = ForSequence(builder, index, body_block, exit_block, line, False) - for_gen.init(expr_reg, target_type, reverse=False) + for_gen.init(expr_reg, target_type, reverse=False, length=length) builder.push_loop_stack(step_block, exit_block) - builder.goto_and_activate(condition_block) + if isinstance(length, Integer) and length.value > 0: + builder.goto(body_block) + builder.activate_block(condition_block) + else: + builder.goto_and_activate(condition_block) + for_gen.gen_condition() builder.activate_block(body_block) @@ -195,10 +219,9 @@ def sequence_from_generator_preallocate_helper( there is no condition list in the generator and only one original sequence with one index is allowed. - e.g. (1) tuple(f(x) for x in a_list/a_tuple) - (2) list(f(x) for x in a_list/a_tuple) - (3) [f(x) for x in a_list/a_tuple] - RTuple as an original sequence is not supported yet. + e.g. (1) tuple(f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple) + (2) list(f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple) + (3) [f(x) for x in a_list/a_tuple/a_str/a_bytes/an_rtuple] Args: empty_op_llbuilder: A function that can generate an empty sequence op when @@ -213,21 +236,61 @@ def sequence_from_generator_preallocate_helper( implementation. """ if len(gen.sequences) == 1 and len(gen.indices) == 1 and len(gen.condlists[0]) == 0: - rtype = builder.node_type(gen.sequences[0]) - if is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype): - sequence = builder.accept(gen.sequences[0]) - length = builder.builder.builtin_len(sequence, gen.line, use_pyssize_t=True) - target_op = empty_op_llbuilder(length, gen.line) - - def set_item(item_index: Value) -> None: - e = builder.accept(gen.left_expr) - builder.call_c(set_item_op, [target_op, item_index, e], gen.line) - - for_loop_helper_with_index( - builder, gen.indices[0], gen.sequences[0], sequence, set_item, gen.line - ) + line = gen.line + sequence_expr = gen.sequences[0] + rtype = builder.node_type(sequence_expr) + if not (is_sequence_rprimitive(rtype) or isinstance(rtype, RTuple)): + return None + + if isinstance(rtype, RTuple): + # If input is RTuple, box it to tuple_rprimitive for generic iteration + # TODO: this can be optimized a bit better with an unrolled ForRTuple helper + proper_type = get_proper_type(builder.types[sequence_expr]) + assert isinstance(proper_type, TupleType), proper_type + + # the for_loop_helper_with_index crashes for empty tuples, bail out + if not proper_type.items: + return None + + proper_types = get_proper_types(proper_type.items) + + get_item_ops: list[LoadLiteral | TupleGet] + if all(isinstance(typ, LiteralType) for typ in proper_types): + get_item_ops = [ + LoadLiteral(cast(LiteralType, typ).value, object_rprimitive) + for typ in proper_types + ] + + else: + sequence = builder.accept(sequence_expr) + get_item_ops = [ + ( + LoadLiteral(typ.value, object_rprimitive) + if isinstance(typ, LiteralType) + else TupleGet(sequence, i, line) + ) + for i, typ in enumerate(proper_types) + ] + + items = list(map(builder.add, get_item_ops)) + sequence = builder.new_tuple(items, line) + + else: + sequence = builder.accept(sequence_expr) + + length = get_expr_length_value(builder, sequence_expr, sequence, line, use_pyssize_t=True) - return target_op + target_op = empty_op_llbuilder(length, line) + + def set_item(item_index: Value) -> None: + e = builder.accept(gen.left_expr) + builder.call_c(set_item_op, [target_op, item_index, e], line) + + for_loop_helper_with_index( + builder, gen.indices[0], sequence_expr, sequence, set_item, line, length + ) + + return target_op return None @@ -251,7 +314,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 +349,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) @@ -511,7 +574,15 @@ def make_for_loop_generator( # Default to a generic for loop. if iterable_expr_reg is None: iterable_expr_reg = builder.accept(expr) - for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested) + + it = iterable_expr_reg.type + for_obj: ForNativeGenerator | ForIterable + if isinstance(it, RInstance) and it.class_ir.has_method(GENERATOR_HELPER_NAME): + # Directly call generator object methods if iterating over a native generator. + for_obj = ForNativeGenerator(builder, index, body_block, loop_exit, line, nested) + else: + # Generic implementation that works of arbitrary iterables. + for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested) item_type = builder._analyze_iterable_item_type(expr) item_rtype = builder.type_to_rtype(item_type) for_obj.init(iterable_expr_reg, item_rtype) @@ -571,7 +642,9 @@ def gen_cleanup(self) -> None: def load_len(self, expr: Value | AssignmentTarget) -> Value: """A helper to get collection length, used by several subclasses.""" - return self.builder.builder.builtin_len(self.builder.read(expr, self.line), self.line) + return self.builder.builder.builtin_len( + self.builder.read(expr, self.line), self.line, use_pyssize_t=True + ) class ForIterable(ForGenerator): @@ -586,7 +659,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 @@ -623,6 +696,63 @@ def gen_cleanup(self) -> None: self.builder.call_c(no_err_occurred_op, [], self.line) +class ForNativeGenerator(ForGenerator): + """Generate IR for a for loop over a native generator.""" + + def need_cleanup(self) -> bool: + # Create a new cleanup block for when the loop is finished. + return True + + def init(self, expr_reg: Value, target_type: RType) -> None: + # Define target to contains the generator expression. It's also the iterator. + # If we are inside a generator function, spill these into the environment class. + builder = self.builder + self.iter_target = builder.maybe_spill(expr_reg) + self.target_type = target_type + + def gen_condition(self) -> None: + builder = self.builder + line = self.line + self.return_value = Register(object_rprimitive) + err = builder.add(LoadErrorValue(object_rprimitive, undefines=True)) + builder.assign(self.return_value, err, line) + + # Call generated generator helper method, passing a PyObject ** as the final + # argument that will be used to store the return value in the return value + # register. We ignore the return value but the presence of a return value + # indicates that the generator has finished. This is faster than raising + # and catching StopIteration, which is the non-native way of doing this. + ptr = builder.add(LoadAddress(object_pointer_rprimitive, self.return_value)) + nn = builder.none_object() + helper_call = MethodCall( + builder.read(self.iter_target), GENERATOR_HELPER_NAME, [nn, nn, nn, nn, ptr], line + ) + # We provide custom handling for error values. + helper_call.error_kind = ERR_NEVER + + self.next_reg = builder.add(helper_call) + builder.add(Branch(self.next_reg, self.loop_exit, self.body_block, Branch.IS_ERROR)) + + def begin_body(self) -> None: + # Assign the value obtained from the generator helper method to the + # lvalue so that it can be referenced by code in the body of the loop. + builder = self.builder + line = self.line + # We unbox here so that iterating with tuple unpacking generates a tuple based + # unpack instead of an iterator based one. + next_reg = builder.coerce(self.next_reg, self.target_type, line) + builder.assign(builder.get_assignment_target(self.index), next_reg, line) + + def gen_step(self) -> None: + # Nothing to do here, since we get the next item as part of gen_condition(). + pass + + def gen_cleanup(self) -> None: + # If return value is NULL (it wasn't assigned to by the generator helper method), + # an exception was raised that we need to propagate. + self.builder.primitive_op(propagate_if_error_op, [self.return_value], self.line) + + class ForAsyncIterable(ForGenerator): """Generate IR for an async for loop.""" @@ -656,7 +786,7 @@ def gen_condition(self) -> None: def except_match() -> Value: addr = builder.add(LoadAddress(pointer_rprimitive, stop_async_iteration_op.src, line)) - return builder.add(LoadMem(stop_async_iteration_op.type, addr)) + return builder.add(LoadMem(stop_async_iteration_op.type, addr, borrow=True)) def try_body() -> None: awaitable = builder.call_c(anext_op, [builder.read(self.iter_target)], line) @@ -693,7 +823,11 @@ def unsafe_index(builder: IRBuilder, target: Value, index: Value, line: int) -> # since we want to use __getitem__ if we don't have an unsafe version, # so we just check manually. if is_list_rprimitive(target.type): - return builder.call_c(list_get_item_unsafe_op, [target, index], line) + return builder.primitive_op(list_get_item_unsafe_op, [target, index], line) + elif is_tuple_rprimitive(target.type): + return builder.call_c(tuple_get_item_unsafe_op, [target, index], line) + elif is_str_rprimitive(target.type): + return builder.call_c(str_get_item_unsafe_op, [target, index], line) else: return builder.gen_method_call(target, "__getitem__", [index], None, line) @@ -704,26 +838,41 @@ class ForSequence(ForGenerator): Supports iterating in both forward and reverse. """ - def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None: + length_reg: Value | AssignmentTarget | None + + def init( + self, expr_reg: Value, target_type: RType, reverse: bool, length: Value | None = None + ) -> None: + assert is_sequence_rprimitive(expr_reg.type), (expr_reg, expr_reg.type) builder = self.builder + # Record a Value indicating the length of the sequence, if known at compile time. + self.length = length self.reverse = reverse # Define target to contain the expression, along with the index that will be used # for the for-loop. If we are inside of a generator function, spill these into the # environment class. self.expr_target = builder.maybe_spill(expr_reg) + if is_immutable_rprimitive(expr_reg.type): + # If the expression is an immutable type, we can load the length just once. + self.length_reg = builder.maybe_spill(self.length or self.load_len(self.expr_target)) + else: + # Otherwise, even if the length is known, we must recalculate the length + # at every iteration for compatibility with python semantics. + self.length_reg = None if not reverse: - index_reg: Value = Integer(0) + index_reg: Value = Integer(0, c_pyssize_t_rprimitive) else: - index_reg = builder.binary_op( - self.load_len(self.expr_target), Integer(1), "-", self.line - ) + if self.length_reg is not None: + len_val = builder.read(self.length_reg) + else: + len_val = self.load_len(self.expr_target) + index_reg = builder.builder.int_sub(len_val, 1) self.index_target = builder.maybe_spill_assignable(index_reg) self.target_type = target_type def gen_condition(self) -> None: builder = self.builder line = self.line - # TODO: Don't reload the length each time when iterating an immutable sequence? if self.reverse: # If we are iterating in reverse order, we obviously need # to check that the index is still positive. Somewhat less @@ -735,9 +884,13 @@ def gen_condition(self) -> None: second_check = BasicBlock() builder.add_bool_branch(comparison, second_check, self.loop_exit) builder.activate_block(second_check) - # For compatibility with python semantics we recalculate the length - # at every iteration. - len_reg = self.load_len(self.expr_target) + if self.length_reg is None: + # For compatibility with python semantics we recalculate the length + # at every iteration. + len_reg = self.load_len(self.expr_target) + else: + # (unless input is immutable type). + len_reg = builder.read(self.length_reg, line) comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, "<", line) builder.add_bool_branch(comparison, self.body_block, self.loop_exit) @@ -766,13 +919,7 @@ def gen_step(self) -> None: builder = self.builder line = self.line step = 1 if not self.reverse else -1 - add = builder.int_op( - short_int_rprimitive, - builder.read(self.index_target, line), - Integer(step), - IntOp.ADD, - line, - ) + add = builder.builder.int_add(builder.read(self.index_target, line), step) builder.assign(self.index_target, add, line) @@ -902,7 +1049,7 @@ def begin_body(self) -> None: value = builder.add(TupleGet(self.next_tuple, 3, line)) # Coerce just in case e.g. key is itself a tuple to be unpacked. - assert isinstance(self.target_type, RTuple) + assert isinstance(self.target_type, RTuple), self.target_type key = builder.coerce(key, self.target_type.types[0], line) value = builder.coerce(value, self.target_type.types[1], line) @@ -985,7 +1132,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 +1143,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): @@ -1073,3 +1221,48 @@ def gen_step(self) -> None: def gen_cleanup(self) -> None: for gen in self.gens: gen.gen_cleanup() + + +def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None: + if isinstance(expr, (StrExpr, BytesExpr)): + return len(expr.value) + elif isinstance(expr, (ListExpr, TupleExpr)): + # if there are no star expressions, or we know the length of them, + # we know the length of the expression + stars = [get_expr_length(builder, i) for i in expr.items if isinstance(i, StarExpr)] + if None not in stars: + other = sum(not isinstance(i, StarExpr) for i in expr.items) + return other + sum(stars) # type: ignore [arg-type] + elif isinstance(expr, StarExpr): + return get_expr_length(builder, expr.expr) + elif ( + isinstance(expr, RefExpr) + and isinstance(expr.node, Var) + and expr.node.is_final + and isinstance(expr.node.final_value, str) + and expr.node.has_explicit_value + ): + return len(expr.node.final_value) + # TODO: extend this, passing length of listcomp and genexp should have worthwhile + # performance boost and can be (sometimes) figured out pretty easily. set and dict + # comps *can* be done as well but will need special logic to consider the possibility + # of key conflicts. Range, enumerate, zip are all simple logic. + + # we might still be able to get the length directly from the type + rtype = builder.node_type(expr) + if isinstance(rtype, RTuple): + return len(rtype.types) + return None + + +def get_expr_length_value( + builder: IRBuilder, expr: Expression, expr_reg: Value, line: int, use_pyssize_t: bool +) -> Value: + rtype = builder.node_type(expr) + assert is_sequence_rprimitive(rtype) or isinstance(rtype, RTuple), rtype + length = get_expr_length(builder, expr) + if length is None: + # We cannot compute the length at compile time, so we will fetch it. + return builder.builder.builtin_len(expr_reg, line, use_pyssize_t=use_pyssize_t) + # The expression result is known at compile time, so we can use a constant. + return Integer(length, c_pyssize_t_rprimitive if use_pyssize_t else short_int_rprimitive) diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 0b46887811fb5..5a35900006d24 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -12,7 +12,7 @@ ) from mypy.errors import Errors from mypy.messages import MessageBuilder -from mypy.nodes import Context, Expression +from mypy.nodes import Context, Expression, StrExpr from mypy.options import Options from mypyc.ir.ops import Integer, Value from mypyc.ir.rtypes import ( @@ -143,15 +143,17 @@ def convert_format_expr_to_str( for x, format_op in zip(exprs, format_ops): node_type = builder.node_type(x) if format_op == FormatOp.STR: - if is_str_rprimitive(node_type): + if is_str_rprimitive(node_type) or isinstance( + x, StrExpr + ): # NOTE: why does mypyc think our fake StrExprs are not str rprimitives? 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..738d19ea67485 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -13,7 +13,8 @@ from __future__ import annotations from collections import defaultdict -from typing import NamedTuple, Sequence +from collections.abc import Sequence +from typing import NamedTuple from mypy.nodes import ( ArgKind, @@ -24,12 +25,11 @@ FuncItem, LambdaExpr, OverloadedFuncDef, - SymbolNode, TypeInfo, Var, ) -from mypy.types import CallableType, get_proper_type -from mypyc.common import LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME +from mypy.types import CallableType, Type, UnboundType, get_proper_type +from mypyc.common import FAST_PREFIX, LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import ( FUNC_CLASSMETHOD, @@ -42,8 +42,8 @@ ) from mypyc.ir.ops import ( BasicBlock, + ComparisonOp, GetAttr, - InitStatic, Integer, LoadAddress, LoadLiteral, @@ -57,37 +57,33 @@ from mypyc.ir.rtypes import ( RInstance, bool_rprimitive, + c_int_rprimitive, dict_rprimitive, int_rprimitive, object_rprimitive, ) -from mypyc.irbuild.builder import IRBuilder, SymbolTarget, gen_arg_defaults +from mypyc.irbuild.builder import IRBuilder, calculate_arg_defaults, gen_arg_defaults from mypyc.irbuild.callable_class import ( add_call_to_callable_class, add_get_to_callable_class, instantiate_callable_class, setup_callable_class, ) -from mypyc.irbuild.context import FuncInfo, ImplicitClass +from mypyc.irbuild.context import FuncInfo, GeneratorClass from mypyc.irbuild.env_class import ( + add_vars_to_env, finalize_env_class, load_env_registers, - load_outer_envs, setup_env_class, - setup_func_for_recursive_call, -) -from mypyc.irbuild.generator import ( - add_methods_to_generator_class, - add_raise_exception_blocks_to_generator_class, - create_switch_for_generator_class, - gen_generator_func, - populate_switch_for_generator_class, - setup_env_for_generator_class, ) +from mypyc.irbuild.generator import gen_generator_func, gen_generator_func_body from mypyc.irbuild.targets import AssignmentTarget -from mypyc.irbuild.util import is_constant -from mypyc.primitives.dict_ops import dict_get_method_with_none, dict_new_op, dict_set_item_op -from mypyc.primitives.generic_ops import py_setattr_op +from mypyc.primitives.dict_ops import ( + dict_get_method_with_none, + dict_new_op, + exact_dict_set_item_op, +) +from mypyc.primitives.generic_ops import generic_getattr, generic_setattr, py_setattr_op from mypyc.primitives.misc_ops import register_function from mypyc.primitives.registry import builtin_names from mypyc.sametype import is_same_method_signature, is_same_type @@ -96,7 +92,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 +110,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) @@ -134,7 +130,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( - dict_set_item_op, + exact_dict_set_item_op, [builder.load_globals_dict(), builder.load_str(dec.func.name), decorated_func], decorated_func.line, ) @@ -146,7 +142,7 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: def transform_lambda_expr(builder: IRBuilder, expr: LambdaExpr) -> Value: typ = get_proper_type(builder.types[expr]) - assert isinstance(typ, CallableType) + assert isinstance(typ, CallableType), typ runtime_args = [] for arg, arg_type in zip(expr.arguments, typ.arg_types): @@ -176,6 +172,7 @@ def gen_func_item( name: str, sig: FuncSignature, cdef: ClassDef | None = None, + make_ext_method: bool = False, ) -> tuple[FuncIR, Value | None]: """Generate and return the FuncIR for a given FuncDef. @@ -227,129 +224,92 @@ def c() -> None: class_name = None if cdef: ir = builder.mapper.type_to_ir[cdef.info] - in_non_ext = not ir.is_ext_class + in_non_ext = not ir.is_ext_class and not make_ext_method class_name = cdef.name if is_singledispatch: func_name = singledispatch_main_func_name(name) else: func_name = name - builder.enter( - FuncInfo( - fitem=fitem, - name=func_name, - class_name=class_name, - namespace=gen_func_ns(builder), - is_nested=is_nested, - contains_nested=contains_nested, - is_decorated=is_decorated, - in_non_ext=in_non_ext, - add_nested_funcs_to_env=add_nested_funcs_to_env, - ) + + fn_info = FuncInfo( + fitem=fitem, + name=func_name, + class_name=class_name, + namespace=gen_func_ns(builder), + is_nested=is_nested, + contains_nested=contains_nested, + is_decorated=is_decorated, + in_non_ext=in_non_ext, + add_nested_funcs_to_env=add_nested_funcs_to_env, ) + is_generator = fn_info.is_generator + builder.enter(fn_info, ret_type=sig.ret_type) + + if is_generator: + fitem = builder.fn_info.fitem + assert isinstance(fitem, FuncDef), fitem + generator_class_ir = builder.mapper.fdef_to_generator[fitem] + builder.fn_info.generator_class = GeneratorClass(generator_class_ir) # Functions that contain nested functions need an environment class to store variables that # are free in their nested functions. Generator functions need an environment class to # store a variable denoting the next instruction to be executed when the __next__ function # is called, along with all the variables inside the function itself. - if builder.fn_info.contains_nested or builder.fn_info.is_generator: + if contains_nested or ( + is_generator and not builder.fn_info.can_merge_generator_and_env_classes() + ): setup_env_class(builder) - if builder.fn_info.is_nested or builder.fn_info.in_non_ext: + if is_nested or in_non_ext: setup_callable_class(builder) - if builder.fn_info.is_generator: - # Do a first-pass and generate a function that just returns a generator object. - gen_generator_func(builder) - args, _, blocks, ret_type, fn_info = builder.leave() - func_ir, func_reg = gen_func_ir( - builder, args, blocks, sig, fn_info, cdef, is_singledispatch + if is_generator: + # First generate a function that just constructs and returns a generator object. + func_ir, func_reg = gen_generator_func( + builder, + lambda args, blocks, fn_info: gen_func_ir( + builder, args, blocks, sig, fn_info, cdef, is_singledispatch + ), ) # Re-enter the FuncItem and visit the body of the function this time. - builder.enter(fn_info) - setup_env_for_generator_class(builder) - load_outer_envs(builder, builder.fn_info.generator_class) - top_level = builder.top_level_fn_info() - if ( - builder.fn_info.is_nested - and isinstance(fitem, FuncDef) - and top_level - and top_level.add_nested_funcs_to_env - ): - setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class) - create_switch_for_generator_class(builder) - add_raise_exception_blocks_to_generator_class(builder, fitem.line) + gen_generator_func_body(builder, fn_info, func_reg) else: - load_env_registers(builder) - gen_arg_defaults(builder) + func_ir, func_reg = gen_func_body(builder, sig, cdef, is_singledispatch) - if builder.fn_info.contains_nested and not builder.fn_info.is_generator: - finalize_env_class(builder) + if is_singledispatch: + # add the generated main singledispatch function + builder.functions.append(func_ir) + # create the dispatch function + assert isinstance(fitem, FuncDef), fitem + return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig) - builder.ret_types[-1] = sig.ret_type + return func_ir, func_reg - # Add all variables and functions that are declared/defined within this - # function and are referenced in functions nested within this one to this - # function's environment class so the nested functions can reference - # them even if they are declared after the nested function's definition. - # Note that this is done before visiting the body of this function. - - env_for_func: FuncInfo | ImplicitClass = builder.fn_info - if builder.fn_info.is_generator: - env_for_func = builder.fn_info.generator_class - elif builder.fn_info.is_nested or builder.fn_info.in_non_ext: - env_for_func = builder.fn_info.callable_class - - if builder.fn_info.fitem in builder.free_variables: - # Sort the variables to keep things deterministic - for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name): - if isinstance(var, Var): - rtype = builder.type_to_rtype(var.type) - builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False) - - if builder.fn_info.fitem in builder.encapsulating_funcs: - for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]: - if isinstance(nested_fn, FuncDef): - # The return type is 'object' instead of an RInstance of the - # callable class because differently defined functions with - # the same name and signature across conditional blocks - # will generate different callable classes, so the callable - # class that gets instantiated must be generic. - builder.add_var_to_env_class( - nested_fn, object_rprimitive, env_for_func, reassign=False - ) - builder.accept(fitem.body) +def gen_func_body( + builder: IRBuilder, sig: FuncSignature, cdef: ClassDef | None, is_singledispatch: bool +) -> tuple[FuncIR, Value | None]: + load_env_registers(builder) + gen_arg_defaults(builder) + if builder.fn_info.contains_nested: + finalize_env_class(builder) + add_vars_to_env(builder) + builder.accept(builder.fn_info.fitem.body) builder.maybe_add_implicit_return() - if builder.fn_info.is_generator: - populate_switch_for_generator_class(builder) - # Hang on to the local symbol table for a while, since we use it # to calculate argument defaults below. symtable = builder.symtables[-1] args, _, blocks, ret_type, fn_info = builder.leave() - if fn_info.is_generator: - add_methods_to_generator_class(builder, fn_info, sig, args, blocks, fitem.is_coroutine) - else: - func_ir, func_reg = gen_func_ir( - builder, args, blocks, sig, fn_info, cdef, is_singledispatch - ) + func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, fn_info, cdef, is_singledispatch) # Evaluate argument defaults in the surrounding scope, since we # calculate them *once* when the function definition is evaluated. calculate_arg_defaults(builder, fn_info, func_reg, symtable) - - if is_singledispatch: - # add the generated main singledispatch function - builder.functions.append(func_ir) - # create the dispatch function - assert isinstance(fitem, FuncDef) - return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig) - return func_ir, func_reg @@ -389,8 +349,12 @@ def gen_func_ir( add_get_to_callable_class(builder, fn_info) func_reg = instantiate_callable_class(builder, fn_info) else: - assert isinstance(fn_info.fitem, FuncDef) - func_decl = builder.mapper.func_to_decl[fn_info.fitem] + fitem = fn_info.fitem + assert isinstance(fitem, FuncDef), fitem + func_decl = builder.mapper.func_to_decl[fitem] + if cdef and fn_info.name == FAST_PREFIX + func_decl.name: + # Special-cased version of a method has a separate FuncDecl, use that one. + func_decl = builder.mapper.type_to_ir[cdef.info].method_decls[fn_info.name] if fn_info.is_decorated or is_singledispatch_main_func: class_name = None if cdef is None else cdef.name func_decl = FuncDecl( @@ -399,24 +363,122 @@ def gen_func_ir( builder.module_name, sig, func_decl.kind, - func_decl.is_prop_getter, - func_decl.is_prop_setter, - ) - func_ir = FuncIR( - func_decl, args, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name + is_prop_getter=func_decl.is_prop_getter, + is_prop_setter=func_decl.is_prop_setter, ) + func_ir = FuncIR(func_decl, args, blocks, fitem.line, traceback_name=fitem.name) else: - func_ir = FuncIR( - func_decl, args, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name - ) + func_ir = FuncIR(func_decl, args, blocks, fitem.line, traceback_name=fitem.name) return (func_ir, func_reg) +def generate_getattr_wrapper(builder: IRBuilder, cdef: ClassDef, getattr: FuncDef) -> None: + """ + Generate a wrapper function for __getattr__ that can be put into the tp_getattro slot. + The wrapper takes one argument besides self which is the attribute name. + It first checks if the name matches any of the attributes of this class. + If it does, it returns that attribute. If none match, it calls __getattr__. + + __getattr__ is not supported in classes that allow interpreted subclasses because the + tp_getattro slot is inherited by subclasses and if the subclass overrides __getattr__, + the override would be ignored in our wrapper. TODO: To support this, the wrapper would + have to check type of self and if it's not the compiled class, resolve "__getattr__" against + the type at runtime and call the returned method, like _Py_slot_tp_getattr_hook in cpython. + + __getattr__ is not supported in classes which inherit from non-native classes because those + have __dict__ which currently has some strange interactions when class attributes and + variables are assigned through __dict__ vs. through regular attribute access. Allowing + __getattr__ on top of that could be problematic. + """ + name = getattr.name + "__wrapper" + ir = builder.mapper.type_to_ir[cdef.info] + line = getattr.line + + error_base = f'"__getattr__" not supported in class "{cdef.name}" because ' + if ir.allow_interpreted_subclasses: + builder.error(error_base + "it allows interpreted subclasses", line) + if ir.inherits_python: + builder.error(error_base + "it inherits from a non-native class", line) + + with builder.enter_method(ir, name, object_rprimitive, internal=True): + attr_arg = builder.add_argument("attr", object_rprimitive) + generic_getattr_result = builder.call_c(generic_getattr, [builder.self(), attr_arg], line) + + return_generic, call_getattr = BasicBlock(), BasicBlock() + null = Integer(0, object_rprimitive, line) + got_generic = builder.add( + ComparisonOp(generic_getattr_result, null, ComparisonOp.NEQ, line) + ) + builder.add_bool_branch(got_generic, return_generic, call_getattr) + + builder.activate_block(return_generic) + builder.add(Return(generic_getattr_result, line)) + + builder.activate_block(call_getattr) + # No attribute matched so call user-provided __getattr__. + getattr_result = builder.gen_method_call( + builder.self(), getattr.name, [attr_arg], object_rprimitive, line + ) + builder.add(Return(getattr_result, line)) + + +def generate_setattr_wrapper(builder: IRBuilder, cdef: ClassDef, setattr: FuncDef) -> None: + """ + Generate a wrapper function for __setattr__ that can be put into the tp_setattro slot. + The wrapper takes two arguments besides self - attribute name and the new value. + Returns 0 on success and -1 on failure. Restrictions are similar to the __getattr__ + wrapper above. + + The wrapper calls the user-defined __setattr__ when the value to set is not NULL. + When it's NULL, this means that the call to tp_setattro comes from a del statement, + so it calls __delattr__ instead. If __delattr__ is not overridden in the native class, + this will call the base implementation in object which doesn't work without __dict__. + """ + name = setattr.name + "__wrapper" + ir = builder.mapper.type_to_ir[cdef.info] + line = setattr.line + + error_base = f'"__setattr__" not supported in class "{cdef.name}" because ' + if ir.allow_interpreted_subclasses: + builder.error(error_base + "it allows interpreted subclasses", line) + if ir.inherits_python: + builder.error(error_base + "it inherits from a non-native class", line) + + with builder.enter_method(ir, name, c_int_rprimitive, internal=True): + attr_arg = builder.add_argument("attr", object_rprimitive) + value_arg = builder.add_argument("value", object_rprimitive) + + call_delattr, call_setattr = BasicBlock(), BasicBlock() + null = Integer(0, object_rprimitive, line) + is_delattr = builder.add(ComparisonOp(value_arg, null, ComparisonOp.EQ, line)) + builder.add_bool_branch(is_delattr, call_delattr, call_setattr) + + builder.activate_block(call_delattr) + delattr_symbol = cdef.info.get("__delattr__") + delattr = delattr_symbol.node if delattr_symbol else None + delattr_override = delattr is not None and not delattr.fullname.startswith("builtins.") + if delattr_override: + builder.gen_method_call(builder.self(), "__delattr__", [attr_arg], None, line) + else: + # Call internal function that cpython normally calls when deleting an attribute. + # Cannot call object.__delattr__ here because it calls PyObject_SetAttr internally + # which in turn calls our wrapper and recurses infinitely. + # Note that since native classes don't have __dict__, this will raise AttributeError + # for dynamic attributes. + builder.call_c(generic_setattr, [builder.self(), attr_arg, null], line) + builder.add(Return(Integer(0, c_int_rprimitive), line)) + + builder.activate_block(call_setattr) + builder.gen_method_call(builder.self(), setattr.name, [attr_arg, value_arg], None, line) + builder.add(Return(Integer(0, c_int_rprimitive), line)) + + 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 +494,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, @@ -475,13 +539,27 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None class_ir.glue_methods[(class_ir, name)] = f builder.functions.append(f) + if fdef.name == "__getattr__": + generate_getattr_wrapper(builder, cdef, fdef) + elif fdef.name == "__setattr__": + generate_setattr_wrapper(builder, cdef, fdef) + elif fdef.name == "__delattr__": + setattr = cdef.info.get("__setattr__") + if not setattr or not setattr.node or setattr.node.fullname.startswith("builtins."): + builder.error( + '"__delattr__" supported only in classes that also override "__setattr__", ' + + "or inherit from a native class that overrides it.", + fdef.line, + ) + def handle_non_ext_method( builder: IRBuilder, non_ext: NonExtClassInfo, cdef: ClassDef, fdef: FuncDef ) -> 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) @@ -505,32 +583,14 @@ def handle_non_ext_method( builder.add_to_non_ext_dict(non_ext, name, func_reg, fdef.line) - -def calculate_arg_defaults( - builder: IRBuilder, - fn_info: FuncInfo, - func_reg: Value | None, - symtable: dict[SymbolNode, SymbolTarget], -) -> None: - """Calculate default argument values and store them. - - They are stored in statics for top level functions and in - the function objects for nested functions (while constants are - still stored computed on demand). - """ - fitem = fn_info.fitem - for arg in fitem.arguments: - # Constant values don't get stored but just recomputed - if arg.initializer and not is_constant(arg.initializer): - value = builder.coerce( - builder.accept(arg.initializer), symtable[arg.variable].type, arg.line - ) - if not fn_info.is_nested: - name = fitem.fullname + "." + arg.variable.name - builder.add(InitStatic(value, name, builder.module_name)) - else: - assert func_reg is not None - builder.add(SetAttr(func_reg, arg.variable.name, value, arg.line)) + # If we identified that this non-extension class method can be special-cased for + # direct access during prepare phase, generate a "static" version of it. + class_ir = builder.mapper.type_to_ir[cdef.info] + name = FAST_PREFIX + fdef.name + if name in class_ir.method_decls: + func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef, make_ext_method=True) + class_ir.methods[name] = func_ir + builder.functions.append(func_ir) def gen_func_ns(builder: IRBuilder) -> str: @@ -559,7 +619,7 @@ def load_decorated_func(builder: IRBuilder, fdef: FuncDef, orig_func_reg: Value) func_reg = orig_func_reg for d in reversed(decorators): decorator = d.accept(builder.visitor) - assert isinstance(decorator, Value) + assert isinstance(decorator, Value), decorator func_reg = builder.py_call(decorator, [func_reg], func_reg.line) return func_reg @@ -797,13 +857,52 @@ def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget: return builder.add_local_reg(fdef, object_rprimitive) -def load_type(builder: IRBuilder, typ: TypeInfo, line: int) -> Value: +# This function still does not support the following imports. +# import json as _json +# from json import decoder +# Using either _json.JSONDecoder or decoder.JSONDecoder as a type hint for a dataclass field will fail. +# See issue mypyc/mypyc#1099. +def load_type(builder: IRBuilder, typ: TypeInfo, unbounded_type: Type | None, line: int) -> Value: + # typ.fullname contains the module where the class object was defined. However, it is possible + # that the class object's module was not imported in the file currently being compiled. So, we + # use unbounded_type.name (if provided by caller) to load the class object through one of the + # imported modules. + # Example: for `json.JSONDecoder`, typ.fullname is `json.decoder.JSONDecoder` but the Python + # file may import `json` not `json.decoder`. + # Another corner case: The Python file being compiled imports mod1 and has a type hint + # `mod1.OuterClass.InnerClass`. But, mod1/__init__.py might import OuterClass like this: + # `from mod2.mod3 import OuterClass`. In this case, typ.fullname is + # `mod2.mod3.OuterClass.InnerClass` and `unbounded_type.name` is `mod1.OuterClass.InnerClass`. + # So, we must use unbounded_type.name to load the class object. + # See issue mypyc/mypyc#1087. + load_attr_path = ( + unbounded_type.name if isinstance(unbounded_type, UnboundType) else typ.fullname + ).removesuffix(f".{typ.name}") if typ in builder.mapper.type_to_ir: class_ir = builder.mapper.type_to_ir[typ] class_obj = builder.builder.get_native_type(class_ir) elif typ.fullname in builtin_names: builtin_addr_type, src = builtin_names[typ.fullname] class_obj = builder.add(LoadAddress(builtin_addr_type, src, line)) + # This elif-condition finds the longest import that matches the load_attr_path. + elif module_name := max( + (i for i in builder.imports if load_attr_path == i or load_attr_path.startswith(f"{i}.")), + default="", + key=len, + ): + # Load the imported module. + loaded_module = builder.load_module(module_name) + # Recursively load attributes of the imported module. These may be submodules, classes or + # any other object. + for attr in ( + load_attr_path.removeprefix(f"{module_name}.").split(".") + if load_attr_path != module_name + else [] + ): + loaded_module = builder.py_get_attr(loaded_module, attr, line) + 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 +939,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 +952,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.call_c(exact_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 +1065,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 +1087,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 +1108,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 ) @@ -1033,8 +1128,8 @@ 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) + loaded_type = load_type(builder, typ, None, line) + builder.call_c(exact_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/generator.py b/mypyc/irbuild/generator.py index 92f9abff467c7..4dcd748f6effc 100644 --- a/mypyc/irbuild/generator.py +++ b/mypyc/irbuild/generator.py @@ -10,10 +10,12 @@ from __future__ import annotations -from mypy.nodes import ARG_OPT, Var -from mypyc.common import ENV_ATTR_NAME, NEXT_LABEL_ATTR_NAME, SELF_NAME +from typing import Callable + +from mypy.nodes import ARG_OPT, FuncDef, Var +from mypyc.common import ENV_ATTR_NAME, GENERATOR_ATTRIBUTE_PREFIX, NEXT_LABEL_ATTR_NAME from mypyc.ir.class_ir import ClassIR -from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg +from mypyc.ir.func_ir import FuncDecl, FuncIR from mypyc.ir.ops import ( NO_TRACEBACK_LINE_NO, BasicBlock, @@ -30,16 +32,25 @@ Unreachable, Value, ) -from mypyc.ir.rtypes import RInstance, int_rprimitive, object_rprimitive -from mypyc.irbuild.builder import IRBuilder, gen_arg_defaults -from mypyc.irbuild.context import FuncInfo, GeneratorClass +from mypyc.ir.rtypes import ( + RInstance, + int32_rprimitive, + object_pointer_rprimitive, + object_rprimitive, +) +from mypyc.irbuild.builder import IRBuilder, calculate_arg_defaults, gen_arg_defaults +from mypyc.irbuild.context import FuncInfo from mypyc.irbuild.env_class import ( add_args_to_env, + add_vars_to_env, finalize_env_class, load_env_registers, load_outer_env, + load_outer_envs, + setup_func_for_recursive_call, ) from mypyc.irbuild.nonlocalcontrol import ExceptNonlocalControl +from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME from mypyc.primitives.exc_ops import ( error_catch_op, exc_matches_op, @@ -49,46 +60,114 @@ ) -def gen_generator_func(builder: IRBuilder) -> None: +def gen_generator_func( + builder: IRBuilder, + gen_func_ir: Callable[ + [list[Register], list[BasicBlock], FuncInfo], tuple[FuncIR, Value | None] + ], +) -> tuple[FuncIR, Value | None]: + """Generate IR for generator function that returns generator object.""" setup_generator_class(builder) - load_env_registers(builder) + load_env_registers(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX) gen_arg_defaults(builder) - finalize_env_class(builder) - builder.add(Return(instantiate_generator_class(builder))) + if builder.fn_info.can_merge_generator_and_env_classes(): + gen = instantiate_generator_class(builder) + builder.fn_info._curr_env_reg = gen + finalize_env_class(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX) + else: + finalize_env_class(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX) + gen = instantiate_generator_class(builder) + builder.add(Return(gen)) + + args, _, blocks, ret_type, fn_info = builder.leave() + func_ir, func_reg = gen_func_ir(args, blocks, fn_info) + return func_ir, func_reg + + +def gen_generator_func_body(builder: IRBuilder, fn_info: FuncInfo, func_reg: Value | None) -> None: + """Generate IR based on the body of a generator function. + + Add "__next__", "__iter__" and other generator methods to the generator + class that implements the function (each function gets a separate class). + + Return the symbol table for the body. + """ + builder.enter(fn_info, ret_type=object_rprimitive) + setup_env_for_generator_class(builder) + + load_outer_envs(builder, builder.fn_info.generator_class) + top_level = builder.top_level_fn_info() + fitem = fn_info.fitem + if ( + builder.fn_info.is_nested + and isinstance(fitem, FuncDef) + and top_level + and top_level.add_nested_funcs_to_env + ): + setup_func_for_recursive_call( + builder, fitem, builder.fn_info.generator_class, prefix=GENERATOR_ATTRIBUTE_PREFIX + ) + create_switch_for_generator_class(builder) + add_raise_exception_blocks_to_generator_class(builder, fitem.line) + + add_vars_to_env(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX) + + builder.accept(fitem.body) + builder.maybe_add_implicit_return() + + populate_switch_for_generator_class(builder) + + # Hang on to the local symbol table, since the caller will use it + # to calculate argument defaults. + symtable = builder.symtables[-1] + + args, _, blocks, ret_type, fn_info = builder.leave() + + add_methods_to_generator_class(builder, fn_info, args, blocks, fitem.is_coroutine) + + # Evaluate argument defaults in the surrounding scope, since we + # calculate them *once* when the function definition is evaluated. + calculate_arg_defaults(builder, fn_info, func_reg, symtable) def instantiate_generator_class(builder: IRBuilder) -> Value: fitem = builder.fn_info.fitem generator_reg = builder.add(Call(builder.fn_info.generator_class.ir.ctor, [], fitem.line)) - # Get the current environment register. If the current function is nested, then the - # generator class gets instantiated from the callable class' '__call__' method, and hence - # we use the callable class' environment register. Otherwise, we use the original - # function's environment register. - if builder.fn_info.is_nested: - curr_env_reg = builder.fn_info.callable_class.curr_env_reg + if builder.fn_info.can_merge_generator_and_env_classes(): + # Set the generator instance to the initial state (zero). + zero = Integer(0) + builder.add(SetAttr(generator_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line)) else: - curr_env_reg = builder.fn_info.curr_env_reg - - # Set the generator class' environment attribute to point at the environment class - # defined in the current scope. - builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line)) - - # Set the generator class' environment class' NEXT_LABEL_ATTR_NAME attribute to 0. - zero = Integer(0) - builder.add(SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line)) + # Get the current environment register. If the current function is nested, then the + # generator class gets instantiated from the callable class' '__call__' method, and hence + # we use the callable class' environment register. Otherwise, we use the original + # function's environment register. + if builder.fn_info.is_nested: + curr_env_reg = builder.fn_info.callable_class.curr_env_reg + else: + curr_env_reg = builder.fn_info.curr_env_reg + + # Set the generator class' environment attribute to point at the environment class + # defined in the current scope. + builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line)) + + # Set the generator instance's environment to the initial state (zero). + zero = Integer(0) + builder.add(SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line)) return generator_reg def setup_generator_class(builder: IRBuilder) -> ClassIR: - name = f"{builder.fn_info.namespaced_name()}_gen" - - generator_class_ir = ClassIR(name, builder.module_name, is_generated=True) - generator_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_info.env_class) - generator_class_ir.mro = [generator_class_ir] + mapper = builder.mapper + assert isinstance(builder.fn_info.fitem, FuncDef), builder.fn_info.fitem + generator_class_ir = mapper.fdef_to_generator[builder.fn_info.fitem] + if builder.fn_info.can_merge_generator_and_env_classes(): + builder.fn_info.env_class = generator_class_ir + else: + generator_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_info.env_class) builder.classes.append(generator_class_ir) - builder.fn_info.generator_class = GeneratorClass(generator_class_ir) return generator_class_ir @@ -140,47 +219,32 @@ def add_raise_exception_blocks_to_generator_class(builder: IRBuilder, line: int) def add_methods_to_generator_class( builder: IRBuilder, fn_info: FuncInfo, - sig: FuncSignature, arg_regs: list[Register], blocks: list[BasicBlock], is_coroutine: bool, ) -> None: - helper_fn_decl = add_helper_to_generator_class(builder, arg_regs, blocks, sig, fn_info) - add_next_to_generator_class(builder, fn_info, helper_fn_decl, sig) - add_send_to_generator_class(builder, fn_info, helper_fn_decl, sig) + helper_fn_decl = add_helper_to_generator_class(builder, arg_regs, blocks, fn_info) + add_next_to_generator_class(builder, fn_info, helper_fn_decl) + add_send_to_generator_class(builder, fn_info, helper_fn_decl) add_iter_to_generator_class(builder, fn_info) - add_throw_to_generator_class(builder, fn_info, helper_fn_decl, sig) + add_throw_to_generator_class(builder, fn_info, helper_fn_decl) add_close_to_generator_class(builder, fn_info) if is_coroutine: add_await_to_generator_class(builder, fn_info) def add_helper_to_generator_class( - builder: IRBuilder, - arg_regs: list[Register], - blocks: list[BasicBlock], - sig: FuncSignature, - fn_info: FuncInfo, + builder: IRBuilder, arg_regs: list[Register], blocks: list[BasicBlock], fn_info: FuncInfo ) -> FuncDecl: """Generates a helper method for a generator class, called by '__next__' and 'throw'.""" - sig = FuncSignature( - ( - RuntimeArg(SELF_NAME, object_rprimitive), - RuntimeArg("type", object_rprimitive), - RuntimeArg("value", object_rprimitive), - RuntimeArg("traceback", object_rprimitive), - RuntimeArg("arg", object_rprimitive), - ), - sig.ret_type, - ) - helper_fn_decl = FuncDecl( - "__mypyc_generator_helper__", fn_info.generator_class.ir.name, builder.module_name, sig - ) + helper_fn_decl = fn_info.generator_class.ir.method_decls[GENERATOR_HELPER_NAME] helper_fn_ir = FuncIR( helper_fn_decl, arg_regs, blocks, fn_info.fitem.line, traceback_name=fn_info.fitem.name ) - fn_info.generator_class.ir.methods["__mypyc_generator_helper__"] = helper_fn_ir + fn_info.generator_class.ir.methods[GENERATOR_HELPER_NAME] = helper_fn_ir builder.functions.append(helper_fn_ir) + fn_info.env_class.env_user_function = helper_fn_ir + return helper_fn_decl @@ -190,9 +254,7 @@ def add_iter_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: builder.add(Return(builder.self())) -def add_next_to_generator_class( - builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature -) -> None: +def add_next_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl) -> None: """Generates the '__next__' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, "__next__", object_rprimitive, fn_info): none_reg = builder.none_object() @@ -200,16 +262,21 @@ def add_next_to_generator_class( result = builder.add( Call( fn_decl, - [builder.self(), none_reg, none_reg, none_reg, none_reg], + [ + builder.self(), + none_reg, + none_reg, + none_reg, + none_reg, + Integer(0, object_pointer_rprimitive), + ], fn_info.fitem.line, ) ) builder.add(Return(result)) -def add_send_to_generator_class( - builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature -) -> None: +def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl) -> None: """Generates the 'send' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, "send", object_rprimitive, fn_info): arg = builder.add_argument("arg", object_rprimitive) @@ -218,16 +285,21 @@ def add_send_to_generator_class( result = builder.add( Call( fn_decl, - [builder.self(), none_reg, none_reg, none_reg, builder.read(arg)], + [ + builder.self(), + none_reg, + none_reg, + none_reg, + builder.read(arg), + Integer(0, object_pointer_rprimitive), + ], fn_info.fitem.line, ) ) builder.add(Return(result)) -def add_throw_to_generator_class( - builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature -) -> None: +def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl) -> None: """Generates the 'throw' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, "throw", object_rprimitive, fn_info): typ = builder.add_argument("type", object_rprimitive) @@ -245,7 +317,14 @@ def add_throw_to_generator_class( result = builder.add( Call( fn_decl, - [builder.self(), builder.read(typ), builder.read(val), builder.read(tb), none_reg], + [ + builder.self(), + builder.read(typ), + builder.read(val), + builder.read(tb), + none_reg, + Integer(0, object_pointer_rprimitive), + ], fn_info.fitem.line, ) ) @@ -325,22 +404,34 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None: # TODO: Use the right type here instead of object? exc_arg = builder.add_local(Var("arg"), object_rprimitive, is_arg=True) + # Parameter that can used to pass a pointer which can used instead of + # raising StopIteration(value). If the value is NULL, this won't be used. + stop_iter_value_arg = builder.add_local( + Var("stop_iter_ptr"), object_pointer_rprimitive, is_arg=True + ) + cls.exc_regs = (exc_type, exc_val, exc_tb) cls.send_arg_reg = exc_arg + cls.stop_iter_value_reg = stop_iter_value_arg cls.self_reg = builder.read(self_target, fitem.line) - cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.symtables[-1]) + if builder.fn_info.can_merge_generator_and_env_classes(): + cls.curr_env_reg = cls.self_reg + else: + cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.symtables[-1]) # Define a variable representing the label to go to the next time # the '__next__' function of the generator is called, and add it # as an attribute to the environment class. cls.next_label_target = builder.add_var_to_env_class( - Var(NEXT_LABEL_ATTR_NAME), int_rprimitive, cls, reassign=False + Var(NEXT_LABEL_ATTR_NAME), int32_rprimitive, cls, reassign=False, always_defined=True ) # Add arguments from the original generator function to the # environment of the generator class. - add_args_to_env(builder, local=False, base=cls, reassign=False) + add_args_to_env( + builder, local=False, base=cls, reassign=False, prefix=GENERATOR_ATTRIBUTE_PREFIX + ) # Set the next label register for the generator class. cls.next_label_reg = builder.read(cls.next_label_target, fitem.line) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 0c9310e6a5cac..fd66288dbcc57 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -1,31 +1,30 @@ """A "low-level" IR builder class. -LowLevelIRBuilder provides core abstractions we use for constructing -IR as well as a number of higher-level ones (accessing attributes, -calling functions and methods, and coercing between types, for -example). The core principle of the low-level IR builder is that all -of its facilities operate solely on the IR level and not the AST -level---it has *no knowledge* of mypy types or expressions. +See the docstring of class LowLevelIRBuilder for more information. + """ from __future__ import annotations -from typing import Callable, Final, Optional, Sequence, Tuple +import sys +from collections.abc import Sequence +from typing import Callable, Final, Optional, cast +from typing_extensions import TypeGuard 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, FAST_ISINSTANCE_MAX_SUBCLASSES, + FAST_PREFIX, + IS_FREE_THREADED, MAX_LITERAL_SHORT_INT, MAX_SHORT_INT, MIN_LITERAL_SHORT_INT, MIN_SHORT_INT, PLATFORM_SIZE, - use_method_vectorcall, - use_vectorcall, ) from mypyc.errors import Errors from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -58,6 +57,7 @@ KeepAlive, LoadAddress, LoadErrorValue, + LoadGlobal, LoadLiteral, LoadMem, LoadStatic, @@ -95,28 +95,28 @@ c_pyssize_t_rprimitive, c_size_t_rprimitive, check_native_int_range, - dict_rprimitive, float_rprimitive, int_rprimitive, - is_bit_rprimitive, - is_bool_rprimitive, + is_bool_or_bit_rprimitive, is_bytes_rprimitive, is_dict_rprimitive, is_fixed_width_rtype, is_float_rprimitive, + is_frozenset_rprimitive, is_int16_rprimitive, is_int32_rprimitive, is_int64_rprimitive, is_int_rprimitive, is_list_rprimitive, is_none_rprimitive, + is_object_rprimitive, + is_optional_type, is_set_rprimitive, is_short_int_rprimitive, is_str_rprimitive, is_tagged, is_tuple_rprimitive, is_uint8_rprimitive, - list_rprimitive, none_rprimitive, object_pointer_rprimitive, object_rprimitive, @@ -130,6 +130,8 @@ from mypyc.primitives.bytes_ops import bytes_compare from mypyc.primitives.dict_ops import ( dict_build_op, + dict_copy, + dict_copy_op, dict_new_op, dict_ssize_t_size_op, dict_update_in_display_op, @@ -141,6 +143,7 @@ generic_ssize_t_len_op, py_call_op, py_call_with_kwargs_op, + py_call_with_posargs_op, py_getattr_op, py_method_call_op, py_vectorcall_method_op, @@ -165,8 +168,11 @@ from mypyc.primitives.misc_ops import ( bool_op, buf_init_item, + debug_print_op, fast_isinstance_op, none_object_op, + not_implemented_op, + set_immortal_op, var_object_size, ) from mypyc.primitives.registry import ( @@ -177,13 +183,25 @@ unary_ops, ) from mypyc.primitives.set_ops import new_set_op -from mypyc.primitives.str_ops import str_check_if_true, str_ssize_t_size_op, unicode_compare -from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op, new_tuple_with_length_op +from mypyc.primitives.str_ops import ( + str_check_if_true, + str_eq, + str_eq_literal, + str_ssize_t_size_op, + unicode_compare, +) +from mypyc.primitives.tuple_ops import ( + list_tuple_op, + load_empty_tuple_constant_op, + new_tuple_op, + new_tuple_with_length_op, + sequence_tuple_op, +) from mypyc.rt_subtype import is_runtime_subtype from mypyc.sametype import is_same_type from mypyc.subtype import is_subtype -DictEntry = Tuple[Optional[Value], Value] +DictEntry = tuple[Optional[Value], Value] # If the number of items is less than the threshold when initializing # a list, we would inline the generate IR using SetMem and expanded @@ -223,6 +241,22 @@ class LowLevelIRBuilder: + """A "low-level" IR builder class. + + LowLevelIRBuilder provides core abstractions we use for constructing + IR as well as a number of higher-level ones (accessing attributes, + calling functions and methods, and coercing between types, for + example). + + The core principle of the low-level IR builder is that all of its + facilities operate solely on the mypyc IR level and not the mypy AST + level---it has *no knowledge* of mypy types or expressions. + + The mypyc.irbuilder.builder.IRBuilder class wraps an instance of this + class and provides additional functionality to transform mypy AST nodes + to IR. + """ + def __init__(self, errors: Errors | None, options: CompilerOptions) -> None: self.errors = errors self.options = options @@ -268,6 +302,9 @@ def goto_and_activate(self, block: BasicBlock) -> None: def keep_alive(self, values: list[Value], *, steal: bool = False) -> None: self.add(KeepAlive(values, steal=steal)) + def load_mem(self, ptr: Value, value_type: RType, *, borrow: bool = False) -> Value: + return self.add(LoadMem(value_type, ptr, borrow=borrow)) + def push_error_handler(self, handler: BasicBlock | None) -> None: self.error_handlers.append(handler) @@ -286,6 +323,11 @@ def flush_keep_alives(self) -> None: self.add(KeepAlive(self.keep_alives.copy())) self.keep_alives = [] + def debug_print(self, toprint: str | Value) -> None: + if isinstance(toprint, str): + toprint = self.load_str(toprint) + self.primitive_op(debug_print_op, [toprint], -1) + # Type conversions def box(self, src: Value) -> Value: @@ -297,14 +339,20 @@ def box(self, src: Value) -> Value: return src def unbox_or_cast( - self, src: Value, target_type: RType, line: int, *, can_borrow: bool = False + self, + src: Value, + target_type: RType, + line: int, + *, + can_borrow: bool = False, + unchecked: bool = False, ) -> Value: if target_type.is_unboxed: return self.add(Unbox(src, target_type, line)) else: if can_borrow: self.keep_alives.append(src) - return self.add(Cast(src, target_type, line, borrow=can_borrow)) + return self.add(Cast(src, target_type, line, borrow=can_borrow, unchecked=unchecked)) def coerce( self, @@ -357,16 +405,12 @@ def coerce( ): # Equivalent types return src - elif (is_bool_rprimitive(src_type) or is_bit_rprimitive(src_type)) and is_tagged( - target_type - ): + elif is_bool_or_bit_rprimitive(src_type) and is_tagged(target_type): shifted = self.int_op( bool_rprimitive, src, Integer(1, bool_rprimitive), IntOp.LEFT_SHIFT ) return self.add(Extend(shifted, target_type, signed=False)) - elif ( - is_bool_rprimitive(src_type) or is_bit_rprimitive(src_type) - ) and is_fixed_width_rtype(target_type): + elif is_bool_or_bit_rprimitive(src_type) and is_fixed_width_rtype(target_type): return self.add(Extend(src, target_type, signed=False)) elif isinstance(src, Integer) and is_float_rprimitive(target_type): if is_tagged(src_type): @@ -413,7 +457,7 @@ def coerce( def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) -> Value: assert is_fixed_width_rtype(target_type), target_type - assert isinstance(target_type, RPrimitive) + assert isinstance(target_type, RPrimitive), target_type res = Register(target_type) @@ -426,7 +470,7 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - size = target_type.size if size < int_rprimitive.size: - # Add a range check when the target type is smaller than the source tyoe + # Add a range check when the target type is smaller than the source type fast2, fast3 = BasicBlock(), BasicBlock() upper_bound = 1 << (size * 8 - 1) if not target_type.is_signed: @@ -496,10 +540,12 @@ def coerce_int_to_fixed_width(self, src: Value, target_type: RType, line: int) - return res def coerce_short_int_to_fixed_width(self, src: Value, target_type: RType, line: int) -> Value: - if is_int64_rprimitive(target_type): + if is_int64_rprimitive(target_type) or ( + PLATFORM_SIZE == 4 and is_int32_rprimitive(target_type) + ): return self.int_op(target_type, src, Integer(1, target_type), IntOp.RIGHT_SHIFT, line) - # TODO: i32 - assert False, (src.type, target_type) + # TODO: i32 on 64-bit platform + assert False, (src.type, target_type, PLATFORM_SIZE) def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value: if ( @@ -517,10 +563,11 @@ def coerce_fixed_width_to_int(self, src: Value, line: int) -> Value: line, ) - assert is_fixed_width_rtype(src.type) - assert isinstance(src.type, RPrimitive) src_type = src.type + assert is_fixed_width_rtype(src_type), src_type + assert isinstance(src_type, RPrimitive), src_type + res = Register(int_rprimitive) fast, fast2, slow, end = BasicBlock(), BasicBlock(), BasicBlock(), BasicBlock() @@ -619,7 +666,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 @@ -638,7 +685,7 @@ def other() -> Value: def get_type_of_obj(self, obj: Value, line: int) -> Value: ob_type_address = self.add(GetElementPtr(obj, PyObject, "ob_type", line)) - ob_type = self.add(LoadMem(object_rprimitive, ob_type_address)) + ob_type = self.load_mem(ob_type_address, object_rprimitive, borrow=True) self.add(KeepAlive([obj])) return ob_type @@ -655,7 +702,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() @@ -758,10 +807,52 @@ def _construct_varargs( for value, kind, name in args: if kind == ARG_STAR: if star_result is None: + # star args fastpath + if len(args) == 1: + # fn(*args) + if is_list_rprimitive(value.type): + value = self.primitive_op(list_tuple_op, [value], line) + elif not is_tuple_rprimitive(value.type) and not isinstance( + value.type, RTuple + ): + value = self.primitive_op(sequence_tuple_op, [value], line) + return value, None + elif len(args) == 2 and args[1][1] == ARG_STAR2: + # fn(*args, **kwargs) + # TODO: extend to cover(*args, **k, **w, **a, **r, **g, **s) + if is_tuple_rprimitive(value.type) or isinstance(value.type, RTuple): + star_result = value + elif is_list_rprimitive(value.type): + star_result = self.primitive_op(list_tuple_op, [value], line) + else: + star_result = self.primitive_op(sequence_tuple_op, [value], line) + + star2_arg = args[1] + star2_value = star2_arg[0] + if is_dict_rprimitive(star2_value.type): + star2_fastpath_op = dict_copy_op + else: + star2_fastpath_op = dict_copy + return star_result, self.primitive_op( + star2_fastpath_op, [star2_value], line + ) + # elif ...: TODO extend this to optimize fn(*args, k=1, **kwargs) case + # TODO optimize this case using the length utils - currently in review 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: + if len(args) == 1: + # early exit with fastpath if the only arg is ARG_STAR2 + # TODO: can we maintain an empty tuple in memory and just reuse it again and again? + if is_dict_rprimitive(value.type): + star2_fastpath_op = dict_copy_op + else: + star2_fastpath_op = dict_copy + return self.new_tuple([], line), self.primitive_op( + star2_fastpath_op, [value], line + ) + star2_result = self._create_dict(star2_keys, star2_values, line) self.call_c(dict_update_in_display_op, [star2_result, value], line=line) @@ -855,9 +946,11 @@ def _construct_varargs( # tuple. Otherwise create the tuple from the list. 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) - if has_star2 and star2_result is None: + elif not is_tuple_rprimitive(star_result.type): + # if star_result is a tuple we took the fast path + star_result = self.primitive_op(list_tuple_op, [star_result], line) + if has_star2 and star2_result is None and len(star2_keys) > 0: + # TODO: use dict_copy_op for simple cases of **kwargs star2_result = self._create_dict(star2_keys, star2_values, line) return star_result, star2_result @@ -874,23 +967,24 @@ def py_call( Use py_call_op or py_call_with_kwargs_op for Python function call. """ - if use_vectorcall(self.options.capi_version): - # More recent Python versions support faster vectorcalls. - result = self._py_vector_call(function, arg_values, line, arg_kinds, arg_names) - if result is not None: - return result + result = self._py_vector_call(function, arg_values, line, arg_kinds, arg_names) + if result is not None: + return result # If all arguments are positional, we can use py_call_op. if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds): return self.call_c(py_call_op, [function] + arg_values, line) - # Otherwise fallback to py_call_with_kwargs_op. + # Otherwise fallback to py_call_with_posargs_op or py_call_with_kwargs_op. assert arg_names is not None pos_args_tuple, kw_args_dict = self._construct_varargs( list(zip(arg_values, arg_kinds, arg_names)), line, has_star=True, has_star2=True ) - assert pos_args_tuple and kw_args_dict + assert pos_args_tuple + + if kw_args_dict is None: + return self.call_c(py_call_with_posargs_op, [function, pos_args_tuple], line) return self.call_c(py_call_with_kwargs_op, [function, pos_args_tuple, kw_args_dict], line) @@ -953,13 +1047,11 @@ def py_method_call( arg_names: Sequence[str | None] | None, ) -> Value: """Call a Python method (non-native and slow).""" - if use_method_vectorcall(self.options.capi_version): - # More recent Python versions support faster vectorcalls. - result = self._py_vector_method_call( - obj, method_name, arg_values, line, arg_kinds, arg_names - ) - if result is not None: - return result + result = self._py_vector_method_call( + obj, method_name, arg_values, line, arg_kinds, arg_names + ) + if result is not None: + return result if arg_kinds is None or all(kind == ARG_POS for kind in arg_kinds): # Use legacy method call API @@ -1091,8 +1183,7 @@ def native_args_to_positional( assert star_arg output_arg = star_arg elif arg.kind == ARG_STAR2: - assert star2_arg - output_arg = star2_arg + output_arg = star2_arg or self._create_dict([], [], line) elif not lst: if is_fixed_width_rtype(arg.type): output_arg = Integer(0, arg.type) @@ -1142,14 +1233,16 @@ def gen_method_call( """Generate either a native or Python method call.""" # If we have *args, then fallback to Python method call. if arg_kinds is not None and any(kind.is_star() for kind in arg_kinds): - return self.py_method_call(base, name, arg_values, base.line, arg_kinds, arg_names) + return self.py_method_call(base, name, arg_values, line, arg_kinds, arg_names) # If the base type is one of ours, do a MethodCall + fast_name = FAST_PREFIX + name if ( isinstance(base.type, RInstance) - and base.type.class_ir.is_ext_class + and (base.type.class_ir.is_ext_class or base.type.class_ir.has_method(fast_name)) and not base.type.class_ir.builtin_base ): + name = name if base.type.class_ir.is_ext_class else fast_name if base.type.class_ir.has_method(name): decl = base.type.class_ir.method_decl(name) if arg_kinds is None: @@ -1228,6 +1321,14 @@ def none_object(self) -> Value: """Load Python None value (type: object_rprimitive).""" return self.add(LoadAddress(none_object_op.type, none_object_op.src, line=-1)) + def true_object(self) -> Value: + """Load Python True object (type: object_rprimitive).""" + return self.add(LoadGlobal(object_rprimitive, "Py_True")) + + def false_object(self) -> Value: + """Load Python False object (type: object_rprimitive).""" + return self.add(LoadGlobal(object_rprimitive, "Py_False")) + def load_int(self, value: int) -> Value: """Load a tagged (Python) integer literal value.""" if value > MAX_LITERAL_SHORT_INT or value < MIN_LITERAL_SHORT_INT: @@ -1311,13 +1412,11 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: # Special case various ops if op in ("is", "is not"): return self.translate_is_op(lreg, rreg, op, line) - # TODO: modify 'str' to use same interface as 'compare_bytes' as it avoids - # call to PyErr_Occurred() - if is_str_rprimitive(ltype) and is_str_rprimitive(rtype) and op in ("==", "!="): - return self.compare_strings(lreg, rreg, op, line) - if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype) and op in ("==", "!="): - return self.compare_bytes(lreg, rreg, op, line) - if is_bool_rprimitive(ltype) and is_bool_rprimitive(rtype) and op in BOOL_BINARY_OPS: + if ( + is_bool_or_bit_rprimitive(ltype) + and is_bool_or_bit_rprimitive(rtype) + and op in BOOL_BINARY_OPS + ): if op in ComparisonOp.signed_ops: return self.bool_comparison_op(lreg, rreg, op, line) else: @@ -1326,13 +1425,12 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: return self.translate_instance_contains(rreg, lreg, op, line) if is_fixed_width_rtype(ltype): if op in FIXED_WIDTH_INT_BINARY_OPS: - if op.endswith("="): - op = op[:-1] + op = op.removesuffix("=") if op != "//": op_id = int_op_to_id[op] else: op_id = IntOp.DIV - if is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype): + if is_bool_or_bit_rprimitive(rtype): rreg = self.coerce(rreg, ltype, line) rtype = ltype if is_fixed_width_rtype(rtype) or is_tagged(rtype): @@ -1344,7 +1442,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: elif op in ComparisonOp.signed_ops: if is_int_rprimitive(rtype): rreg = self.coerce_int_to_fixed_width(rreg, ltype, line) - elif is_bool_rprimitive(rtype) or is_bit_rprimitive(rtype): + elif is_bool_or_bit_rprimitive(rtype): rreg = self.coerce(rreg, ltype, line) op_id = ComparisonOp.signed_ops[op] if is_fixed_width_rtype(rreg.type): @@ -1353,8 +1451,7 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: return self.comparison_op(lreg, self.coerce(rreg, ltype, line), op_id, line) elif is_fixed_width_rtype(rtype): if op in FIXED_WIDTH_INT_BINARY_OPS: - if op.endswith("="): - op = op[:-1] + op = op.removesuffix("=") if op != "//": op_id = int_op_to_id[op] else: @@ -1365,13 +1462,13 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: ) if is_tagged(ltype): return self.fixed_width_int_op(rtype, lreg, rreg, op_id, line) - if is_bool_rprimitive(ltype) or is_bit_rprimitive(ltype): + if is_bool_or_bit_rprimitive(ltype): lreg = self.coerce(lreg, rtype, line) return self.fixed_width_int_op(rtype, lreg, rreg, op_id, line) elif op in ComparisonOp.signed_ops: if is_int_rprimitive(ltype): lreg = self.coerce_int_to_fixed_width(lreg, rtype, line) - elif is_bool_rprimitive(ltype) or is_bit_rprimitive(ltype): + elif is_bool_or_bit_rprimitive(ltype): lreg = self.coerce(lreg, rtype, line) op_id = ComparisonOp.signed_ops[op] if isinstance(lreg, Integer): @@ -1398,11 +1495,49 @@ 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. @@ -1417,6 +1552,39 @@ def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) - def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two strings""" + if op == "==": + # We can specialize this case if one or both values are string literals + literal_fastpath = False + + def is_string_literal(value: Value) -> TypeGuard[LoadLiteral]: + return isinstance(value, LoadLiteral) and is_str_rprimitive(value.type) + + if is_string_literal(lhs): + if is_string_literal(rhs): + # we can optimize out the check entirely in some constant-folded cases + return self.true() if lhs.value == rhs.value else self.false() + + # if lhs argument is string literal, switch sides to match specializer C api + lhs, rhs = rhs, lhs + literal_fastpath = True + elif is_string_literal(rhs): + literal_fastpath = True + + if literal_fastpath: + literal_string = cast(str, cast(LoadLiteral, rhs).value) + literal_length = Integer(len(literal_string), c_pyssize_t_rprimitive, line) + return self.primitive_op(str_eq_literal, [lhs, rhs, literal_length], line) + + return self.primitive_op(str_eq, [lhs, rhs], line) + + elif op == "!=": + # perform a standard equality check, then negate + eq = self.compare_strings(lhs, rhs, "==", line) + return self.add(ComparisonOp(eq, self.false(), ComparisonOp.EQ, line)) + + # TODO: modify 'str' to use same interface as 'compare_bytes' as it would avoid + # call to PyErr_Occurred() below + compare_result = self.call_c(unicode_compare, [lhs, rhs], line) error_constant = Integer(-1, c_int_rprimitive, line) compare_error_check = self.add( @@ -1450,9 +1618,13 @@ def compare_bytes(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Value: """Compare two tuples item by item""" # type cast to pass mypy check - assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple) + assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple), (lhs.type, rhs.type) equal = True if op == "==" else False result = Register(bool_rprimitive) + # tuples of different lengths + if len(lhs.type.types) != len(rhs.type.types): + self.add(Assign(result, self.false() if equal else self.true(), line)) + return result # empty tuples if len(lhs.type.types) == 0 and len(rhs.type.types) == 0: self.add(Assign(result, self.true() if equal else self.false(), line)) @@ -1476,8 +1648,8 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val compare = self.binary_op(lhs_item, rhs_item, op, line) # 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) + if not is_bool_or_bit_rprimitive(compare.type): + 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: @@ -1495,8 +1667,8 @@ 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) + if not is_bool_or_bit_rprimitive(res.type): + 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 @@ -1516,62 +1688,140 @@ def bool_comparison_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Va op_id = ComparisonOp.signed_ops[op] return self.comparison_op(lreg, rreg, op_id, line) - def unary_not(self, value: Value, line: int) -> Value: - mask = Integer(1, value.type, line) - return self.int_op(value.type, value, mask, IntOp.XOR, line) + def _non_specialized_unary_op(self, value: Value, op: str, line: int) -> Value: + if isinstance(value.type, RInstance): + result = self.dunder_op(value, None, op, line) + if result is not None: + return result + primitive_ops_candidates = unary_ops.get(op, []) + target = self.matching_primitive_op(primitive_ops_candidates, [value], line) + assert target, "Unsupported unary operation: %s" % op + return target - def unary_op(self, value: Value, expr_op: str, line: int) -> Value: - typ = value.type - if is_bool_rprimitive(typ) or is_bit_rprimitive(typ): - if expr_op == "not": - return self.unary_not(value, line) - if expr_op == "+": - return value - if is_fixed_width_rtype(typ): - if expr_op == "-": - # Translate to '0 - x' - return self.int_op(typ, Integer(0, typ), value, IntOp.SUB, line) - elif expr_op == "~": - if typ.is_signed: - # Translate to 'x ^ -1' - return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line) - else: - # Translate to 'x ^ 0xff...' - mask = (1 << (typ.size * 8)) - 1 - return self.int_op(typ, value, Integer(mask, typ), IntOp.XOR, line) - elif expr_op == "+": - return value - if is_float_rprimitive(typ): - if expr_op == "-": - return self.add(FloatNeg(value, line)) - elif expr_op == "+": - return value + def unary_not(self, value: Value, line: int, *, likely_bool: bool = False) -> Value: + """Perform unary 'not'. + Args: + likely_bool: The operand is likely a bool value, even if the type is something + more general, so specialize for bool values + """ + typ = value.type + if is_bool_or_bit_rprimitive(typ): + mask = Integer(1, typ, line) + return self.int_op(typ, value, mask, IntOp.XOR, line) + if is_tagged(typ) or is_fixed_width_rtype(typ): + return self.binary_op(value, Integer(0), "==", line) + if ( + is_str_rprimitive(typ) + or is_list_rprimitive(typ) + or is_tuple_rprimitive(typ) + or is_dict_rprimitive(typ) + or isinstance(typ, RInstance) + ): + bool_val = self.bool_value(value) + return self.unary_not(bool_val, line) + if is_optional_type(typ): + value_typ = optional_value_type(typ) + assert value_typ + if ( + is_str_rprimitive(value_typ) + or is_list_rprimitive(value_typ) + or is_tuple_rprimitive(value_typ) + or is_dict_rprimitive(value_typ) + or isinstance(value_typ, RInstance) + ): + # 'X | None' type: Check for None first and then specialize for X. + res = Register(bit_rprimitive) + cmp = self.add(ComparisonOp(value, self.none_object(), ComparisonOp.EQ, line)) + none, not_none, out = BasicBlock(), BasicBlock(), BasicBlock() + self.add(Branch(cmp, none, not_none, Branch.BOOL)) + self.activate_block(none) + self.add(Assign(res, self.true())) + self.goto(out) + self.activate_block(not_none) + val = self.unary_not( + self.unbox_or_cast(value, value_typ, line, can_borrow=True, unchecked=True), + line, + ) + self.add(Assign(res, val)) + self.goto(out) + self.activate_block(out) + return res + if likely_bool and is_object_rprimitive(typ): + # First quickly check if it's a bool, and otherwise fall back to generic op. + res = Register(bit_rprimitive) + false, not_false, true, other = BasicBlock(), BasicBlock(), BasicBlock(), BasicBlock() + out = BasicBlock() + cmp = self.add(ComparisonOp(value, self.true_object(), ComparisonOp.EQ, line)) + self.add(Branch(cmp, false, not_false, Branch.BOOL)) + self.activate_block(false) + self.add(Assign(res, self.false())) + self.goto(out) + self.activate_block(not_false) + cmp = self.add(ComparisonOp(value, self.false_object(), ComparisonOp.EQ, line)) + self.add(Branch(cmp, true, other, Branch.BOOL)) + self.activate_block(true) + self.add(Assign(res, self.true())) + self.goto(out) + self.activate_block(other) + val = self._non_specialized_unary_op(value, "not", line) + self.add(Assign(res, val)) + self.goto(out) + self.activate_block(out) + return res + return self._non_specialized_unary_op(value, "not", line) + + def unary_minus(self, value: Value, line: int) -> Value: + """Perform unary '-'.""" + typ = value.type if isinstance(value, Integer): # TODO: Overflow? Unsigned? - num = value.value - if is_short_int_rprimitive(typ): - num >>= 1 - return Integer(-num, typ, value.line) - if is_tagged(typ) and expr_op == "+": + return Integer(-value.numeric_value(), typ, line) + elif isinstance(value, Float): + return Float(-value.value, line) + elif is_fixed_width_rtype(typ): + # Translate to '0 - x' + return self.int_op(typ, Integer(0, typ), value, IntOp.SUB, line) + elif is_float_rprimitive(typ): + return self.add(FloatNeg(value, line)) + return self._non_specialized_unary_op(value, "-", line) + + def unary_plus(self, value: Value, line: int) -> Value: + """Perform unary '+'.""" + typ = value.type + if ( + is_tagged(typ) + or is_float_rprimitive(typ) + or is_bool_or_bit_rprimitive(typ) + or is_fixed_width_rtype(typ) + ): return 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__" + return self._non_specialized_unary_op(value, "+", line) + + def unary_invert(self, value: Value, line: int) -> Value: + """Perform unary '~'.""" + typ = value.type + if is_fixed_width_rtype(typ): + if typ.is_signed: + # Translate to 'x ^ -1' + return self.int_op(typ, value, Integer(-1, typ), IntOp.XOR, line) 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) - assert target, "Unsupported unary operation: %s" % expr_op - return target + # Translate to 'x ^ 0xff...' + mask = (1 << (typ.size * 8)) - 1 + return self.int_op(typ, value, Integer(mask, typ), IntOp.XOR, line) + return self._non_specialized_unary_op(value, "~", line) + + def unary_op(self, value: Value, op: str, line: int) -> Value: + """Perform a unary operation.""" + if op == "not": + return self.unary_not(value, line) + elif op == "-": + return self.unary_minus(value, line) + elif op == "+": + return self.unary_plus(value, line) + elif op == "~": + return self.unary_invert(value, line) + raise RuntimeError("Unsupported unary operation: %s" % op) def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value: result: Value | None = None @@ -1636,7 +1886,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 @@ -1687,7 +1937,7 @@ def bool_value(self, value: Value) -> Value: The result type can be bit_rprimitive or bool_rprimitive. """ - if is_bool_rprimitive(value.type) or is_bit_rprimitive(value.type): + if is_bool_or_bit_rprimitive(value.type): result = value elif is_runtime_subtype(value.type, int_rprimitive): zero = Integer(0, short_int_rprimitive) @@ -1695,10 +1945,12 @@ def bool_value(self, value: Value) -> Value: elif is_fixed_width_rtype(value.type): zero = Integer(0, value.type) result = self.add(ComparisonOp(value, zero, ComparisonOp.NEQ)) - elif is_same_type(value.type, str_rprimitive): + elif is_str_rprimitive(value.type): result = self.call_c(str_check_if_true, [value], value.line) - elif is_same_type(value.type, list_rprimitive) or is_same_type( - value.type, dict_rprimitive + elif ( + is_list_rprimitive(value.type) + or is_dict_rprimitive(value.type) + or is_tuple_rprimitive(value.type) ): length = self.builtin_len(value, value.line) zero = Integer(0) @@ -1744,7 +1996,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: @@ -1822,6 +2074,8 @@ def call_c( line, var_arg_idx, is_pure=desc.is_pure, + returns_null=desc.returns_null, + capsule=desc.capsule, ) ) if desc.is_borrowed: @@ -1889,7 +2143,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, @@ -1905,10 +2159,12 @@ def primitive_op( desc.extra_int_constants, desc.priority, is_pure=desc.is_pure, + returns_null=False, + capsule=desc.capsule, ) - 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 = [] @@ -1958,6 +2214,8 @@ def matching_primitive_op( for desc in candidates: if len(desc.arg_types) != len(args): continue + if desc.experimental and not self.options.experimental_features: + continue if all( # formal is not None and # TODO is_subtype(actual.type, formal) @@ -1972,7 +2230,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 +2292,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) @@ -2043,6 +2301,33 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value: def compare_floats(self, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.add(FloatComparisonOp(lhs, rhs, op, line)) + def int_add(self, lhs: Value, rhs: Value | int) -> Value: + """Helper to add two native integers. + + The result has the type of lhs. + """ + if isinstance(rhs, int): + rhs = Integer(rhs, lhs.type) + return self.int_op(lhs.type, lhs, rhs, IntOp.ADD, line=-1) + + def int_sub(self, lhs: Value, rhs: Value | int) -> Value: + """Helper to subtract a native integer from another one. + + The result has the type of lhs. + """ + if isinstance(rhs, int): + rhs = Integer(rhs, lhs.type) + return self.int_op(lhs.type, lhs, rhs, IntOp.SUB, line=-1) + + def int_mul(self, lhs: Value, rhs: Value | int) -> Value: + """Helper to multiply two native integers. + + The result has the type of lhs. + """ + if isinstance(rhs, int): + rhs = Integer(rhs, lhs.type) + return self.int_op(lhs.type, lhs, rhs, IntOp.MUL, line=-1) + def fixed_width_int_op( self, type: RPrimitive, lhs: Value, rhs: Value, op: int, line: int ) -> Value: @@ -2171,9 +2456,9 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val size_value = None if is_list_rprimitive(typ) or is_tuple_rprimitive(typ) or is_bytes_rprimitive(typ): size_value = self.primitive_op(var_object_size, [val], line) - elif is_set_rprimitive(typ): + elif is_set_rprimitive(typ) or is_frozenset_rprimitive(typ): elem_address = self.add(GetElementPtr(val, PySetObject, "used")) - size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + size_value = self.load_mem(elem_address, c_pyssize_t_rprimitive) self.add(KeepAlive([val])) elif is_dict_rprimitive(typ): size_value = self.call_c(dict_ssize_t_size_op, [val], line) @@ -2211,8 +2496,11 @@ def builtin_len(self, val: Value, line: int, use_pyssize_t: bool = False) -> Val return self.call_c(generic_len_op, [val], line) def new_tuple(self, items: list[Value], line: int) -> Value: - size: Value = Integer(len(items), c_pyssize_t_rprimitive) - return self.call_c(new_tuple_op, [size] + items, line) + if items: + size: Value = Integer(len(items), c_pyssize_t_rprimitive) + return self.call_c(new_tuple_op, [size] + items, line) + else: + return self.call_c(load_empty_tuple_constant_op, [], line) def new_tuple_with_length(self, length: Value, line: int) -> Value: """This function returns an uninitialized tuple. @@ -2229,7 +2517,12 @@ 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) + + def set_immortal_if_free_threaded(self, v: Value, line: int) -> None: + """Make an object immortal on free-threaded builds (to avoid contention).""" + if IS_FREE_THREADED and sys.version_info >= (3, 14): + self.primitive_op(set_immortal_op, [v], line) # Internal helpers @@ -2313,20 +2606,44 @@ 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. + """Add an equality comparison operation. + + Note that this doesn't cover all possible types. Args: expr_op: either '==' or '!=' """ ltype = lreg.type rtype = rreg.type + + if is_str_rprimitive(ltype) and is_str_rprimitive(rtype): + return self.compare_strings(lreg, rreg, expr_op, line) + if is_bytes_rprimitive(ltype) and is_bytes_rprimitive(rtype): + return self.compare_bytes(lreg, rreg, expr_op, line) + + lopt = optional_value_type(ltype) + ropt = optional_value_type(rtype) + + # Can we do a quick comparison of two optional types (special case None values)? + fast_opt_eq = False + if lopt is not None: + if ropt is not None and is_same_type(lopt, ropt) and self._never_equal_to_none(lopt): + fast_opt_eq = True + if is_same_type(lopt, rtype) and self._never_equal_to_none(lopt): + fast_opt_eq = True + elif ropt is not None: + if is_same_type(ropt, ltype) and self._never_equal_to_none(ropt): + fast_opt_eq = True + if fast_opt_eq: + return self._translate_fast_optional_eq_cmp(lreg, rreg, expr_op, line) + if not (isinstance(ltype, RInstance) and ltype == rtype): return None @@ -2353,6 +2670,76 @@ def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> return self.gen_method_call(lreg, op_methods[expr_op], [rreg], ltype, line) + def _never_equal_to_none(self, typ: RType) -> bool: + """Are the values of type never equal to None?""" + # TODO: Support RInstance with no custom __eq__/__ne__ and other primitive types. + return is_str_rprimitive(typ) or is_bytes_rprimitive(typ) + + def _translate_fast_optional_eq_cmp( + self, lreg: Value, rreg: Value, expr_op: str, line: int + ) -> Value: + """Generate eq/ne fast path between 'X | None' and ('X | None' or X). + + Assume 'X' never compares equal to None. + """ + if not isinstance(lreg.type, RUnion): + lreg, rreg = rreg, lreg + value_typ = optional_value_type(lreg.type) + assert value_typ + res = Register(bool_rprimitive) + + # Fast path: left value is None? + cmp = self.add(ComparisonOp(lreg, self.none_object(), ComparisonOp.EQ, line)) + l_none = BasicBlock() + l_not_none = BasicBlock() + out = BasicBlock() + self.add(Branch(cmp, l_none, l_not_none, Branch.BOOL)) + self.activate_block(l_none) + if not isinstance(rreg.type, RUnion): + val = self.false() if expr_op == "==" else self.true() + self.add(Assign(res, val)) + else: + op = ComparisonOp.EQ if expr_op == "==" else ComparisonOp.NEQ + cmp = self.add(ComparisonOp(rreg, self.none_object(), op, line)) + self.add(Assign(res, cmp)) + self.goto(out) + + self.activate_block(l_not_none) + if not isinstance(rreg.type, RUnion): + # Both operands are known to be not None, perform specialized comparison + eq = self.translate_eq_cmp( + self.unbox_or_cast(lreg, value_typ, line, can_borrow=True, unchecked=True), + rreg, + expr_op, + line, + ) + assert eq is not None + self.add(Assign(res, eq)) + else: + r_none = BasicBlock() + r_not_none = BasicBlock() + # Fast path: right value is None? + cmp = self.add(ComparisonOp(rreg, self.none_object(), ComparisonOp.EQ, line)) + self.add(Branch(cmp, r_none, r_not_none, Branch.BOOL)) + self.activate_block(r_none) + # None vs not-None + val = self.false() if expr_op == "==" else self.true() + self.add(Assign(res, val)) + self.goto(out) + self.activate_block(r_not_none) + # Both operands are known to be not None, perform specialized comparison + eq = self.translate_eq_cmp( + self.unbox_or_cast(lreg, value_typ, line, can_borrow=True, unchecked=True), + self.unbox_or_cast(rreg, value_typ, line, can_borrow=True, unchecked=True), + expr_op, + line, + ) + assert eq is not None + self.add(Assign(res, eq)) + self.goto(out) + self.activate_block(out) + return res + def translate_is_op(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value: """Create equality comparison operation between object identities diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py index 15928d939cbf6..f08911a1bc4c9 100644 --- a/mypyc/irbuild/main.py +++ b/mypyc/irbuild/main.py @@ -25,7 +25,7 @@ def f(x: int) -> int: from typing import Any, Callable, TypeVar, cast from mypy.build import Graph -from mypy.nodes import ClassDef, Expression, MypyFile +from mypy.nodes import ClassDef, Expression, FuncDef, MypyFile from mypy.state import state from mypy.types import Type from mypyc.analysis.attrdefined import analyze_always_defined_attrs @@ -37,7 +37,12 @@ def f(x: int) -> int: from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.prebuildvisitor import PreBuildVisitor -from mypyc.irbuild.prepare import build_type_map, find_singledispatch_register_impls +from mypyc.irbuild.prepare import ( + adjust_generator_classes_of_methods, + build_type_map, + create_generator_class_for_func, + find_singledispatch_register_impls, +) from mypyc.irbuild.visitor import IRBuilderVisitor from mypyc.irbuild.vtable import compute_vtable from mypyc.options import CompilerOptions @@ -64,18 +69,31 @@ def build_ir( """ build_type_map(mapper, modules, graph, types, options, errors) + adjust_generator_classes_of_methods(mapper) singledispatch_info = find_singledispatch_register_impls(modules, errors) result: ModuleIRs = {} + if errors.num_errors > 0: + return result # Generate IR for all modules. class_irs = [] for module in modules: # First pass to determine free symbols. - pbv = PreBuildVisitor(errors, module, singledispatch_info.decorators_to_remove) + pbv = PreBuildVisitor(errors, module, singledispatch_info.decorators_to_remove, types) module.accept(pbv) + # Declare generator classes for nested async functions and generators. + for fdef in pbv.nested_funcs: + if isinstance(fdef, FuncDef): + # Make generator class name sufficiently unique. + suffix = f"___{fdef.line}" + if fdef.is_coroutine or fdef.is_generator: + create_generator_class_for_func( + module.fullname, None, fdef, mapper, name_suffix=suffix + ) + # Construct and configure builder objects (cyclic runtime dependency). visitor = IRBuilderVisitor() builder = IRBuilder( diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 90ce0e16c7418..c986499b6f65e 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -25,6 +25,7 @@ from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncDecl, FuncSignature, RuntimeArg from mypyc.ir.rtypes import ( + KNOWN_NATIVE_TYPES, RInstance, RTuple, RType, @@ -33,6 +34,7 @@ bytes_rprimitive, dict_rprimitive, float_rprimitive, + frozenset_rprimitive, int16_rprimitive, int32_rprimitive, int64_rprimitive, @@ -62,6 +64,9 @@ 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() + # The corresponding generator class that implements a generator/async function + self.fdef_to_generator: dict[FuncDef, ClassIR] = {} def type_to_rtype(self, typ: Type | None) -> RType: if typ is None: @@ -69,6 +74,10 @@ def type_to_rtype(self, typ: Type | None) -> RType: typ = get_proper_type(typ) if isinstance(typ, Instance): + if typ.type.is_newtype: + # Unwrap NewType to its base type for rprimitive mapping + assert len(typ.type.bases) == 1, typ.type.bases + return self.type_to_rtype(typ.type.bases[0]) if typ.type.fullname == "builtins.int": return int_rprimitive elif typ.type.fullname == "builtins.float": @@ -88,6 +97,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return dict_rprimitive elif typ.type.fullname == "builtins.set": return set_rprimitive + elif typ.type.fullname == "builtins.frozenset": + return frozenset_rprimitive elif typ.type.fullname == "builtins.tuple": return tuple_rprimitive # Varying-length tuple elif typ.type.fullname == "builtins.range": @@ -109,6 +120,8 @@ def type_to_rtype(self, typ: Type | None) -> RType: return int16_rprimitive elif typ.type.fullname == "mypy_extensions.u8": return uint8_rprimitive + elif typ.type.fullname in KNOWN_NATIVE_TYPES: + return KNOWN_NATIVE_TYPES[typ.type.fullname] else: return object_rprimitive elif isinstance(typ, TupleType): @@ -160,7 +173,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 +212,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 +230,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..c2ca9cfd32ff7 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -1,5 +1,7 @@ +from __future__ import annotations + +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator, List, Optional, Tuple from mypy.nodes import MatchStmt, NameExpr, TypeInfo from mypy.patterns import ( @@ -14,7 +16,7 @@ ValuePattern, ) from mypy.traverser import TraverserVisitor -from mypy.types import Instance, TupleType, get_proper_type +from mypy.types import Instance, LiteralType, TupleType, get_proper_type from mypyc.ir.ops import BasicBlock, Value from mypyc.ir.rtypes import object_rprimitive from mypyc.irbuild.builder import IRBuilder @@ -56,7 +58,7 @@ class MatchVisitor(TraverserVisitor): subject: Value match: MatchStmt - as_pattern: Optional[AsPattern] = None + as_pattern: AsPattern | None = None def __init__(self, builder: IRBuilder, match_node: MatchStmt) -> None: self.builder = builder @@ -124,14 +126,14 @@ def visit_or_pattern(self, pattern: OrPattern) -> None: def visit_class_pattern(self, pattern: ClassPattern) -> None: # TODO: use faster instance check for native classes (while still - # making sure to account for inheritence) + # making sure to account for inheritance) isinstance_op = ( fast_isinstance_op if self.builder.is_builtin_ref_expr(pattern.class_ref) 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 ) @@ -149,24 +151,8 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None: return node = pattern.class_ref.node - assert isinstance(node, TypeInfo) - - ty = node.names.get("__match_args__") - assert ty - - match_args_type = get_proper_type(ty.type) - assert isinstance(match_args_type, TupleType) - - match_args: List[str] = [] - - for item in match_args_type.items: - proper_item = get_proper_type(item) - assert isinstance(proper_item, Instance) and proper_item.last_known_value - - match_arg = proper_item.last_known_value.value - assert isinstance(match_arg, str) - - match_args.append(match_arg) + assert isinstance(node, TypeInfo), node + match_args = extract_dunder_match_args_names(node) for i, expr in enumerate(pattern.positionals): self.builder.activate_block(self.code_block) @@ -220,7 +206,7 @@ def visit_mapping_pattern(self, pattern: MappingPattern) -> None: self.builder.add_bool_branch(is_dict, self.code_block, self.next_block) - keys: List[Value] = [] + keys: list[Value] = [] for key, value in zip(pattern.keys, pattern.values): self.builder.activate_block(self.code_block) @@ -246,7 +232,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) @@ -330,7 +316,7 @@ def bind_as_pattern(self, value: Value, new_block: bool = False) -> None: self.builder.goto(self.code_block) @contextmanager - def enter_subpattern(self, subject: Value) -> Generator[None, None, None]: + def enter_subpattern(self, subject: Value) -> Generator[None]: old_subject = self.subject self.subject = subject yield @@ -339,10 +325,10 @@ def enter_subpattern(self, subject: Value) -> Generator[None, None, None]: def prep_sequence_pattern( seq_pattern: SequencePattern, -) -> Tuple[Optional[int], Optional[NameExpr], List[Pattern]]: - star_index: Optional[int] = None - capture: Optional[NameExpr] = None - patterns: List[Pattern] = [] +) -> tuple[int | None, NameExpr | None, list[Pattern]]: + star_index: int | None = None + capture: NameExpr | None = None + patterns: list[Pattern] = [] for i, pattern in enumerate(seq_pattern.patterns): if isinstance(pattern, StarredPattern): @@ -353,3 +339,24 @@ def prep_sequence_pattern( patterns.append(pattern) return star_index, capture, patterns + + +def extract_dunder_match_args_names(info: TypeInfo) -> list[str]: + ty = info.names.get("__match_args__") + assert ty + match_args_type = get_proper_type(ty.type) + assert isinstance(match_args_type, TupleType), match_args_type + + match_args: list[str] = [] + for item in match_args_type.items: + proper_item = get_proper_type(item) + + match_arg = None + if isinstance(proper_item, Instance) and proper_item.last_known_value: + match_arg = proper_item.last_known_value.value + elif isinstance(proper_item, LiteralType): + match_arg = proper_item.value + assert isinstance(match_arg, str), f"Unrecognized __match_args__ item: {item}" + + match_args.append(match_arg) + return match_args diff --git a/mypyc/irbuild/missingtypevisitor.py b/mypyc/irbuild/missingtypevisitor.py new file mode 100644 index 0000000000000..e655d270a4a4c --- /dev/null +++ b/mypyc/irbuild/missingtypevisitor.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from mypy.nodes import Expression, Node +from mypy.traverser import ExtendedTraverserVisitor +from mypy.types import AnyType, Type, TypeOfAny + + +class MissingTypesVisitor(ExtendedTraverserVisitor): + """AST visitor that can be used to add any missing types as a generic AnyType.""" + + def __init__(self, types: dict[Expression, Type]) -> None: + super().__init__() + self.types: dict[Expression, Type] = types + + def visit(self, o: Node) -> bool: + if isinstance(o, Expression) and o not in self.types: + self.types[o] = AnyType(TypeOfAny.special_form) + + # If returns True, will continue to nested nodes. + return True diff --git a/mypyc/irbuild/nonlocalcontrol.py b/mypyc/irbuild/nonlocalcontrol.py index 0ac9bd3cee31b..4a7136fbd18d5 100644 --- a/mypyc/irbuild/nonlocalcontrol.py +++ b/mypyc/irbuild/nonlocalcontrol.py @@ -16,9 +16,11 @@ Integer, Register, Return, + SetMem, Unreachable, Value, ) +from mypyc.ir.rtypes import object_rprimitive from mypyc.irbuild.targets import AssignmentTarget from mypyc.primitives.exc_ops import restore_exc_info_op, set_stop_iteration_value @@ -108,10 +110,27 @@ def gen_return(self, builder: IRBuilder, value: Value, line: int) -> None: # StopIteration instead of using RaiseStandardError because # the obvious thing doesn't work if the value is a tuple # (???). + + true, false = BasicBlock(), BasicBlock() + stop_iter_reg = builder.fn_info.generator_class.stop_iter_value_reg + assert stop_iter_reg is not None + + builder.add(Branch(stop_iter_reg, true, false, Branch.IS_ERROR)) + + builder.activate_block(true) + # The default/slow path is to raise a StopIteration exception with + # return value. builder.call_c(set_stop_iteration_value, [value], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.builder.pop_error_handler() + builder.activate_block(false) + # The fast path is to store return value via caller-provided pointer + # instead of raising an exception. This can only be used when the + # caller is a native function. + builder.add(SetMem(object_rprimitive, stop_iter_reg, value)) + builder.add(Return(Integer(0, object_rprimitive))) + class CleanupNonlocalControl(NonlocalControl): """Abstract nonlocal control that runs some cleanup code.""" @@ -156,7 +175,7 @@ def gen_return(self, builder: IRBuilder, value: Value, line: int) -> None: self.ret_reg = Register(builder.ret_types[-1]) # assert needed because of apparent mypy bug... it loses track of the union # and infers the type as object - assert isinstance(self.ret_reg, (Register, AssignmentTarget)) + assert isinstance(self.ret_reg, (Register, AssignmentTarget)), self.ret_reg builder.assign(self.ret_reg, value, line) builder.add(Goto(self.target)) diff --git a/mypyc/irbuild/prebuildvisitor.py b/mypyc/irbuild/prebuildvisitor.py index 17f907d42111f..e630fed0d85a7 100644 --- a/mypyc/irbuild/prebuildvisitor.py +++ b/mypyc/irbuild/prebuildvisitor.py @@ -1,6 +1,7 @@ from __future__ import annotations from mypy.nodes import ( + AssignmentStmt, Block, Decorator, Expression, @@ -16,7 +17,9 @@ Var, ) from mypy.traverser import ExtendedTraverserVisitor +from mypy.types import Type from mypyc.errors import Errors +from mypyc.irbuild.missingtypevisitor import MissingTypesVisitor class PreBuildVisitor(ExtendedTraverserVisitor): @@ -39,6 +42,7 @@ def __init__( errors: Errors, current_file: MypyFile, decorators_to_remove: dict[FuncDef, list[int]], + types: dict[Expression, Type], ) -> None: super().__init__() # Dict from a function to symbols defined directly in the @@ -73,7 +77,7 @@ def __init__( self.decorators_to_remove: dict[FuncDef, list[int]] = decorators_to_remove # A mapping of import groups (a series of Import nodes with - # nothing inbetween) where each group is keyed by its first + # nothing in between) where each group is keyed by its first # import node. self.module_import_groups: dict[Import, list[Import]] = {} self._current_import_group: Import | None = None @@ -82,11 +86,20 @@ def __init__( self.current_file: MypyFile = current_file + self.missing_types_visitor = MissingTypesVisitor(types) + def visit(self, o: Node) -> bool: if not isinstance(o, Import): self._current_import_group = None return True + def visit_assignment_stmt(self, stmt: AssignmentStmt) -> None: + # These are cases where mypy may not have types for certain expressions, + # but mypyc needs some form type to exist. + if stmt.is_alias_def: + stmt.rvalue.accept(self.missing_types_visitor) + return super().visit_assignment_stmt(stmt) + def visit_block(self, block: Block) -> None: self._current_import_group = None super().visit_block(block) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 29e06439abdd6..9f3c7fc6f2707 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -14,7 +14,8 @@ from __future__ import annotations from collections import defaultdict -from typing import Iterable, NamedTuple, Tuple +from collections.abc import Iterable +from typing import Final, NamedTuple from mypy.build import Graph from mypy.nodes import ( @@ -37,7 +38,7 @@ from mypy.semanal import refers_to_fullname from mypy.traverser import TraverserVisitor from mypy.types import Instance, Type, get_proper_type -from mypyc.common import PROPSET_PREFIX, get_id_from_name +from mypyc.common import FAST_PREFIX, PROPSET_PREFIX, SELF_NAME, get_id_from_name from mypyc.crash import catch_errors from mypyc.errors import Errors from mypyc.ir.class_ir import ClassIR @@ -50,7 +51,15 @@ RuntimeArg, ) from mypyc.ir.ops import DeserMaps -from mypyc.ir.rtypes import RInstance, RType, dict_rprimitive, none_rprimitive, tuple_rprimitive +from mypyc.ir.rtypes import ( + RInstance, + RType, + dict_rprimitive, + none_rprimitive, + object_pointer_rprimitive, + object_rprimitive, + tuple_rprimitive, +) from mypyc.irbuild.mapper import Mapper from mypyc.irbuild.util import ( get_func_def, @@ -62,6 +71,8 @@ from mypyc.options import CompilerOptions from mypyc.sametype import is_same_type +GENERATOR_HELPER_NAME: Final = "__mypyc_generator_helper__" + def build_type_map( mapper: Mapper, @@ -81,23 +92,31 @@ 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) + class_ir.is_ext_class = is_extension_class(module.path, cdef, errors) if class_ir.is_ext_class: class_ir.deletable = cdef.info.deletable_attributes.copy() # If global optimizations are disabled, turn of tracking of class children if not options.global_opts: class_ir.children = None mapper.type_to_ir[cdef.info] = class_ir + mapper.symbol_fullnames.add(class_ir.fullname) + class_ir.is_enum = cdef.info.is_enum and len(cdef.info.enum_members) > 0 # 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: @@ -107,10 +126,10 @@ def build_type_map( # Collect all the functions also. We collect from the symbol table # so that we can easily pick out the right copy of a function that - # is conditionally defined. + # is conditionally defined. This doesn't include nested functions! 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 @@ -140,9 +159,16 @@ def load_type_map(mapper: Mapper, modules: list[MypyFile], deser_ctx: DeserMaps) """Populate a Mapper with deserialized IR from a list of modules.""" for module in modules: for node in module.names.values(): - if isinstance(node.node, TypeInfo) and is_from_module(node.node, module): + if ( + isinstance(node.node, TypeInfo) + and is_from_module(node.node, module) + and not node.node.is_newtype + and not node.node.is_named_tuple + and node.node.typeddict_type is None + ): 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 +190,80 @@ 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) + FUNC_CLASSMETHOD + if fdef.is_class + else (FUNC_STATICMETHOD if fdef.is_static else FUNC_NORMAL) + ) + sig = mapper.fdef_to_sig(fdef, options.strict_dunders_typing) + decl = FuncDecl( + fdef.name, + class_name, + module_name, + sig, + kind, + is_generator=fdef.is_generator, + is_coroutine=fdef.is_coroutine, ) - decl = FuncDecl(fdef.name, class_name, module_name, mapper.fdef_to_sig(fdef), kind) mapper.func_to_decl[fdef] = decl return decl +def create_generator_class_for_func( + module_name: str, class_name: str | None, fdef: FuncDef, mapper: Mapper, name_suffix: str = "" +) -> ClassIR: + """For a generator/async function, declare a generator class. + + Each generator and async function gets a dedicated class that implements the + generator protocol with generated methods. + """ + assert fdef.is_coroutine or fdef.is_generator + name = "_".join(x for x in [fdef.name, class_name] if x) + "_gen" + name_suffix + cir = ClassIR(name, module_name, is_generated=True, is_final_class=class_name is None) + cir.reuse_freed_instance = True + mapper.fdef_to_generator[fdef] = cir + + helper_sig = FuncSignature( + ( + RuntimeArg(SELF_NAME, object_rprimitive), + RuntimeArg("type", object_rprimitive), + RuntimeArg("value", object_rprimitive), + RuntimeArg("traceback", object_rprimitive), + RuntimeArg("arg", object_rprimitive), + # If non-NULL, used to store return value instead of raising StopIteration(retv) + RuntimeArg("stop_iter_ptr", object_pointer_rprimitive), + ), + object_rprimitive, + ) + + # The implementation of most generator functionality is behind this magic method. + helper_fn_decl = FuncDecl(GENERATOR_HELPER_NAME, name, module_name, helper_sig, internal=True) + cir.method_decls[helper_fn_decl.name] = helper_fn_decl + return cir + + 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": @@ -203,6 +282,36 @@ def prepare_method_def( ir.property_types[node.name] = decl.sig.ret_type +def prepare_fast_path( + ir: ClassIR, + module_name: str, + cdef: ClassDef, + mapper: Mapper, + node: SymbolNode | None, + options: CompilerOptions, +) -> None: + """Add fast (direct) variants of methods in non-extension classes.""" + if ir.is_enum: + # We check that non-empty enums are implicitly final in mypy, so we + # can generate direct calls to enum methods. + if isinstance(node, OverloadedFuncDef): + if node.is_property: + return + node = node.impl + if not isinstance(node, FuncDef): + # TODO: support decorated methods (at least @classmethod and @staticmethod). + return + # The simplest case is a regular or overloaded method without decorators. In this + # case we can generate practically identical IR method body, but with a signature + # suitable for direct calls (usual non-extension class methods are converted to + # callable classes, and thus have an extra __mypyc_self__ argument). + name = FAST_PREFIX + node.name + sig = mapper.fdef_to_sig(node, options.strict_dunders_typing) + decl = FuncDecl(name, cdef.name, module_name, sig, FUNC_NORMAL) + ir.method_decls[name] = decl + return + + def is_valid_multipart_property_def(prop: OverloadedFuncDef) -> bool: # Checks to ensure supported property decorator semantics if len(prop.items) != 2: @@ -237,7 +346,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. @@ -248,13 +362,29 @@ def prepare_class_def( ir = mapper.type_to_ir[cdef.info] info = cdef.info - attrs = get_mypyc_attrs(cdef) + attrs, attrs_lines = get_mypyc_attrs(cdef, path, errors) if attrs.get("allow_interpreted_subclasses") is True: ir.allow_interpreted_subclasses = True if attrs.get("serializable") is True: # Supports copy.copy and pickle (including subclasses) ir._serializable = True + free_list_len = attrs.get("free_list_len") + if free_list_len is not None: + line = attrs_lines["free_list_len"] + if ir.is_trait: + errors.error('"free_list_len" can\'t be used with traits', path, line) + if ir.allow_interpreted_subclasses: + errors.error( + '"free_list_len" can\'t be used in a class that allows interpreted subclasses', + path, + line, + ) + if free_list_len == 1: + ir.reuse_freed_instance = True + else: + errors.error(f'Unsupported value for "free_list_len": {free_list_len}', path, line) + # Check for subclassing from builtin types for cls in info.mro: # Special case exceptions and dicts @@ -272,6 +402,16 @@ def prepare_class_def( errors.error( "Inheriting from most builtin types is unimplemented", path, cdef.line ) + errors.note( + "Potential workaround: @mypy_extensions.mypyc_attr(native_class=False)", + path, + cdef.line, + ) + errors.note( + "https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes", + path, + cdef.line, + ) # Set up the parent class bases = [mapper.type_to_ir[base.type] for base in info.bases if base.type in mapper.type_to_ir] @@ -304,7 +444,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 +456,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 +484,24 @@ 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) + if not node.node.impl: + errors.error( + "Overloads without implementation are not supported", path, cdef.line + ) + else: + prepare_method_def(ir, module_name, cdef, mapper, node.node.impl, options) if ir.builtin_base: ir.attributes.clear() @@ -432,21 +582,57 @@ def add_setter_declaration( ir.method_decls[setter_name] = decl +def check_matching_args(init_sig: FuncSignature, new_sig: FuncSignature) -> bool: + num_init_args = len(init_sig.args) - init_sig.num_bitmap_args + num_new_args = len(new_sig.args) - new_sig.num_bitmap_args + if num_init_args != num_new_args: + return False + + for idx in range(1, num_init_args): + init_arg = init_sig.args[idx] + new_arg = new_sig.args[idx] + if init_arg.type != new_arg.type: + return False + + if init_arg.kind != new_arg.kind: + return False + + return True + + def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: Mapper) -> None: # Set up a constructor decl init_node = cdef.info["__init__"].node + + new_node: SymbolNode | None = None + new_symbol = cdef.info.get("__new__") + # We are only interested in __new__ method defined in a user-defined class, + # so we ignore it if it comes from a builtin type. It's usually builtins.object + # but could also be builtins.type for metaclasses so we detect the prefix which + # matches both. + if new_symbol and new_symbol.fullname and not new_symbol.fullname.startswith("builtins."): + new_node = new_symbol.node + if isinstance(new_node, (Decorator, OverloadedFuncDef)): + new_node = get_func_def(new_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) + args_match = True + if isinstance(new_node, FuncDef): + new_sig = mapper.fdef_to_sig(new_node, True) + args_match = check_matching_args(init_sig, new_sig) defining_ir = mapper.type_to_ir.get(init_node.info) # If there is a nontrivial __init__ that wasn't defined in an # extension class, we need to make the constructor take *args, # **kwargs so it can call tp_init. if ( - defining_ir is None - or not defining_ir.is_ext_class - or cdef.info["__init__"].plugin_generated - ) and init_node.info.fullname != "builtins.object": + ( + defining_ir is None + or not defining_ir.is_ext_class + or cdef.info["__init__"].plugin_generated + ) + and init_node.info.fullname != "builtins.object" + ) or not args_match: init_sig = FuncSignature( [ init_sig.args[0], @@ -463,24 +649,31 @@ 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) + + prepare_fast_path(ir, module_name, cdef, mapper, 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( @@ -488,7 +681,7 @@ def prepare_non_ext_class_def( ) -RegisterImplInfo = Tuple[TypeInfo, FuncDef] +RegisterImplInfo = tuple[TypeInfo, FuncDef] class SingledispatchInfo(NamedTuple): @@ -519,6 +712,12 @@ def __init__(self, errors: Errors) -> None: self.decorators_to_remove: dict[FuncDef, list[int]] = {} self.errors: Errors = errors + self.func_stack_depth = 0 + + def visit_func_def(self, o: FuncDef) -> None: + self.func_stack_depth += 1 + super().visit_func_def(o) + self.func_stack_depth -= 1 def visit_decorator(self, dec: Decorator) -> None: if dec.decorators: @@ -530,6 +729,10 @@ def visit_decorator(self, dec: Decorator) -> None: for i, d in enumerate(decorators_to_store): impl = get_singledispatch_register_call_info(d, dec.func) if impl is not None: + if self.func_stack_depth > 0: + self.errors.error( + "Registering nested functions not supported", self.current_path, d.line + ) self.singledispatch_impls[impl.singledispatch_func].append( (impl.dispatch_type, dec.func) ) @@ -546,6 +749,12 @@ def visit_decorator(self, dec: Decorator) -> None: ) else: if refers_to_fullname(d, "functools.singledispatch"): + if self.func_stack_depth > 0: + self.errors.error( + "Nested singledispatch functions not supported", + self.current_path, + d.line, + ) decorators_to_remove.append(i) # make sure that we still treat the function as a singledispatch function # even if we don't find any registered implementations (which might happen @@ -607,3 +816,82 @@ def registered_impl_from_possible_register_call( if isinstance(node, Decorator): return RegisteredImpl(node.func, dispatch_type) return None + + +def adjust_generator_classes_of_methods(mapper: Mapper) -> None: + """Make optimizations and adjustments to generated generator classes of methods. + + This is a separate pass after type map has been built, since we need all classes + to be processed to analyze class hierarchies. + """ + + generator_methods = [] + + for fdef, fn_ir in mapper.func_to_decl.items(): + if isinstance(fdef, FuncDef) and (fdef.is_coroutine or fdef.is_generator): + gen_ir = create_generator_class_for_func( + fn_ir.module_name, fn_ir.class_name, fdef, mapper + ) + # TODO: We could probably support decorators sometimes (static and class method?) + if not fdef.is_decorated: + name = fn_ir.name + precise_ret_type = True + if fn_ir.class_name is not None: + class_ir = mapper.type_to_ir[fdef.info] + subcls = class_ir.subclasses() + if subcls is None: + # Override could be of a different type, so we can't make assumptions. + precise_ret_type = False + elif class_ir.is_trait: + # Give up on traits. We could possibly have an abstract base class + # for generator return types to make this use precise types. + precise_ret_type = False + else: + for s in subcls: + if name in s.method_decls: + m = s.method_decls[name] + if ( + m.is_generator != fn_ir.is_generator + or m.is_coroutine != fn_ir.is_coroutine + ): + # Override is of a different kind, and the optimization + # to use a precise generator return type doesn't work. + precise_ret_type = False + else: + class_ir = None + + if precise_ret_type: + # Give a more precise type for generators, so that we can optimize + # code that uses them. They return a generator object, which has a + # specific class. Without this, the type would have to be 'object'. + fn_ir.sig.ret_type = RInstance(gen_ir) + if fn_ir.bound_sig: + fn_ir.bound_sig.ret_type = RInstance(gen_ir) + if class_ir is not None: + if class_ir.is_method_final(name): + gen_ir.is_final_class = True + generator_methods.append((name, class_ir, gen_ir)) + + new_bases = {} + + for name, class_ir, gen in generator_methods: + # For generator methods, we need to have subclass generator classes inherit from + # baseclass generator classes when there are overrides to maintain LSP. + base = class_ir.real_base() + if base is not None: + if base.has_method(name): + base_sig = base.method_sig(name) + if isinstance(base_sig.ret_type, RInstance): + base_gen = base_sig.ret_type.class_ir + new_bases[gen] = base_gen + + # Add generator inheritance relationships by adjusting MROs. + for deriv, base in new_bases.items(): + if base.children is not None: + base.children.append(deriv) + while True: + deriv.mro.append(base) + deriv.base_mro.append(base) + if base not in new_bases: + break + base = new_bases[base] diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 7c59584578866..b64f51043b138 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -14,7 +14,7 @@ from __future__ import annotations -from typing import Callable, Optional +from typing import Callable, Final, Optional, cast from mypy.nodes import ( ARG_NAMED, @@ -29,15 +29,20 @@ NameExpr, RefExpr, StrExpr, + SuperExpr, TupleExpr, + Var, ) from mypy.types import AnyType, TypeOfAny from mypyc.ir.ops import ( BasicBlock, + Call, Extend, Integer, + PrimitiveDescription, RaiseStandardError, Register, + SetAttr, Truncate, Unreachable, Value, @@ -48,6 +53,7 @@ RTuple, RType, bool_rprimitive, + bytes_rprimitive, c_int_rprimitive, dict_rprimitive, int16_rprimitive, @@ -65,11 +71,13 @@ is_list_rprimitive, is_uint8_rprimitive, list_rprimitive, + object_rprimitive, set_rprimitive, str_rprimitive, uint8_rprimitive, ) from mypyc.irbuild.builder import IRBuilder +from mypyc.irbuild.constant_fold import constant_fold_expr from mypyc.irbuild.for_helpers import ( comprehension_helper, sequence_from_generator_preallocate_helper, @@ -82,14 +90,30 @@ join_formatted_strings, tokenizer_format_call, ) +from mypyc.primitives.bytes_ops import isinstance_bytearray, isinstance_bytes from mypyc.primitives.dict_ops import ( dict_items_op, dict_keys_op, dict_setdefault_spec_init_op, dict_values_op, + isinstance_dict, ) -from mypyc.primitives.list_ops import new_list_set_item_op -from mypyc.primitives.tuple_ops import new_tuple_set_item_op +from mypyc.primitives.float_ops import isinstance_float +from mypyc.primitives.generic_ops import generic_setattr, setup_object +from mypyc.primitives.int_ops import isinstance_int +from mypyc.primitives.list_ops import isinstance_list, new_list_set_item_op +from mypyc.primitives.misc_ops import isinstance_bool +from mypyc.primitives.set_ops import isinstance_frozenset, isinstance_set +from mypyc.primitives.str_ops import ( + bytes_decode_ascii_strict, + bytes_decode_latin1_strict, + bytes_decode_utf8_strict, + isinstance_str, + str_encode_ascii_strict, + str_encode_latin1_strict, + str_encode_utf8_strict, +) +from mypyc.primitives.tuple_ops import isinstance_tuple, new_tuple_set_item_op # Specializers are attempted before compiling the arguments to the # function. Specializers can return None to indicate that they failed @@ -275,7 +299,7 @@ def translate_tuple_from_generator_call( """Special case for simplest tuple creation from a generator. For example: - tuple(f(x) for x in some_list/some_tuple/some_str) + tuple(f(x) for x in some_list/some_tuple/some_str/some_bytes) 'translate_safe_generator_call()' would take care of other cases if this fails. """ @@ -540,6 +564,21 @@ def gen_inner_stmts() -> None: return retval +isinstance_primitives: Final = { + "builtins.bool": isinstance_bool, + "builtins.bytearray": isinstance_bytearray, + "builtins.bytes": isinstance_bytes, + "builtins.dict": isinstance_dict, + "builtins.float": isinstance_float, + "builtins.frozenset": isinstance_frozenset, + "builtins.int": isinstance_int, + "builtins.list": isinstance_list, + "builtins.set": isinstance_set, + "builtins.str": isinstance_str, + "builtins.tuple": isinstance_tuple, +} + + @specialize_function("builtins.isinstance") def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: """Special case for builtins.isinstance. @@ -548,21 +587,84 @@ def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> there is no need to coerce something to a new type before checking what type it is, and the coercion could lead to bugs. """ - if ( - len(expr.args) == 2 - and expr.arg_kinds == [ARG_POS, ARG_POS] - and isinstance(expr.args[1], (RefExpr, TupleExpr)) - ): - builder.types[expr.args[0]] = AnyType(TypeOfAny.from_error) + if not (len(expr.args) == 2 and expr.arg_kinds == [ARG_POS, ARG_POS]): + return None + + obj_expr = expr.args[0] + type_expr = expr.args[1] + + if isinstance(type_expr, TupleExpr) and not type_expr.items: + # we can compile this case to a noop + return builder.false() - irs = builder.flatten_classes(expr.args[1]) + if isinstance(type_expr, (RefExpr, TupleExpr)): + builder.types[obj_expr] = AnyType(TypeOfAny.from_error) + + irs = builder.flatten_classes(type_expr) if irs is not None: can_borrow = all( ir.is_ext_class and not ir.inherits_python and not ir.allow_interpreted_subclasses for ir in irs ) - obj = builder.accept(expr.args[0], can_borrow=can_borrow) + obj = builder.accept(obj_expr, can_borrow=can_borrow) return builder.builder.isinstance_helper(obj, irs, expr.line) + + if isinstance(type_expr, RefExpr): + node = type_expr.node + if node: + desc = isinstance_primitives.get(node.fullname) + if desc: + obj = builder.accept(obj_expr) + return builder.primitive_op(desc, [obj], expr.line) + + elif isinstance(type_expr, TupleExpr): + node_names: list[str] = [] + for item in type_expr.items: + if not isinstance(item, RefExpr): + return None + if item.node is None: + return None + if item.node.fullname not in node_names: + node_names.append(item.node.fullname) + + descs = [isinstance_primitives.get(fullname) for fullname in node_names] + if None in descs: + # not all types are primitive types, abort + return None + + obj = builder.accept(obj_expr) + + retval = Register(bool_rprimitive) + pass_block = BasicBlock() + fail_block = BasicBlock() + exit_block = BasicBlock() + + # Chain the checks: if any succeed, jump to pass_block; else, continue + for i, desc in enumerate(descs): + is_last = i == len(descs) - 1 + next_block = fail_block if is_last else BasicBlock() + builder.add_bool_branch( + builder.primitive_op(cast(PrimitiveDescription, desc), [obj], expr.line), + pass_block, + next_block, + ) + if not is_last: + builder.activate_block(next_block) + + # If any check passed + builder.activate_block(pass_block) + builder.assign(retval, builder.true(), expr.line) + builder.goto(exit_block) + + # If all checks failed + builder.activate_block(fail_block) + builder.assign(retval, builder.false(), expr.line) + builder.goto(exit_block) + + # Return the result + builder.activate_block(exit_block) + return retval + return None @@ -614,21 +716,18 @@ def translate_dict_setdefault(builder: IRBuilder, expr: CallExpr, callee: RefExp @specialize_function("format", str_rprimitive) def translate_str_format(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: - if ( - isinstance(callee, MemberExpr) - and isinstance(callee.expr, StrExpr) - and expr.arg_kinds.count(ARG_POS) == len(expr.arg_kinds) - ): - format_str = callee.expr.value - tokens = tokenizer_format_call(format_str) - if tokens is None: - return None - literals, format_ops = tokens - # Convert variables to strings - substitutions = convert_format_expr_to_str(builder, format_ops, expr.args, expr.line) - if substitutions is None: - return None - return join_formatted_strings(builder, literals, substitutions, expr.line) + if isinstance(callee, MemberExpr): + folded_callee = constant_fold_expr(builder, callee.expr) + if isinstance(folded_callee, str) and expr.arg_kinds.count(ARG_POS) == len(expr.arg_kinds): + tokens = tokenizer_format_call(folded_callee) + if tokens is None: + return None + literals, format_ops = tokens + # Convert variables to strings + substitutions = convert_format_expr_to_str(builder, format_ops, expr.args, expr.line) + if substitutions is None: + return None + return join_formatted_strings(builder, literals, substitutions, expr.line) return None @@ -674,6 +773,24 @@ def translate_fstring(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va format_ops.append(FormatOp.STR) exprs.append(item.args[0]) + def get_literal_str(expr: Expression) -> str | None: + if isinstance(expr, StrExpr): + return expr.value + elif isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_final: + final_value = expr.node.final_value + if final_value is not None: + return str(final_value) + return None + + for i in range(len(exprs) - 1): + while ( + len(exprs) >= i + 2 + and (first := get_literal_str(exprs[i])) is not None + and (second := get_literal_str(exprs[i + 1])) is not None + ): + exprs = [*exprs[:i], StrExpr(first + second), *exprs[i + 2 :]] + format_ops = [*format_ops[:i], FormatOp.STR, *format_ops[i + 2 :]] + substitutions = convert_format_expr_to_str(builder, format_ops, exprs, expr.line) if substitutions is None: return None @@ -682,6 +799,119 @@ 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("decode", bytes_rprimitive) +def bytes_decode_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + """Specialize common cases of obj.decode 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("_", "-") # normalize + # Specialized encodings and their accepted aliases + if encoding in ["u8", "utf", "utf8", "utf-8", "cp65001"]: + return builder.call_c(bytes_decode_utf8_strict, [builder.accept(callee.expr)], expr.line) + elif encoding in ["646", "ascii", "usascii", "us-ascii"]: + return builder.call_c(bytes_decode_ascii_strict, [builder.accept(callee.expr)], expr.line) + elif encoding in [ + "iso8859-1", + "iso-8859-1", + "8859", + "cp819", + "latin", + "latin1", + "latin-1", + "l1", + ]: + return builder.call_c(bytes_decode_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 +1050,93 @@ 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 = constant_fold_expr(builder, expr.args[0]) + if isinstance(arg, (str, bytes)) and len(arg) == 1: + return Integer(ord(arg)) + return None + + +def is_object(callee: RefExpr) -> bool: + """Returns True for object. calls.""" + return ( + isinstance(callee, MemberExpr) + and isinstance(callee.expr, NameExpr) + and callee.expr.fullname == "builtins.object" + ) + + +def is_super_or_object(expr: CallExpr, callee: RefExpr) -> bool: + """Returns True for super(). or object. calls.""" + return isinstance(expr.callee, SuperExpr) or is_object(callee) + + +@specialize_function("__new__", object_rprimitive) +def translate_object_new(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + fn = builder.fn_info + if fn.name != "__new__" or not is_super_or_object(expr, callee): + return None + + ir = builder.get_current_class_ir() + if ir is None: + return None + + call = '"object.__new__()"' + if not ir.is_ext_class: + builder.error(f"{call} not supported for non-extension classes", expr.line) + return None + if ir.inherits_python: + builder.error( + f"{call} not supported for classes inheriting from non-native classes", expr.line + ) + return None + if len(expr.args) != 1: + builder.error(f"{call} supported only with 1 argument, got {len(expr.args)}", expr.line) + return None + + typ_arg = expr.args[0] + method_args = fn.fitem.arg_names + if isinstance(typ_arg, NameExpr) and len(method_args) > 0 and method_args[0] == typ_arg.name: + subtype = builder.accept(expr.args[0]) + subs = ir.subclasses() + if subs is not None and len(subs) == 0: + return builder.add(Call(ir.setup, [subtype], expr.line)) + # Call a function that dynamically resolves the setup function of extension classes from the type object. + # This is necessary because the setup involves default attribute initialization and setting up + # the vtable which are specific to a given type and will not work if a subtype is created using + # the setup function of its base. + return builder.call_c(setup_object, [subtype], expr.line) + + return None + + +@specialize_function("__setattr__", object_rprimitive) +def translate_object_setattr(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + is_super = isinstance(expr.callee, SuperExpr) + is_object_callee = is_object(callee) + if not ((is_super and len(expr.args) >= 2) or (is_object_callee and len(expr.args) >= 3)): + return None + + self_reg = builder.accept(expr.args[0]) if is_object_callee else builder.self() + ir = builder.get_current_class_ir() + if ir and (not ir.is_ext_class or ir.builtin_base or ir.inherits_python): + return None + # Need to offset by 1 for super().__setattr__ calls because there is no self arg in this case. + name_idx = 0 if is_super else 1 + value_idx = 1 if is_super else 2 + attr_name = expr.args[name_idx] + attr_value = expr.args[value_idx] + value = builder.accept(attr_value) + + if isinstance(attr_name, StrExpr) and ir and ir.has_attr(attr_name.value): + name = attr_name.value + value = builder.coerce(value, ir.attributes[name], expr.line) + return builder.add(SetAttr(self_reg, name, value, expr.line)) + + name_reg = builder.accept(attr_name) + return builder.call_c(generic_setattr, [self_reg, name_reg, value], expr.line) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 4d828b1b9d823..fdcf4f7771538 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -9,8 +9,10 @@ from __future__ import annotations import importlib.util -from typing import Callable, Sequence +from collections.abc import Sequence +from typing import Callable +import mypy.nodes from mypy.nodes import ( ARG_NAMED, ARG_POS, @@ -31,6 +33,7 @@ ListExpr, Lvalue, MatchStmt, + NameExpr, OperatorAssignmentStmt, RaiseStmt, ReturnStmt, @@ -45,12 +48,15 @@ YieldExpr, YieldFromExpr, ) +from mypyc.common import TEMP_ATTR_NAME from mypyc.ir.ops import ( + ERR_NEVER, NAMESPACE_MODULE, NO_TRACEBACK_LINE_NO, Assign, BasicBlock, Branch, + Call, InitStatic, Integer, LoadAddress, @@ -58,6 +64,7 @@ LoadLiteral, LoadStatic, MethodCall, + PrimitiveDescription, RaiseStandardError, Register, Return, @@ -85,6 +92,7 @@ FinallyNonlocalControl, TryFinallyNonlocalControl, ) +from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME from mypyc.irbuild.targets import ( AssignmentTarget, AssignmentTargetAttr, @@ -98,6 +106,8 @@ get_exc_info_op, get_exc_value_op, keep_propagating_op, + no_err_occurred_op, + propagate_if_error_op, raise_exception_op, reraise_exception_op, restore_exc_info_op, @@ -161,10 +171,43 @@ def transform_return_stmt(builder: IRBuilder, stmt: ReturnStmt) -> None: builder.nonlocal_control[-1].gen_return(builder, retval, stmt.line) +def check_unsupported_cls_assignment(builder: IRBuilder, stmt: AssignmentStmt) -> None: + fn = builder.fn_info + method_args = fn.fitem.arg_names + if fn.name != "__new__" or len(method_args) == 0: + return + + ir = builder.get_current_class_ir() + if ir is None or ir.inherits_python or not ir.is_ext_class: + return + + cls_arg = method_args[0] + + def flatten(lvalues: list[Expression]) -> list[Expression]: + flat = [] + for lvalue in lvalues: + if isinstance(lvalue, (TupleExpr, ListExpr)): + flat += flatten(lvalue.items) + else: + flat.append(lvalue) + return flat + + lvalues = flatten(stmt.lvalues) + + for lvalue in lvalues: + if isinstance(lvalue, NameExpr) and lvalue.name == cls_arg: + # Disallowed because it could break the transformation of object.__new__ calls + # inside __new__ methods. + builder.error( + f'Assignment to argument "{cls_arg}" in "__new__" method unsupported', stmt.line + ) + + def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: lvalues = stmt.lvalues assert lvalues builder.disallow_class_assignments(lvalues, stmt.line) + check_unsupported_cls_assignment(builder, stmt) first_lvalue = lvalues[0] if stmt.type and isinstance(stmt.rvalue, TempNode): # This is actually a variable annotation without initializer. Don't generate @@ -209,12 +252,11 @@ def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: and any(t.is_refcounted for t in rvalue_reg.type.types) ): n = len(first_lvalue.items) - for i in range(n): - target = builder.get_assignment_target(first_lvalue.items[i]) - rvalue_item = builder.add(TupleGet(rvalue_reg, i, borrow=True)) - rvalue_item = builder.add(Unborrow(rvalue_item)) - builder.assign(target, rvalue_item, line) + borrows = [builder.add(TupleGet(rvalue_reg, i, borrow=True)) for i in range(n)] builder.builder.keep_alive([rvalue_reg], steal=True) + for lvalue_item, rvalue_item in zip(first_lvalue.items, borrows): + rvalue_item = builder.add(Unborrow(rvalue_item)) + builder.assign(builder.get_assignment_target(lvalue_item), rvalue_item, line) builder.flush_keep_alives() return @@ -347,10 +389,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 = "" @@ -652,10 +694,15 @@ def try_finally_resolve_control( if ret_reg: builder.activate_block(rest) return_block, rest = BasicBlock(), BasicBlock() - builder.add(Branch(builder.read(ret_reg), rest, return_block, Branch.IS_ERROR)) + # For spill targets in try/finally, use nullable read to avoid AttributeError + if isinstance(ret_reg, AssignmentTargetAttr) and ret_reg.attr.startswith(TEMP_ATTR_NAME): + ret_val = builder.read_nullable_attr(ret_reg.obj, ret_reg.attr, -1) + else: + ret_val = builder.read(ret_reg) + builder.add(Branch(ret_val, rest, return_block, Branch.IS_ERROR)) builder.activate_block(return_block) - builder.nonlocal_control[-1].gen_return(builder, builder.read(ret_reg), -1) + builder.nonlocal_control[-1].gen_return(builder, ret_val, -1) # TODO: handle break/continue builder.activate_block(rest) @@ -672,7 +719,7 @@ def try_finally_resolve_control( def transform_try_finally_stmt( - builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc + builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc, line: int = -1 ) -> None: """Generalized try/finally handling that takes functions to gen the bodies. @@ -708,6 +755,125 @@ def transform_try_finally_stmt( builder.activate_block(out_block) +def transform_try_finally_stmt_async( + builder: IRBuilder, try_body: GenFunc, finally_body: GenFunc, line: int = -1 +) -> None: + """Async-aware try/finally handling for when finally contains await. + + This version uses a modified approach that preserves exceptions across await.""" + + # We need to handle returns properly, so we'll use TryFinallyNonlocalControl + # to track return values, similar to the regular try/finally implementation + + err_handler, main_entry, return_entry, finally_entry = ( + BasicBlock(), + BasicBlock(), + BasicBlock(), + BasicBlock(), + ) + + # Track if we're returning from the try block + control = TryFinallyNonlocalControl(return_entry) + builder.builder.push_error_handler(err_handler) + builder.nonlocal_control.append(control) + builder.goto_and_activate(BasicBlock()) + try_body() + builder.goto(main_entry) + builder.nonlocal_control.pop() + builder.builder.pop_error_handler() + ret_reg = control.ret_reg + + # Normal case - no exception or return + builder.activate_block(main_entry) + builder.goto(finally_entry) + + # Return case + builder.activate_block(return_entry) + builder.goto(finally_entry) + + # Exception case - need to catch to clear the error indicator + builder.activate_block(err_handler) + # Catch the error to clear Python's error indicator + builder.call_c(error_catch_op, [], line) + # We're not going to use old_exc since it won't survive await + # The exception is now in sys.exc_info() + builder.goto(finally_entry) + + # Finally block + builder.activate_block(finally_entry) + + # Execute finally body + finally_body() + + # After finally, we need to handle exceptions carefully: + # 1. If finally raised a new exception, it's in the error indicator - let it propagate + # 2. If finally didn't raise, check if we need to reraise the original from sys.exc_info() + # 3. If there was a return, return that value + # 4. Otherwise, normal exit + + # First, check if there's a current exception in the error indicator + # (this would be from the finally block) + no_current_exc = builder.call_c(no_err_occurred_op, [], line) + finally_raised = BasicBlock() + check_original = BasicBlock() + builder.add(Branch(no_current_exc, check_original, finally_raised, Branch.BOOL)) + + # Finally raised an exception - let it propagate naturally + builder.activate_block(finally_raised) + builder.call_c(keep_propagating_op, [], NO_TRACEBACK_LINE_NO) + builder.add(Unreachable()) + + # No exception from finally, check if we need to handle return or original exception + builder.activate_block(check_original) + + # Check if we have a return value + if ret_reg: + return_block, check_old_exc = BasicBlock(), BasicBlock() + builder.add( + Branch( + builder.read(ret_reg, allow_error_value=True), + check_old_exc, + return_block, + Branch.IS_ERROR, + ) + ) + + builder.activate_block(return_block) + builder.nonlocal_control[-1].gen_return(builder, builder.read(ret_reg), -1) + + builder.activate_block(check_old_exc) + + # Check if we need to reraise the original exception from sys.exc_info + exc_info = builder.call_c(get_exc_info_op, [], line) + exc_type = builder.add(TupleGet(exc_info, 0, line)) + + # Check if exc_type is None + none_obj = builder.none_object() + has_exc = builder.binary_op(exc_type, none_obj, "is not", line) + + reraise_block, exit_block = BasicBlock(), BasicBlock() + builder.add(Branch(has_exc, reraise_block, exit_block, Branch.BOOL)) + + # Reraise the original exception + builder.activate_block(reraise_block) + builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) + builder.add(Unreachable()) + + # Normal exit + builder.activate_block(exit_block) + + +# A simple visitor to detect await expressions +class AwaitDetector(mypy.traverser.TraverserVisitor): + def __init__(self) -> None: + super().__init__() + self.has_await = False + + def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> None: + self.has_await = True + super().visit_await_expr(o) + + def transform_try_stmt(builder: IRBuilder, t: TryStmt) -> None: # Our compilation strategy for try/except/else/finally is to # treat try/except/else and try/finally as separate language @@ -716,6 +882,17 @@ def transform_try_stmt(builder: IRBuilder, t: TryStmt) -> None: # body of a try/finally block. if t.is_star: builder.error("Exception groups and except* cannot be compiled yet", t.line) + + # Check if we're in an async function with a finally block that contains await + use_async_version = False + if t.finally_body and builder.fn_info.is_coroutine: + detector = AwaitDetector() + t.finally_body.accept(detector) + + if detector.has_await: + # Use the async version that handles exceptions correctly + use_async_version = True + if t.finally_body: def transform_try_body() -> None: @@ -726,7 +903,14 @@ def transform_try_body() -> None: body = t.finally_body - transform_try_finally_stmt(builder, transform_try_body, lambda: builder.accept(body)) + if use_async_version: + transform_try_finally_stmt_async( + builder, transform_try_body, lambda: builder.accept(body), t.line + ) + else: + transform_try_finally_stmt( + builder, transform_try_body, lambda: builder.accept(body), t.line + ) else: transform_try_except_stmt(builder, t) @@ -757,7 +941,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) @@ -774,7 +958,7 @@ def maybe_natively_call_exit(exc_info: bool) -> Value: args = [none, none, none] if is_native: - assert isinstance(mgr_v.type, RInstance) + assert isinstance(mgr_v.type, RInstance), mgr_v.type exit_val = builder.gen_method_call( builder.read(mgr), f"__{al}exit__", @@ -817,6 +1001,7 @@ def finally_body() -> None: builder, lambda: transform_try_except(builder, try_body, [(None, None, except_body)], None, line), finally_body, + line, ) @@ -876,7 +1061,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. @@ -904,7 +1089,7 @@ def emit_yield(builder: IRBuilder, val: Value, line: int) -> Value: next_label = len(cls.continuation_blocks) cls.continuation_blocks.append(next_block) builder.assign(cls.next_label_target, Integer(next_label), line) - builder.add(Return(retval)) + builder.add(Return(retval, yield_target=next_block)) builder.activate_block(next_block) add_raise_exception_blocks_to_generator_class(builder, line) @@ -923,19 +1108,64 @@ def emit_yield_from_or_await( to_yield_reg = Register(object_rprimitive) received_reg = Register(object_rprimitive) - get_op = coro_op if is_await else iter_op - iter_val = builder.call_c(get_op, [val], line) + helper_method = GENERATOR_HELPER_NAME + if ( + isinstance(val, (Call, MethodCall)) + and isinstance(val.type, RInstance) + and val.type.class_ir.has_method(helper_method) + ): + # This is a generated native generator class, and we can use a fast path. + # This allows two optimizations: + # 1) No need to call CPy_GetCoro() or iter() since for native generators + # it just returns the generator object (implemented here). + # 2) Instead of calling next(), call generator helper method directly, + # since next() just calls __next__ which calls the helper method. + iter_val: Value = val + else: + get_op = coro_op if is_await else iter_op + 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) stop_block, main_block, done_block = BasicBlock(), BasicBlock(), BasicBlock() - _y_init = builder.call_c(next_raw_op, [builder.read(iter_reg)], line) + + if isinstance(iter_reg.type, RInstance) and iter_reg.type.class_ir.has_method(helper_method): + # Second fast path optimization: call helper directly (see also comment above). + # + # Calling a generated generator, so avoid raising StopIteration by passing + # an extra PyObject ** argument to helper where the stop iteration value is stored. + fast_path = True + obj = builder.read(iter_reg) + nn = builder.none_object() + stop_iter_val = Register(object_rprimitive) + err = builder.add(LoadErrorValue(object_rprimitive, undefines=True)) + builder.assign(stop_iter_val, err, line) + ptr = builder.add(LoadAddress(object_pointer_rprimitive, stop_iter_val)) + m = MethodCall(obj, helper_method, [nn, nn, nn, nn, ptr], line) + # Generators have custom error handling, so disable normal error handling. + m.error_kind = ERR_NEVER + _y_init = builder.add(m) + else: + fast_path = False + _y_init = builder.call_c(next_raw_op, [builder.read(iter_reg)], line) + builder.add(Branch(_y_init, stop_block, main_block, Branch.IS_ERROR)) - # Try extracting a return value from a StopIteration and return it. - # If it wasn't, this reraises the exception. builder.activate_block(stop_block) - builder.assign(result, builder.call_c(check_stop_op, [], line), line) + if fast_path: + builder.primitive_op(propagate_if_error_op, [stop_iter_val], line) + builder.assign(result, stop_iter_val, line) + else: + # Try extracting a return value from a StopIteration and return it. + # If it wasn't, this reraises the exception. + builder.assign(result, builder.call_c(check_stop_op, [], line), line) + # Clear the spilled iterator/coroutine so that it will be freed. + # Otherwise, the freeing of the spilled register would likely be delayed. + err = builder.add(LoadErrorValue(iter_reg.type)) + builder.assign(iter_reg, err, line) builder.goto(done_block) builder.activate_block(main_block) diff --git a/mypyc/irbuild/targets.py b/mypyc/irbuild/targets.py index 270c2896bc062..8bc9da074f07d 100644 --- a/mypyc/irbuild/targets.py +++ b/mypyc/irbuild/targets.py @@ -20,6 +20,9 @@ def __init__(self, register: Register) -> None: self.register = register self.type = register.type + def __repr__(self) -> str: + return f"AssignmentTargetRegister({self.register.name})" + class AssignmentTargetIndex(AssignmentTarget): """base[index] as assignment target""" @@ -31,6 +34,9 @@ def __init__(self, base: Value, index: Value) -> None: # lvalue type in mypy and use a better type to avoid unneeded boxing. self.type = object_rprimitive + def __repr__(self) -> str: + return f"AssignmentTargetIndex({self.base!r}, {self.index!r})" + class AssignmentTargetAttr(AssignmentTarget): """obj.attr as assignment target""" @@ -48,6 +54,10 @@ def __init__(self, obj: Value, attr: str, can_borrow: bool = False) -> None: self.obj_type = object_rprimitive self.type = object_rprimitive + def __repr__(self) -> str: + can_borrow_str = ", can_borrow=True" if self.can_borrow else "" + return f"AssignmentTargetAttr({self.obj!r}.{self.attr}{can_borrow_str})" + class AssignmentTargetTuple(AssignmentTarget): """x, ..., y as assignment target""" @@ -55,3 +65,6 @@ class AssignmentTargetTuple(AssignmentTarget): def __init__(self, items: list[AssignmentTarget], star_idx: int | None = None) -> None: self.items = items self.star_idx = star_idx + + def __repr__(self) -> str: + return f"AssignmentTargetTuple({self.items}, {self.star_idx})" diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index ed01a59d1214e..3028e940f7f99 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any +from typing import Any, Final, Literal, TypedDict, cast +from typing_extensions import NotRequired from mypy.nodes import ( ARG_NAMED, @@ -27,8 +28,31 @@ UnaryExpr, Var, ) +from mypy.semanal import refers_to_fullname +from mypy.types import FINAL_DECORATOR_NAMES +from mypyc.errors import Errors + +MYPYC_ATTRS: Final[frozenset[MypycAttr]] = frozenset( + ["native_class", "allow_interpreted_subclasses", "serializable", "free_list_len"] +) + +DATACLASS_DECORATORS: Final = frozenset(["dataclasses.dataclass", "attr.s", "attr.attrs"]) + + +MypycAttr = Literal[ + "native_class", "allow_interpreted_subclasses", "serializable", "free_list_len" +] -DATACLASS_DECORATORS = {"dataclasses.dataclass", "attr.s", "attr.attrs"} + +class MypycAttrs(TypedDict): + native_class: NotRequired[bool] + allow_interpreted_subclasses: NotRequired[bool] + serializable: NotRequired[bool] + free_list_len: NotRequired[int] + + +def is_final_decorator(d: Expression) -> bool: + return refers_to_fullname(d, FINAL_DECORATOR_NAMES) def is_trait_decorator(d: Expression) -> bool: @@ -67,6 +91,8 @@ def is_dataclass(cdef: ClassDef) -> bool: return any(is_dataclass_decorator(d) for d in cdef.decorators) +# The string values returned by this function are inspected in +# mypyc/lib-rt/misc_ops.c:CPyDataclass_SleightOfHand(...). def dataclass_type(cdef: ClassDef) -> str | None: for d in cdef.decorators: typ = dataclass_decorator_type(d) @@ -87,6 +113,8 @@ def get_mypyc_attr_literal(e: Expression) -> Any: return False elif isinstance(e, RefExpr) and e.fullname == "builtins.None": return None + elif isinstance(e, IntExpr): + return e.value return NotImplemented @@ -101,39 +129,127 @@ def get_mypyc_attr_call(d: Expression) -> CallExpr | None: return None -def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]: +def get_mypyc_attrs( + stmt: ClassDef | Decorator, path: str, errors: Errors +) -> tuple[MypycAttrs, dict[MypycAttr, int]]: """Collect all the mypyc_attr attributes on a class definition or a function.""" - attrs: dict[str, Any] = {} + attrs: MypycAttrs = {} + lines: dict[MypycAttr, int] = {} + + def set_mypyc_attr(key: str, value: Any, line: int) -> None: + if key in MYPYC_ATTRS: + key = cast(MypycAttr, key) + attrs[key] = value + lines[key] = line + else: + errors.error(f'"{key}" is not a supported "mypyc_attr"', path, line) + supported_keys = '", "'.join(sorted(MYPYC_ATTRS)) + errors.note(f'supported keys: "{supported_keys}"', path, line) + for dec in stmt.decorators: - d = get_mypyc_attr_call(dec) - if d: + if d := get_mypyc_attr_call(dec): + line = d.line for name, arg in zip(d.arg_names, d.args): if name is None: if isinstance(arg, StrExpr): - attrs[arg.value] = True + set_mypyc_attr(arg.value, True, line) + else: + errors.error( + 'All "mypyc_attr" positional arguments must be string literals.', + path, + line, + ) else: - attrs[name] = get_mypyc_attr_literal(arg) + arg_value = get_mypyc_attr_literal(arg) + set_mypyc_attr(name, arg_value, line) - return attrs + return attrs, lines -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) - for d in cdef.decorators - ): +def is_extension_class(path: str, cdef: ClassDef, errors: Errors) -> bool: + # Check for @mypyc_attr(native_class=True/False) decorator. + explicit_native_class = get_explicit_native_class(path, cdef, errors) + + # Classes with native_class=False are explicitly marked as non extension. + if explicit_native_class is False: return False + + implicit_extension_class, reason = is_implicit_extension_class(cdef) + + # Classes with native_class=True should be extension classes, but they might + # not be able to be due to other reasons. Print an error in that case. + if explicit_native_class is True and not implicit_extension_class: + errors.error( + f"Class is marked as native_class=True but it can't be a native class. {reason}", + path, + cdef.line, + ) + + return implicit_extension_class + + +def get_explicit_native_class(path: str, cdef: ClassDef, errors: Errors) -> bool | None: + """Return value of @mypyc_attr(native_class=True/False) decorator. + + Look for a @mypyc_attr decorator with native_class=True/False and return + the value assigned or None if it doesn't exist. Other values are an error. + """ + + for d in cdef.decorators: + mypyc_attr_call = get_mypyc_attr_call(d) + if not mypyc_attr_call: + continue + + for i, name in enumerate(mypyc_attr_call.arg_names): + if name != "native_class": + continue + + arg = mypyc_attr_call.args[i] + if not isinstance(arg, NameExpr): + errors.error("native_class must be used with True or False only", path, cdef.line) + return None + + if arg.name == "False": + return False + elif arg.name == "True": + return True + else: + errors.error("native_class must be used with True or False only", path, cdef.line) + return None + return None + + +def is_implicit_extension_class(cdef: ClassDef) -> tuple[bool, str]: + """Check if class can be extension class and return a user-friendly reason it can't be one.""" + + for d in cdef.decorators: + if ( + not is_trait_decorator(d) + and not is_dataclass_decorator(d) + and not get_mypyc_attr_call(d) + and not is_final_decorator(d) + ): + return ( + False, + "Classes that have decorators other than supported decorators" + " can't be native classes.", + ) + if cdef.info.typeddict_type: - return False + return False, "TypedDict classes can't be native classes." if cdef.info.is_named_tuple: - return False + return False, "NamedTuple classes can't be native classes." if cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in ( "abc.ABCMeta", "typing.TypingMeta", "typing.GenericMeta", ): - return False - return True + return ( + False, + "Classes with a metaclass other than ABCMeta, TypingMeta or" + " GenericMeta can't be native classes.", + ) + return True, "" def get_func_def(op: FuncDef | Decorator | OverloadedFuncDef) -> FuncDef: diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index 05a033c3e6ad4..dc81e95a2980e 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -73,6 +73,7 @@ TypeAliasStmt, TypeApplication, TypedDictExpr, + TypeFormExpr, TypeVarExpr, TypeVarTupleExpr, UnaryExpr, @@ -387,6 +388,9 @@ def visit_var(self, o: Var) -> None: def visit_cast_expr(self, o: CastExpr) -> Value: assert False, "CastExpr should have been handled in CallExpr" + def visit_type_form_expr(self, o: TypeFormExpr) -> Value: + assert False, "TypeFormExpr should have been handled in CallExpr" + def visit_assert_type_expr(self, o: AssertTypeExpr) -> Value: assert False, "AssertTypeExpr should have been handled in CallExpr" diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 833b1bd2e76a0..6d1e7502a7e7a 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -64,6 +64,15 @@ typedef struct tuple_T4CIOO { } tuple_T4CIOO; #endif +// System-wide empty tuple constant +extern PyObject * __mypyc_empty_tuple__; + +static inline PyObject *CPyTuple_LoadEmptyTupleConstant(void) { +#if !CPY_3_12_FEATURES + Py_INCREF(__mypyc_empty_tuple__); +#endif + return __mypyc_empty_tuple__; +} // Native object operations @@ -139,6 +148,7 @@ CPyTagged CPyTagged_Remainder_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op); CPyTagged CPyTagged_Rshift_(CPyTagged left, CPyTagged right); CPyTagged CPyTagged_Lshift_(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_BitLength(CPyTagged self); PyObject *CPyTagged_Str(CPyTagged n); CPyTagged CPyTagged_FromFloat(double f); @@ -646,14 +656,13 @@ PyObject *CPyObject_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); PyObject *CPyList_Build(Py_ssize_t len, ...); PyObject *CPyList_GetItem(PyObject *list, CPyTagged index); -PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index); PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index); PyObject *CPyList_GetItemBorrow(PyObject *list, CPyTagged index); PyObject *CPyList_GetItemShortBorrow(PyObject *list, CPyTagged index); PyObject *CPyList_GetItemInt64(PyObject *list, int64_t index); PyObject *CPyList_GetItemInt64Borrow(PyObject *list, int64_t index); bool CPyList_SetItem(PyObject *list, CPyTagged index, PyObject *value); -bool CPyList_SetItemUnsafe(PyObject *list, CPyTagged index, PyObject *value); +void CPyList_SetItemUnsafe(PyObject *list, Py_ssize_t index, PyObject *value); bool CPyList_SetItemInt64(PyObject *list, int64_t index, PyObject *value); PyObject *CPyList_PopLast(PyObject *obj); PyObject *CPyList_Pop(PyObject *obj, CPyTagged index); @@ -662,9 +671,13 @@ int CPyList_Insert(PyObject *list, CPyTagged index, PyObject *value); PyObject *CPyList_Extend(PyObject *o1, PyObject *o2); int CPyList_Remove(PyObject *list, PyObject *obj); CPyTagged CPyList_Index(PyObject *list, PyObject *obj); +PyObject *CPySequence_Sort(PyObject *seq); PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size); PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq); +PyObject *CPySequence_InPlaceMultiply(PyObject *seq, CPyTagged t_size); PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); +char CPyList_Clear(PyObject *list); +PyObject *CPyList_Copy(PyObject *list); int CPySequence_Check(PyObject *obj); @@ -700,14 +713,13 @@ tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset); int CPyMapping_Check(PyObject *obj); // Check that dictionary didn't change size during iteration. -static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { +static inline char CPyDict_CheckSize(PyObject *dict, Py_ssize_t size) { if (!PyDict_CheckExact(dict)) { // Dict subclasses will be checked by Python runtime. return 1; } - Py_ssize_t py_size = CPyTagged_AsSsize_t(size); Py_ssize_t dict_size = PyDict_Size(dict); - if (py_size != dict_size) { + if (size != dict_size) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); return 0; } @@ -717,19 +729,47 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { // Str operations +// Macros for strip type. These values are copied from CPython. +#define LEFTSTRIP 0 +#define RIGHTSTRIP 1 +#define BOTHSTRIP 2 +char CPyStr_Equal(PyObject *str1, PyObject *str2); +char CPyStr_EqualLiteral(PyObject *str, PyObject *literal_str, Py_ssize_t literal_length); PyObject *CPyStr_Build(Py_ssize_t len, ...); PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index); +PyObject *CPyStr_GetItemUnsafe(PyObject *str, Py_ssize_t index); +CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction); +CPyTagged CPyStr_FindWithEnd(PyObject *str, PyObject *substr, CPyTagged start, CPyTagged end, int direction); PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split); +PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split); +PyObject *_CPyStr_Strip(PyObject *self, int strip_type, PyObject *sep); +static inline PyObject *CPyStr_Strip(PyObject *self, PyObject *sep) { + return _CPyStr_Strip(self, BOTHSTRIP, sep); +} +static inline PyObject *CPyStr_LStrip(PyObject *self, PyObject *sep) { + return _CPyStr_Strip(self, LEFTSTRIP, sep); +} +static inline PyObject *CPyStr_RStrip(PyObject *self, PyObject *sep) { + return _CPyStr_Strip(self, RIGHTSTRIP, sep); +} PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace); PyObject *CPyStr_Append(PyObject *o1, PyObject *o2); PyObject *CPyStr_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); -bool CPyStr_Startswith(PyObject *self, PyObject *subobj); -bool CPyStr_Endswith(PyObject *self, PyObject *subobj); +int CPyStr_Startswith(PyObject *self, PyObject *subobj); +int CPyStr_Endswith(PyObject *self, PyObject *subobj); +PyObject *CPyStr_Removeprefix(PyObject *self, PyObject *prefix); +PyObject *CPyStr_Removesuffix(PyObject *self, PyObject *suffix); 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_DecodeUTF8(PyObject *bytes); +PyObject *CPy_DecodeASCII(PyObject *bytes); +PyObject *CPy_DecodeLatin1(PyObject *bytes); PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors); +Py_ssize_t CPyStr_Count(PyObject *unicode, PyObject *substring, CPyTagged start); +Py_ssize_t CPyStr_CountFull(PyObject *unicode, PyObject *substring, CPyTagged start, CPyTagged end); +CPyTagged CPyStr_Ord(PyObject *obj); // Bytes operations @@ -740,6 +780,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); @@ -756,7 +797,8 @@ bool CPySet_Remove(PyObject *set, PyObject *key); PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index); PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); -bool CPySequenceTuple_SetItemUnsafe(PyObject *tuple, CPyTagged index, PyObject *value); +PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index); +void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value); // Exception operations @@ -783,7 +825,7 @@ static inline PyObject *_CPy_FromDummy(PyObject *p) { return p; } -static int CPy_NoErrOccured(void) { +static int CPy_NoErrOccurred(void) { return PyErr_Occurred() == NULL; } @@ -846,6 +888,12 @@ static inline bool CPy_TypeCheck(PyObject *o, PyObject *type) { return PyObject_TypeCheck(o, (PyTypeObject *)type); } +static inline PyObject *CPy_TYPE(PyObject *obj) { + PyObject *result = (PyObject *)Py_TYPE(obj); + Py_INCREF(result); + return result; +} + PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o); PyObject *CPy_GetCoro(PyObject *obj); PyObject *CPyIter_Send(PyObject *iter, PyObject *val); @@ -854,15 +902,17 @@ PyObject *CPy_FetchStopIterationValue(void); PyObject *CPyType_FromTemplate(PyObject *template_, PyObject *orig_bases, PyObject *modname); -PyObject *CPyType_FromTemplateWarpper(PyObject *template_, +PyObject *CPyType_FromTemplateWrapper(PyObject *template_, PyObject *orig_bases, PyObject *modname); int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, - PyObject *dict, PyObject *annotations); + PyObject *dict, PyObject *annotations, + PyObject *dataclass_type); PyObject *CPyPickle_SetState(PyObject *obj, PyObject *state); PyObject *CPyPickle_GetState(PyObject *obj); CPyTagged CPyTagged_Id(PyObject *o); void CPyDebug_Print(const char *msg); +void CPyDebug_PrintObject(PyObject *obj); void CPy_Init(void); int CPyArg_ParseTupleAndKeywords(PyObject *, PyObject *, const char *, const char *, const char * const *, ...); @@ -899,6 +949,24 @@ PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func, PyOb PyObject *CPy_GetAIter(PyObject *obj); PyObject *CPy_GetANext(PyObject *aiter); void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_value); +void CPyTrace_LogEvent(const char *location, const char *line, const char *op, const char *details); + +static inline PyObject *CPyObject_GenericGetAttr(PyObject *self, PyObject *name) { + return _PyObject_GenericGetAttrWithDict(self, name, NULL, 1); +} +static inline int CPyObject_GenericSetAttr(PyObject *self, PyObject *name, PyObject *value) { + return _PyObject_GenericSetAttrWithDict(self, name, value, NULL); +} + +PyObject *CPy_SetupObject(PyObject *type); + +#if CPY_3_11_FEATURES +PyObject *CPy_GetName(PyObject *obj); +#endif + +#if CPY_3_14_FEATURES +void CPy_SetImmortal(PyObject *obj); +#endif #ifdef __cplusplus } diff --git a/mypyc/lib-rt/base64/arch/avx/codec.c b/mypyc/lib-rt/base64/arch/avx/codec.c new file mode 100644 index 0000000000000..7a64a94be2aff --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx/codec.c @@ -0,0 +1,68 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_AVX +#include + +// Only enable inline assembly on supported compilers and on 64-bit CPUs. +#ifndef BASE64_AVX_USE_ASM +# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64 +# define BASE64_AVX_USE_ASM 1 +# else +# define BASE64_AVX_USE_ASM 0 +# endif +#endif + +#include "../ssse3/dec_reshuffle.c" +#include "../ssse3/dec_loop.c" + +#if BASE64_AVX_USE_ASM +# include "./enc_loop_asm.c" +#else +# include "../ssse3/enc_translate.c" +# include "../ssse3/enc_reshuffle.c" +# include "../ssse3/enc_loop.c" +#endif + +#endif // HAVE_AVX + +void +base64_stream_encode_avx BASE64_ENC_PARAMS +{ +#if HAVE_AVX + #include "../generic/enc_head.c" + + // For supported compilers, use a hand-optimized inline assembly + // encoder. Otherwise fall back on the SSSE3 encoder, but compiled with + // AVX flags to generate better optimized AVX code. + +#if BASE64_AVX_USE_ASM + enc_loop_avx(&s, &slen, &o, &olen); +#else + enc_loop_ssse3(&s, &slen, &o, &olen); +#endif + + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_avx BASE64_DEC_PARAMS +{ +#if HAVE_AVX + #include "../generic/dec_head.c" + dec_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/avx/enc_loop_asm.c b/mypyc/lib-rt/base64/arch/avx/enc_loop_asm.c new file mode 100644 index 0000000000000..979269af57740 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx/enc_loop_asm.c @@ -0,0 +1,264 @@ +// Apologies in advance for combining the preprocessor with inline assembly, +// two notoriously gnarly parts of C, but it was necessary to avoid a lot of +// code repetition. The preprocessor is used to template large sections of +// inline assembly that differ only in the registers used. If the code was +// written out by hand, it would become very large and hard to audit. + +// Generate a block of inline assembly that loads register R0 from memory. The +// offset at which the register is loaded is set by the given round. +#define LOAD(R0, ROUND) \ + "vlddqu ("#ROUND" * 12)(%[src]), %["R0"] \n\t" + +// Generate a block of inline assembly that deinterleaves and shuffles register +// R0 using preloaded constants. Outputs in R0 and R1. +#define SHUF(R0, R1, R2) \ + "vpshufb %[lut0], %["R0"], %["R1"] \n\t" \ + "vpand %["R1"], %[msk0], %["R2"] \n\t" \ + "vpand %["R1"], %[msk2], %["R1"] \n\t" \ + "vpmulhuw %["R2"], %[msk1], %["R2"] \n\t" \ + "vpmullw %["R1"], %[msk3], %["R1"] \n\t" \ + "vpor %["R1"], %["R2"], %["R1"] \n\t" + +// Generate a block of inline assembly that takes R0 and R1 and translates +// their contents to the base64 alphabet, using preloaded constants. +#define TRAN(R0, R1, R2) \ + "vpsubusb %[n51], %["R1"], %["R0"] \n\t" \ + "vpcmpgtb %[n25], %["R1"], %["R2"] \n\t" \ + "vpsubb %["R2"], %["R0"], %["R0"] \n\t" \ + "vpshufb %["R0"], %[lut1], %["R2"] \n\t" \ + "vpaddb %["R1"], %["R2"], %["R0"] \n\t" + +// Generate a block of inline assembly that stores the given register R0 at an +// offset set by the given round. +#define STOR(R0, ROUND) \ + "vmovdqu %["R0"], ("#ROUND" * 16)(%[dst]) \n\t" + +// Generate a block of inline assembly that generates a single self-contained +// encoder round: fetch the data, process it, and store the result. Then update +// the source and destination pointers. +#define ROUND() \ + LOAD("a", 0) \ + SHUF("a", "b", "c") \ + TRAN("a", "b", "c") \ + STOR("a", 0) \ + "add $12, %[src] \n\t" \ + "add $16, %[dst] \n\t" + +// Define a macro that initiates a three-way interleaved encoding round by +// preloading registers a, b and c from memory. +// The register graph shows which registers are in use during each step, and +// is a visual aid for choosing registers for that step. Symbol index: +// +// + indicates that a register is loaded by that step. +// | indicates that a register is in use and must not be touched. +// - indicates that a register is decommissioned by that step. +// x indicates that a register is used as a temporary by that step. +// V indicates that a register is an input or output to the macro. +// +#define ROUND_3_INIT() /* a b c d e f */ \ + LOAD("a", 0) /* + */ \ + SHUF("a", "d", "e") /* | + x */ \ + LOAD("b", 1) /* | + | */ \ + TRAN("a", "d", "e") /* | | - x */ \ + LOAD("c", 2) /* V V V */ + +// Define a macro that translates, shuffles and stores the input registers A, B +// and C, and preloads registers D, E and F for the next round. +// This macro can be arbitrarily daisy-chained by feeding output registers D, E +// and F back into the next round as input registers A, B and C. The macro +// carefully interleaves memory operations with data operations for optimal +// pipelined performance. + +#define ROUND_3(ROUND, A,B,C,D,E,F) /* A B C D E F */ \ + LOAD(D, (ROUND + 3)) /* V V V + */ \ + SHUF(B, E, F) /* | | | | + x */ \ + STOR(A, (ROUND + 0)) /* - | | | | */ \ + TRAN(B, E, F) /* | | | - x */ \ + LOAD(E, (ROUND + 4)) /* | | | + */ \ + SHUF(C, A, F) /* + | | | | x */ \ + STOR(B, (ROUND + 1)) /* | - | | | */ \ + TRAN(C, A, F) /* - | | | x */ \ + LOAD(F, (ROUND + 5)) /* | | | + */ \ + SHUF(D, A, B) /* + x | | | | */ \ + STOR(C, (ROUND + 2)) /* | - | | | */ \ + TRAN(D, A, B) /* - x V V V */ + +// Define a macro that terminates a ROUND_3 macro by taking pre-loaded +// registers D, E and F, and translating, shuffling and storing them. +#define ROUND_3_END(ROUND, A,B,C,D,E,F) /* A B C D E F */ \ + SHUF(E, A, B) /* + x V V V */ \ + STOR(D, (ROUND + 3)) /* | - | | */ \ + TRAN(E, A, B) /* - x | | */ \ + SHUF(F, C, D) /* + x | | */ \ + STOR(E, (ROUND + 4)) /* | - | */ \ + TRAN(F, C, D) /* - x | */ \ + STOR(F, (ROUND + 5)) /* - */ + +// Define a type A round. Inputs are a, b, and c, outputs are d, e, and f. +#define ROUND_3_A(ROUND) \ + ROUND_3(ROUND, "a", "b", "c", "d", "e", "f") + +// Define a type B round. Inputs and outputs are swapped with regard to type A. +#define ROUND_3_B(ROUND) \ + ROUND_3(ROUND, "d", "e", "f", "a", "b", "c") + +// Terminating macro for a type A round. +#define ROUND_3_A_LAST(ROUND) \ + ROUND_3_A(ROUND) \ + ROUND_3_END(ROUND, "a", "b", "c", "d", "e", "f") + +// Terminating macro for a type B round. +#define ROUND_3_B_LAST(ROUND) \ + ROUND_3_B(ROUND) \ + ROUND_3_END(ROUND, "d", "e", "f", "a", "b", "c") + +// Suppress clang's warning that the literal string in the asm statement is +// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99 +// compilers). It may be true, but the goal here is not C99 portability. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" + +static inline void +enc_loop_avx (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + // For a clearer explanation of the algorithm used by this function, + // please refer to the plain (not inline assembly) implementation. This + // function follows the same basic logic. + + if (*slen < 16) { + return; + } + + // Process blocks of 12 bytes at a time. Input is read in blocks of 16 + // bytes, so "reserve" four bytes from the input buffer to ensure that + // we never read beyond the end of the input buffer. + size_t rounds = (*slen - 4) / 12; + + *slen -= rounds * 12; // 12 bytes consumed per round + *olen += rounds * 16; // 16 bytes produced per round + + // Number of times to go through the 36x loop. + size_t loops = rounds / 36; + + // Number of rounds remaining after the 36x loop. + rounds %= 36; + + // Lookup tables. + const __m128i lut0 = _mm_set_epi8( + 10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1); + + const __m128i lut1 = _mm_setr_epi8( + 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0); + + // Temporary registers. + __m128i a, b, c, d, e, f; + + __asm__ volatile ( + + // If there are 36 rounds or more, enter a 36x unrolled loop of + // interleaved encoding rounds. The rounds interleave memory + // operations (load/store) with data operations (table lookups, + // etc) to maximize pipeline throughput. + " test %[loops], %[loops] \n\t" + " jz 18f \n\t" + " jmp 36f \n\t" + " \n\t" + ".balign 64 \n\t" + "36: " ROUND_3_INIT() + " " ROUND_3_A( 0) + " " ROUND_3_B( 3) + " " ROUND_3_A( 6) + " " ROUND_3_B( 9) + " " ROUND_3_A(12) + " " ROUND_3_B(15) + " " ROUND_3_A(18) + " " ROUND_3_B(21) + " " ROUND_3_A(24) + " " ROUND_3_B(27) + " " ROUND_3_A_LAST(30) + " add $(12 * 36), %[src] \n\t" + " add $(16 * 36), %[dst] \n\t" + " dec %[loops] \n\t" + " jnz 36b \n\t" + + // Enter an 18x unrolled loop for rounds of 18 or more. + "18: cmp $18, %[rounds] \n\t" + " jl 9f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A(0) + " " ROUND_3_B(3) + " " ROUND_3_A(6) + " " ROUND_3_B(9) + " " ROUND_3_A_LAST(12) + " sub $18, %[rounds] \n\t" + " add $(12 * 18), %[src] \n\t" + " add $(16 * 18), %[dst] \n\t" + + // Enter a 9x unrolled loop for rounds of 9 or more. + "9: cmp $9, %[rounds] \n\t" + " jl 6f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A(0) + " " ROUND_3_B_LAST(3) + " sub $9, %[rounds] \n\t" + " add $(12 * 9), %[src] \n\t" + " add $(16 * 9), %[dst] \n\t" + + // Enter a 6x unrolled loop for rounds of 6 or more. + "6: cmp $6, %[rounds] \n\t" + " jl 55f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A_LAST(0) + " sub $6, %[rounds] \n\t" + " add $(12 * 6), %[src] \n\t" + " add $(16 * 6), %[dst] \n\t" + + // Dispatch the remaining rounds 0..5. + "55: cmp $3, %[rounds] \n\t" + " jg 45f \n\t" + " je 3f \n\t" + " cmp $1, %[rounds] \n\t" + " jg 2f \n\t" + " je 1f \n\t" + " jmp 0f \n\t" + + "45: cmp $4, %[rounds] \n\t" + " je 4f \n\t" + + // Block of non-interlaced encoding rounds, which can each + // individually be jumped to. Rounds fall through to the next. + "5: " ROUND() + "4: " ROUND() + "3: " ROUND() + "2: " ROUND() + "1: " ROUND() + "0: \n\t" + + // Outputs (modified). + : [rounds] "+r" (rounds), + [loops] "+r" (loops), + [src] "+r" (*s), + [dst] "+r" (*o), + [a] "=&x" (a), + [b] "=&x" (b), + [c] "=&x" (c), + [d] "=&x" (d), + [e] "=&x" (e), + [f] "=&x" (f) + + // Inputs (not modified). + : [lut0] "x" (lut0), + [lut1] "x" (lut1), + [msk0] "x" (_mm_set1_epi32(0x0FC0FC00)), + [msk1] "x" (_mm_set1_epi32(0x04000040)), + [msk2] "x" (_mm_set1_epi32(0x003F03F0)), + [msk3] "x" (_mm_set1_epi32(0x01000010)), + [n51] "x" (_mm_set1_epi8(51)), + [n25] "x" (_mm_set1_epi8(25)) + + // Clobbers. + : "cc", "memory" + ); +} + +#pragma GCC diagnostic pop diff --git a/mypyc/lib-rt/base64/arch/avx2/codec.c b/mypyc/lib-rt/base64/arch/avx2/codec.c new file mode 100644 index 0000000000000..a54385bf89bea --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/codec.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_AVX2 +#include + +// Only enable inline assembly on supported compilers and on 64-bit CPUs. +#ifndef BASE64_AVX2_USE_ASM +# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64 +# define BASE64_AVX2_USE_ASM 1 +# else +# define BASE64_AVX2_USE_ASM 0 +# endif +#endif + +#include "./dec_reshuffle.c" +#include "./dec_loop.c" + +#if BASE64_AVX2_USE_ASM +# include "./enc_loop_asm.c" +#else +# include "./enc_translate.c" +# include "./enc_reshuffle.c" +# include "./enc_loop.c" +#endif + +#endif // HAVE_AVX2 + +void +base64_stream_encode_avx2 BASE64_ENC_PARAMS +{ +#if HAVE_AVX2 + #include "../generic/enc_head.c" + enc_loop_avx2(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_avx2 BASE64_DEC_PARAMS +{ +#if HAVE_AVX2 + #include "../generic/dec_head.c" + dec_loop_avx2(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/avx2/dec_loop.c b/mypyc/lib-rt/base64/arch/avx2/dec_loop.c new file mode 100644 index 0000000000000..b8a4ccafd82af --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/dec_loop.c @@ -0,0 +1,110 @@ +static BASE64_FORCE_INLINE int +dec_loop_avx2_inner (const uint8_t **s, uint8_t **o, size_t *rounds) +{ + const __m256i lut_lo = _mm256_setr_epi8( + 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A, + 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A); + + const __m256i lut_hi = _mm256_setr_epi8( + 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10); + + const __m256i lut_roll = _mm256_setr_epi8( + 0, 16, 19, 4, -65, -65, -71, -71, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 19, 4, -65, -65, -71, -71, + 0, 0, 0, 0, 0, 0, 0, 0); + + const __m256i mask_2F = _mm256_set1_epi8(0x2F); + + // Load input: + __m256i str = _mm256_loadu_si256((__m256i *) *s); + + // See the SSSE3 decoder for an explanation of the algorithm. + const __m256i hi_nibbles = _mm256_and_si256(_mm256_srli_epi32(str, 4), mask_2F); + const __m256i lo_nibbles = _mm256_and_si256(str, mask_2F); + const __m256i hi = _mm256_shuffle_epi8(lut_hi, hi_nibbles); + const __m256i lo = _mm256_shuffle_epi8(lut_lo, lo_nibbles); + + if (!_mm256_testz_si256(lo, hi)) { + return 0; + } + + const __m256i eq_2F = _mm256_cmpeq_epi8(str, mask_2F); + const __m256i roll = _mm256_shuffle_epi8(lut_roll, _mm256_add_epi8(eq_2F, hi_nibbles)); + + // Now simply add the delta values to the input: + str = _mm256_add_epi8(str, roll); + + // Reshuffle the input to packed 12-byte output format: + str = dec_reshuffle(str); + + // Store the output: + _mm256_storeu_si256((__m256i *) *o, str); + + *s += 32; + *o += 24; + *rounds -= 1; + + return 1; +} + +static inline void +dec_loop_avx2 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 45) { + return; + } + + // Process blocks of 32 bytes per round. Because 8 extra zero bytes are + // written after the output, ensure that there will be at least 13 + // bytes of input data left to cover the gap. (11 data bytes and up to + // two end-of-string markers.) + size_t rounds = (*slen - 13) / 32; + + *slen -= rounds * 32; // 32 bytes consumed per round + *olen += rounds * 24; // 24 bytes produced per round + + do { + if (rounds >= 8) { + if (dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds)) { + continue; + } + break; + } + if (rounds >= 4) { + if (dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds)) { + continue; + } + break; + } + if (rounds >= 2) { + if (dec_loop_avx2_inner(s, o, &rounds) && + dec_loop_avx2_inner(s, o, &rounds)) { + continue; + } + break; + } + dec_loop_avx2_inner(s, o, &rounds); + break; + + } while (rounds > 0); + + // Adjust for any rounds that were skipped: + *slen += rounds * 32; + *olen -= rounds * 24; +} diff --git a/mypyc/lib-rt/base64/arch/avx2/dec_reshuffle.c b/mypyc/lib-rt/base64/arch/avx2/dec_reshuffle.c new file mode 100644 index 0000000000000..bc875ce9dd256 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/dec_reshuffle.c @@ -0,0 +1,34 @@ +static BASE64_FORCE_INLINE __m256i +dec_reshuffle (const __m256i in) +{ + // in, lower lane, bits, upper case are most significant bits, lower + // case are least significant bits: + // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ + // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG + // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD + // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA + + const __m256i merge_ab_and_bc = _mm256_maddubs_epi16(in, _mm256_set1_epi32(0x01400140)); + // 0000kkkk LLllllll 0000JJJJ JJjjKKKK + // 0000hhhh IIiiiiii 0000GGGG GGggHHHH + // 0000eeee FFffffff 0000DDDD DDddEEEE + // 0000bbbb CCcccccc 0000AAAA AAaaBBBB + + __m256i out = _mm256_madd_epi16(merge_ab_and_bc, _mm256_set1_epi32(0x00011000)); + // 00000000 JJJJJJjj KKKKkkkk LLllllll + // 00000000 GGGGGGgg HHHHhhhh IIiiiiii + // 00000000 DDDDDDdd EEEEeeee FFffffff + // 00000000 AAAAAAaa BBBBbbbb CCcccccc + + // Pack bytes together in each lane: + out = _mm256_shuffle_epi8(out, _mm256_setr_epi8( + 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1, + 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1)); + // 00000000 00000000 00000000 00000000 + // LLllllll KKKKkkkk JJJJJJjj IIiiiiii + // HHHHhhhh GGGGGGgg FFffffff EEEEeeee + // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa + + // Pack lanes: + return _mm256_permutevar8x32_epi32(out, _mm256_setr_epi32(0, 1, 2, 4, 5, 6, -1, -1)); +} diff --git a/mypyc/lib-rt/base64/arch/avx2/enc_loop.c b/mypyc/lib-rt/base64/arch/avx2/enc_loop.c new file mode 100644 index 0000000000000..6f4aa0ab580cd --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/enc_loop.c @@ -0,0 +1,89 @@ +static BASE64_FORCE_INLINE void +enc_loop_avx2_inner_first (const uint8_t **s, uint8_t **o) +{ + // First load is done at s - 0 to not get a segfault: + __m256i src = _mm256_loadu_si256((__m256i *) *s); + + // Shift by 4 bytes, as required by enc_reshuffle: + src = _mm256_permutevar8x32_epi32(src, _mm256_setr_epi32(0, 0, 1, 2, 3, 4, 5, 6)); + + // Reshuffle, translate, store: + src = enc_reshuffle(src); + src = enc_translate(src); + _mm256_storeu_si256((__m256i *) *o, src); + + // Subsequent loads will be done at s - 4, set pointer for next round: + *s += 20; + *o += 32; +} + +static BASE64_FORCE_INLINE void +enc_loop_avx2_inner (const uint8_t **s, uint8_t **o) +{ + // Load input: + __m256i src = _mm256_loadu_si256((__m256i *) *s); + + // Reshuffle, translate, store: + src = enc_reshuffle(src); + src = enc_translate(src); + _mm256_storeu_si256((__m256i *) *o, src); + + *s += 24; + *o += 32; +} + +static inline void +enc_loop_avx2 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 32) { + return; + } + + // Process blocks of 24 bytes at a time. Because blocks are loaded 32 + // bytes at a time an offset of -4, ensure that there will be at least + // 4 remaining bytes after the last round, so that the final read will + // not pass beyond the bounds of the input buffer: + size_t rounds = (*slen - 4) / 24; + + *slen -= rounds * 24; // 24 bytes consumed per round + *olen += rounds * 32; // 32 bytes produced per round + + // The first loop iteration requires special handling to ensure that + // the read, which is done at an offset, does not underflow the buffer: + enc_loop_avx2_inner_first(s, o); + rounds--; + + while (rounds > 0) { + if (rounds >= 8) { + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_avx2_inner(s, o); + enc_loop_avx2_inner(s, o); + rounds -= 2; + continue; + } + enc_loop_avx2_inner(s, o); + break; + } + + // Add the offset back: + *s += 4; +} diff --git a/mypyc/lib-rt/base64/arch/avx2/enc_loop_asm.c b/mypyc/lib-rt/base64/arch/avx2/enc_loop_asm.c new file mode 100644 index 0000000000000..eb775a1d1f03d --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/enc_loop_asm.c @@ -0,0 +1,291 @@ +// Apologies in advance for combining the preprocessor with inline assembly, +// two notoriously gnarly parts of C, but it was necessary to avoid a lot of +// code repetition. The preprocessor is used to template large sections of +// inline assembly that differ only in the registers used. If the code was +// written out by hand, it would become very large and hard to audit. + +// Generate a block of inline assembly that loads register R0 from memory. The +// offset at which the register is loaded is set by the given round and a +// constant offset. +#define LOAD(R0, ROUND, OFFSET) \ + "vlddqu ("#ROUND" * 24 + "#OFFSET")(%[src]), %["R0"] \n\t" + +// Generate a block of inline assembly that deinterleaves and shuffles register +// R0 using preloaded constants. Outputs in R0 and R1. +#define SHUF(R0, R1, R2) \ + "vpshufb %[lut0], %["R0"], %["R1"] \n\t" \ + "vpand %["R1"], %[msk0], %["R2"] \n\t" \ + "vpand %["R1"], %[msk2], %["R1"] \n\t" \ + "vpmulhuw %["R2"], %[msk1], %["R2"] \n\t" \ + "vpmullw %["R1"], %[msk3], %["R1"] \n\t" \ + "vpor %["R1"], %["R2"], %["R1"] \n\t" + +// Generate a block of inline assembly that takes R0 and R1 and translates +// their contents to the base64 alphabet, using preloaded constants. +#define TRAN(R0, R1, R2) \ + "vpsubusb %[n51], %["R1"], %["R0"] \n\t" \ + "vpcmpgtb %[n25], %["R1"], %["R2"] \n\t" \ + "vpsubb %["R2"], %["R0"], %["R0"] \n\t" \ + "vpshufb %["R0"], %[lut1], %["R2"] \n\t" \ + "vpaddb %["R1"], %["R2"], %["R0"] \n\t" + +// Generate a block of inline assembly that stores the given register R0 at an +// offset set by the given round. +#define STOR(R0, ROUND) \ + "vmovdqu %["R0"], ("#ROUND" * 32)(%[dst]) \n\t" + +// Generate a block of inline assembly that generates a single self-contained +// encoder round: fetch the data, process it, and store the result. Then update +// the source and destination pointers. +#define ROUND() \ + LOAD("a", 0, -4) \ + SHUF("a", "b", "c") \ + TRAN("a", "b", "c") \ + STOR("a", 0) \ + "add $24, %[src] \n\t" \ + "add $32, %[dst] \n\t" + +// Define a macro that initiates a three-way interleaved encoding round by +// preloading registers a, b and c from memory. +// The register graph shows which registers are in use during each step, and +// is a visual aid for choosing registers for that step. Symbol index: +// +// + indicates that a register is loaded by that step. +// | indicates that a register is in use and must not be touched. +// - indicates that a register is decommissioned by that step. +// x indicates that a register is used as a temporary by that step. +// V indicates that a register is an input or output to the macro. +// +#define ROUND_3_INIT() /* a b c d e f */ \ + LOAD("a", 0, -4) /* + */ \ + SHUF("a", "d", "e") /* | + x */ \ + LOAD("b", 1, -4) /* | + | */ \ + TRAN("a", "d", "e") /* | | - x */ \ + LOAD("c", 2, -4) /* V V V */ + +// Define a macro that translates, shuffles and stores the input registers A, B +// and C, and preloads registers D, E and F for the next round. +// This macro can be arbitrarily daisy-chained by feeding output registers D, E +// and F back into the next round as input registers A, B and C. The macro +// carefully interleaves memory operations with data operations for optimal +// pipelined performance. + +#define ROUND_3(ROUND, A,B,C,D,E,F) /* A B C D E F */ \ + LOAD(D, (ROUND + 3), -4) /* V V V + */ \ + SHUF(B, E, F) /* | | | | + x */ \ + STOR(A, (ROUND + 0)) /* - | | | | */ \ + TRAN(B, E, F) /* | | | - x */ \ + LOAD(E, (ROUND + 4), -4) /* | | | + */ \ + SHUF(C, A, F) /* + | | | | x */ \ + STOR(B, (ROUND + 1)) /* | - | | | */ \ + TRAN(C, A, F) /* - | | | x */ \ + LOAD(F, (ROUND + 5), -4) /* | | | + */ \ + SHUF(D, A, B) /* + x | | | | */ \ + STOR(C, (ROUND + 2)) /* | - | | | */ \ + TRAN(D, A, B) /* - x V V V */ + +// Define a macro that terminates a ROUND_3 macro by taking pre-loaded +// registers D, E and F, and translating, shuffling and storing them. +#define ROUND_3_END(ROUND, A,B,C,D,E,F) /* A B C D E F */ \ + SHUF(E, A, B) /* + x V V V */ \ + STOR(D, (ROUND + 3)) /* | - | | */ \ + TRAN(E, A, B) /* - x | | */ \ + SHUF(F, C, D) /* + x | | */ \ + STOR(E, (ROUND + 4)) /* | - | */ \ + TRAN(F, C, D) /* - x | */ \ + STOR(F, (ROUND + 5)) /* - */ + +// Define a type A round. Inputs are a, b, and c, outputs are d, e, and f. +#define ROUND_3_A(ROUND) \ + ROUND_3(ROUND, "a", "b", "c", "d", "e", "f") + +// Define a type B round. Inputs and outputs are swapped with regard to type A. +#define ROUND_3_B(ROUND) \ + ROUND_3(ROUND, "d", "e", "f", "a", "b", "c") + +// Terminating macro for a type A round. +#define ROUND_3_A_LAST(ROUND) \ + ROUND_3_A(ROUND) \ + ROUND_3_END(ROUND, "a", "b", "c", "d", "e", "f") + +// Terminating macro for a type B round. +#define ROUND_3_B_LAST(ROUND) \ + ROUND_3_B(ROUND) \ + ROUND_3_END(ROUND, "d", "e", "f", "a", "b", "c") + +// Suppress clang's warning that the literal string in the asm statement is +// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99 +// compilers). It may be true, but the goal here is not C99 portability. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" + +static inline void +enc_loop_avx2 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + // For a clearer explanation of the algorithm used by this function, + // please refer to the plain (not inline assembly) implementation. This + // function follows the same basic logic. + + if (*slen < 32) { + return; + } + + // Process blocks of 24 bytes at a time. Because blocks are loaded 32 + // bytes at a time an offset of -4, ensure that there will be at least + // 4 remaining bytes after the last round, so that the final read will + // not pass beyond the bounds of the input buffer. + size_t rounds = (*slen - 4) / 24; + + *slen -= rounds * 24; // 24 bytes consumed per round + *olen += rounds * 32; // 32 bytes produced per round + + // Pre-decrement the number of rounds to get the number of rounds + // *after* the first round, which is handled as a special case. + rounds--; + + // Number of times to go through the 36x loop. + size_t loops = rounds / 36; + + // Number of rounds remaining after the 36x loop. + rounds %= 36; + + // Lookup tables. + const __m256i lut0 = _mm256_set_epi8( + 10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1, + 14, 15, 13, 14, 11, 12, 10, 11, 8, 9, 7, 8, 5, 6, 4, 5); + + const __m256i lut1 = _mm256_setr_epi8( + 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0, + 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0); + + // Temporary registers. + __m256i a, b, c, d, e; + + // Temporary register f doubles as the shift mask for the first round. + __m256i f = _mm256_setr_epi32(0, 0, 1, 2, 3, 4, 5, 6); + + __asm__ volatile ( + + // The first loop iteration requires special handling to ensure + // that the read, which is normally done at an offset of -4, + // does not underflow the buffer. Load the buffer at an offset + // of 0 and permute the input to achieve the same effect. + LOAD("a", 0, 0) + "vpermd %[a], %[f], %[a] \n\t" + + // Perform the standard shuffling and translation steps. + SHUF("a", "b", "c") + TRAN("a", "b", "c") + + // Store the result and increment the source and dest pointers. + "vmovdqu %[a], (%[dst]) \n\t" + "add $24, %[src] \n\t" + "add $32, %[dst] \n\t" + + // If there are 36 rounds or more, enter a 36x unrolled loop of + // interleaved encoding rounds. The rounds interleave memory + // operations (load/store) with data operations (table lookups, + // etc) to maximize pipeline throughput. + " test %[loops], %[loops] \n\t" + " jz 18f \n\t" + " jmp 36f \n\t" + " \n\t" + ".balign 64 \n\t" + "36: " ROUND_3_INIT() + " " ROUND_3_A( 0) + " " ROUND_3_B( 3) + " " ROUND_3_A( 6) + " " ROUND_3_B( 9) + " " ROUND_3_A(12) + " " ROUND_3_B(15) + " " ROUND_3_A(18) + " " ROUND_3_B(21) + " " ROUND_3_A(24) + " " ROUND_3_B(27) + " " ROUND_3_A_LAST(30) + " add $(24 * 36), %[src] \n\t" + " add $(32 * 36), %[dst] \n\t" + " dec %[loops] \n\t" + " jnz 36b \n\t" + + // Enter an 18x unrolled loop for rounds of 18 or more. + "18: cmp $18, %[rounds] \n\t" + " jl 9f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A(0) + " " ROUND_3_B(3) + " " ROUND_3_A(6) + " " ROUND_3_B(9) + " " ROUND_3_A_LAST(12) + " sub $18, %[rounds] \n\t" + " add $(24 * 18), %[src] \n\t" + " add $(32 * 18), %[dst] \n\t" + + // Enter a 9x unrolled loop for rounds of 9 or more. + "9: cmp $9, %[rounds] \n\t" + " jl 6f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A(0) + " " ROUND_3_B_LAST(3) + " sub $9, %[rounds] \n\t" + " add $(24 * 9), %[src] \n\t" + " add $(32 * 9), %[dst] \n\t" + + // Enter a 6x unrolled loop for rounds of 6 or more. + "6: cmp $6, %[rounds] \n\t" + " jl 55f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A_LAST(0) + " sub $6, %[rounds] \n\t" + " add $(24 * 6), %[src] \n\t" + " add $(32 * 6), %[dst] \n\t" + + // Dispatch the remaining rounds 0..5. + "55: cmp $3, %[rounds] \n\t" + " jg 45f \n\t" + " je 3f \n\t" + " cmp $1, %[rounds] \n\t" + " jg 2f \n\t" + " je 1f \n\t" + " jmp 0f \n\t" + + "45: cmp $4, %[rounds] \n\t" + " je 4f \n\t" + + // Block of non-interlaced encoding rounds, which can each + // individually be jumped to. Rounds fall through to the next. + "5: " ROUND() + "4: " ROUND() + "3: " ROUND() + "2: " ROUND() + "1: " ROUND() + "0: \n\t" + + // Outputs (modified). + : [rounds] "+r" (rounds), + [loops] "+r" (loops), + [src] "+r" (*s), + [dst] "+r" (*o), + [a] "=&x" (a), + [b] "=&x" (b), + [c] "=&x" (c), + [d] "=&x" (d), + [e] "=&x" (e), + [f] "+x" (f) + + // Inputs (not modified). + : [lut0] "x" (lut0), + [lut1] "x" (lut1), + [msk0] "x" (_mm256_set1_epi32(0x0FC0FC00)), + [msk1] "x" (_mm256_set1_epi32(0x04000040)), + [msk2] "x" (_mm256_set1_epi32(0x003F03F0)), + [msk3] "x" (_mm256_set1_epi32(0x01000010)), + [n51] "x" (_mm256_set1_epi8(51)), + [n25] "x" (_mm256_set1_epi8(25)) + + // Clobbers. + : "cc", "memory" + ); +} + +#pragma GCC diagnostic pop diff --git a/mypyc/lib-rt/base64/arch/avx2/enc_reshuffle.c b/mypyc/lib-rt/base64/arch/avx2/enc_reshuffle.c new file mode 100644 index 0000000000000..82c659b39ce74 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/enc_reshuffle.c @@ -0,0 +1,83 @@ +static BASE64_FORCE_INLINE __m256i +enc_reshuffle (const __m256i input) +{ + // Translation of the SSSE3 reshuffling algorithm to AVX2. This one + // works with shifted (4 bytes) input in order to be able to work + // efficiently in the two 128-bit lanes. + + // Input, bytes MSB to LSB: + // 0 0 0 0 x w v u t s r q p o n m + // l k j i h g f e d c b a 0 0 0 0 + + const __m256i in = _mm256_shuffle_epi8(input, _mm256_set_epi8( + 10, 11, 9, 10, + 7, 8, 6, 7, + 4, 5, 3, 4, + 1, 2, 0, 1, + + 14, 15, 13, 14, + 11, 12, 10, 11, + 8, 9, 7, 8, + 5, 6, 4, 5)); + // in, bytes MSB to LSB: + // w x v w + // t u s t + // q r p q + // n o m n + // k l j k + // h i g h + // e f d e + // b c a b + + const __m256i t0 = _mm256_and_si256(in, _mm256_set1_epi32(0x0FC0FC00)); + // bits, upper case are most significant bits, lower case are least + // significant bits. + // 0000wwww XX000000 VVVVVV00 00000000 + // 0000tttt UU000000 SSSSSS00 00000000 + // 0000qqqq RR000000 PPPPPP00 00000000 + // 0000nnnn OO000000 MMMMMM00 00000000 + // 0000kkkk LL000000 JJJJJJ00 00000000 + // 0000hhhh II000000 GGGGGG00 00000000 + // 0000eeee FF000000 DDDDDD00 00000000 + // 0000bbbb CC000000 AAAAAA00 00000000 + + const __m256i t1 = _mm256_mulhi_epu16(t0, _mm256_set1_epi32(0x04000040)); + // 00000000 00wwwwXX 00000000 00VVVVVV + // 00000000 00ttttUU 00000000 00SSSSSS + // 00000000 00qqqqRR 00000000 00PPPPPP + // 00000000 00nnnnOO 00000000 00MMMMMM + // 00000000 00kkkkLL 00000000 00JJJJJJ + // 00000000 00hhhhII 00000000 00GGGGGG + // 00000000 00eeeeFF 00000000 00DDDDDD + // 00000000 00bbbbCC 00000000 00AAAAAA + + const __m256i t2 = _mm256_and_si256(in, _mm256_set1_epi32(0x003F03F0)); + // 00000000 00xxxxxx 000000vv WWWW0000 + // 00000000 00uuuuuu 000000ss TTTT0000 + // 00000000 00rrrrrr 000000pp QQQQ0000 + // 00000000 00oooooo 000000mm NNNN0000 + // 00000000 00llllll 000000jj KKKK0000 + // 00000000 00iiiiii 000000gg HHHH0000 + // 00000000 00ffffff 000000dd EEEE0000 + // 00000000 00cccccc 000000aa BBBB0000 + + const __m256i t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010)); + // 00xxxxxx 00000000 00vvWWWW 00000000 + // 00uuuuuu 00000000 00ssTTTT 00000000 + // 00rrrrrr 00000000 00ppQQQQ 00000000 + // 00oooooo 00000000 00mmNNNN 00000000 + // 00llllll 00000000 00jjKKKK 00000000 + // 00iiiiii 00000000 00ggHHHH 00000000 + // 00ffffff 00000000 00ddEEEE 00000000 + // 00cccccc 00000000 00aaBBBB 00000000 + + return _mm256_or_si256(t1, t3); + // 00xxxxxx 00wwwwXX 00vvWWWW 00VVVVVV + // 00uuuuuu 00ttttUU 00ssTTTT 00SSSSSS + // 00rrrrrr 00qqqqRR 00ppQQQQ 00PPPPPP + // 00oooooo 00nnnnOO 00mmNNNN 00MMMMMM + // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ + // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG + // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD + // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA +} diff --git a/mypyc/lib-rt/base64/arch/avx2/enc_translate.c b/mypyc/lib-rt/base64/arch/avx2/enc_translate.c new file mode 100644 index 0000000000000..370da98f596ca --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx2/enc_translate.c @@ -0,0 +1,30 @@ +static BASE64_FORCE_INLINE __m256i +enc_translate (const __m256i in) +{ + // A lookup table containing the absolute offsets for all ranges: + const __m256i lut = _mm256_setr_epi8( + 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0, + 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0); + + // Translate values 0..63 to the Base64 alphabet. There are five sets: + // # From To Abs Index Characters + // 0 [0..25] [65..90] +65 0 ABCDEFGHIJKLMNOPQRSTUVWXYZ + // 1 [26..51] [97..122] +71 1 abcdefghijklmnopqrstuvwxyz + // 2 [52..61] [48..57] -4 [2..11] 0123456789 + // 3 [62] [43] -19 12 + + // 4 [63] [47] -16 13 / + + // Create LUT indices from the input. The index for range #0 is right, + // others are 1 less than expected: + __m256i indices = _mm256_subs_epu8(in, _mm256_set1_epi8(51)); + + // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0: + const __m256i mask = _mm256_cmpgt_epi8(in, _mm256_set1_epi8(25)); + + // Subtract -1, so add 1 to indices for range #[1..4]. All indices are + // now correct: + indices = _mm256_sub_epi8(indices, mask); + + // Add offsets to input values: + return _mm256_add_epi8(in, _mm256_shuffle_epi8(lut, indices)); +} diff --git a/mypyc/lib-rt/base64/arch/avx512/codec.c b/mypyc/lib-rt/base64/arch/avx512/codec.c new file mode 100644 index 0000000000000..98210826a5fe9 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx512/codec.c @@ -0,0 +1,44 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_AVX512 +#include + +#include "../avx2/dec_reshuffle.c" +#include "../avx2/dec_loop.c" +#include "enc_reshuffle_translate.c" +#include "enc_loop.c" + +#endif // HAVE_AVX512 + +void +base64_stream_encode_avx512 BASE64_ENC_PARAMS +{ +#if HAVE_AVX512 + #include "../generic/enc_head.c" + enc_loop_avx512(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +// Reuse AVX2 decoding. Not supporting AVX512 at present +int +base64_stream_decode_avx512 BASE64_DEC_PARAMS +{ +#if HAVE_AVX512 + #include "../generic/dec_head.c" + dec_loop_avx2(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/avx512/enc_loop.c b/mypyc/lib-rt/base64/arch/avx512/enc_loop.c new file mode 100644 index 0000000000000..cb44696ba31a5 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx512/enc_loop.c @@ -0,0 +1,61 @@ +static BASE64_FORCE_INLINE void +enc_loop_avx512_inner (const uint8_t **s, uint8_t **o) +{ + // Load input. + __m512i src = _mm512_loadu_si512((__m512i *) *s); + + // Reshuffle, translate, store. + src = enc_reshuffle_translate(src); + _mm512_storeu_si512((__m512i *) *o, src); + + *s += 48; + *o += 64; +} + +static inline void +enc_loop_avx512 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 64) { + return; + } + + // Process blocks of 48 bytes at a time. Because blocks are loaded 64 + // bytes at a time, ensure that there will be at least 24 remaining + // bytes after the last round, so that the final read will not pass + // beyond the bounds of the input buffer. + size_t rounds = (*slen - 24) / 48; + + *slen -= rounds * 48; // 48 bytes consumed per round + *olen += rounds * 64; // 64 bytes produced per round + + while (rounds > 0) { + if (rounds >= 8) { + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_avx512_inner(s, o); + enc_loop_avx512_inner(s, o); + rounds -= 2; + continue; + } + enc_loop_avx512_inner(s, o); + break; + } +} diff --git a/mypyc/lib-rt/base64/arch/avx512/enc_reshuffle_translate.c b/mypyc/lib-rt/base64/arch/avx512/enc_reshuffle_translate.c new file mode 100644 index 0000000000000..ae12b3af25490 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/avx512/enc_reshuffle_translate.c @@ -0,0 +1,50 @@ +// AVX512 algorithm is based on permutevar and multishift. The code is based on +// https://github.com/WojciechMula/base64simd which is under BSD-2 license. + +static BASE64_FORCE_INLINE __m512i +enc_reshuffle_translate (const __m512i input) +{ + // 32-bit input + // [ 0 0 0 0 0 0 0 0|c1 c0 d5 d4 d3 d2 d1 d0| + // b3 b2 b1 b0 c5 c4 c3 c2|a5 a4 a3 a2 a1 a0 b5 b4] + // output order [1, 2, 0, 1] + // [b3 b2 b1 b0 c5 c4 c3 c2|c1 c0 d5 d4 d3 d2 d1 d0| + // a5 a4 a3 a2 a1 a0 b5 b4|b3 b2 b1 b0 c3 c2 c1 c0] + + const __m512i shuffle_input = _mm512_setr_epi32(0x01020001, + 0x04050304, + 0x07080607, + 0x0a0b090a, + 0x0d0e0c0d, + 0x10110f10, + 0x13141213, + 0x16171516, + 0x191a1819, + 0x1c1d1b1c, + 0x1f201e1f, + 0x22232122, + 0x25262425, + 0x28292728, + 0x2b2c2a2b, + 0x2e2f2d2e); + + // Reorder bytes + // [b3 b2 b1 b0 c5 c4 c3 c2|c1 c0 d5 d4 d3 d2 d1 d0| + // a5 a4 a3 a2 a1 a0 b5 b4|b3 b2 b1 b0 c3 c2 c1 c0] + const __m512i in = _mm512_permutexvar_epi8(shuffle_input, input); + + // After multishift a single 32-bit lane has following layout + // [c1 c0 d5 d4 d3 d2 d1 d0|b1 b0 c5 c4 c3 c2 c1 c0| + // a1 a0 b5 b4 b3 b2 b1 b0|d1 d0 a5 a4 a3 a2 a1 a0] + // (a = [10:17], b = [4:11], c = [22:27], d = [16:21]) + + // 48, 54, 36, 42, 16, 22, 4, 10 + const __m512i shifts = _mm512_set1_epi64(0x3036242a1016040alu); + __m512i shuffled_in = _mm512_multishift_epi64_epi8(shifts, in); + + // Translate immediately after reshuffled. + const __m512i lookup = _mm512_loadu_si512(base64_table_enc_6bit); + + // Translation 6-bit values to ASCII. + return _mm512_permutexvar_epi8(shuffled_in, lookup); +} diff --git a/mypyc/lib-rt/base64/arch/generic/32/dec_loop.c b/mypyc/lib-rt/base64/arch/generic/32/dec_loop.c new file mode 100644 index 0000000000000..aa290d7e03a86 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/32/dec_loop.c @@ -0,0 +1,86 @@ +static BASE64_FORCE_INLINE int +dec_loop_generic_32_inner (const uint8_t **s, uint8_t **o, size_t *rounds) +{ + const uint32_t str + = base64_table_dec_32bit_d0[(*s)[0]] + | base64_table_dec_32bit_d1[(*s)[1]] + | base64_table_dec_32bit_d2[(*s)[2]] + | base64_table_dec_32bit_d3[(*s)[3]]; + +#if BASE64_LITTLE_ENDIAN + + // LUTs for little-endian set MSB in case of invalid character: + if (str & UINT32_C(0x80000000)) { + return 0; + } +#else + // LUTs for big-endian set LSB in case of invalid character: + if (str & UINT32_C(1)) { + return 0; + } +#endif + // Store the output: + memcpy(*o, &str, sizeof (str)); + + *s += 4; + *o += 3; + *rounds -= 1; + + return 1; +} + +static inline void +dec_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 8) { + return; + } + + // Process blocks of 4 bytes per round. Because one extra zero byte is + // written after the output, ensure that there will be at least 4 bytes + // of input data left to cover the gap. (Two data bytes and up to two + // end-of-string markers.) + size_t rounds = (*slen - 4) / 4; + + *slen -= rounds * 4; // 4 bytes consumed per round + *olen += rounds * 3; // 3 bytes produced per round + + do { + if (rounds >= 8) { + if (dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds)) { + continue; + } + break; + } + if (rounds >= 4) { + if (dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds)) { + continue; + } + break; + } + if (rounds >= 2) { + if (dec_loop_generic_32_inner(s, o, &rounds) && + dec_loop_generic_32_inner(s, o, &rounds)) { + continue; + } + break; + } + dec_loop_generic_32_inner(s, o, &rounds); + break; + + } while (rounds > 0); + + // Adjust for any rounds that were skipped: + *slen += rounds * 4; + *olen -= rounds * 3; +} diff --git a/mypyc/lib-rt/base64/arch/generic/32/enc_loop.c b/mypyc/lib-rt/base64/arch/generic/32/enc_loop.c new file mode 100644 index 0000000000000..b5e6eefd9d430 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/32/enc_loop.c @@ -0,0 +1,73 @@ +static BASE64_FORCE_INLINE void +enc_loop_generic_32_inner (const uint8_t **s, uint8_t **o) +{ + uint32_t src; + + // Load input: + memcpy(&src, *s, sizeof (src)); + + // Reorder to 32-bit big-endian, if not already in that format. The + // workset must be in big-endian, otherwise the shifted bits do not + // carry over properly among adjacent bytes: + src = BASE64_HTOBE32(src); + + // Two indices for the 12-bit lookup table: + const size_t index0 = (src >> 20) & 0xFFFU; + const size_t index1 = (src >> 8) & 0xFFFU; + + // Table lookup and store: + memcpy(*o + 0, base64_table_enc_12bit + index0, 2); + memcpy(*o + 2, base64_table_enc_12bit + index1, 2); + + *s += 3; + *o += 4; +} + +static inline void +enc_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 4) { + return; + } + + // Process blocks of 3 bytes at a time. Because blocks are loaded 4 + // bytes at a time, ensure that there will be at least one remaining + // byte after the last round, so that the final read will not pass + // beyond the bounds of the input buffer: + size_t rounds = (*slen - 1) / 3; + + *slen -= rounds * 3; // 3 bytes consumed per round + *olen += rounds * 4; // 4 bytes produced per round + + do { + if (rounds >= 8) { + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_generic_32_inner(s, o); + enc_loop_generic_32_inner(s, o); + rounds -= 2; + continue; + } + enc_loop_generic_32_inner(s, o); + break; + + } while (rounds > 0); +} diff --git a/mypyc/lib-rt/base64/arch/generic/64/enc_loop.c b/mypyc/lib-rt/base64/arch/generic/64/enc_loop.c new file mode 100644 index 0000000000000..e6a29cd5ec418 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/64/enc_loop.c @@ -0,0 +1,77 @@ +static BASE64_FORCE_INLINE void +enc_loop_generic_64_inner (const uint8_t **s, uint8_t **o) +{ + uint64_t src; + + // Load input: + memcpy(&src, *s, sizeof (src)); + + // Reorder to 64-bit big-endian, if not already in that format. The + // workset must be in big-endian, otherwise the shifted bits do not + // carry over properly among adjacent bytes: + src = BASE64_HTOBE64(src); + + // Four indices for the 12-bit lookup table: + const size_t index0 = (src >> 52) & 0xFFFU; + const size_t index1 = (src >> 40) & 0xFFFU; + const size_t index2 = (src >> 28) & 0xFFFU; + const size_t index3 = (src >> 16) & 0xFFFU; + + // Table lookup and store: + memcpy(*o + 0, base64_table_enc_12bit + index0, 2); + memcpy(*o + 2, base64_table_enc_12bit + index1, 2); + memcpy(*o + 4, base64_table_enc_12bit + index2, 2); + memcpy(*o + 6, base64_table_enc_12bit + index3, 2); + + *s += 6; + *o += 8; +} + +static inline void +enc_loop_generic_64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 8) { + return; + } + + // Process blocks of 6 bytes at a time. Because blocks are loaded 8 + // bytes at a time, ensure that there will be at least 2 remaining + // bytes after the last round, so that the final read will not pass + // beyond the bounds of the input buffer: + size_t rounds = (*slen - 2) / 6; + + *slen -= rounds * 6; // 6 bytes consumed per round + *olen += rounds * 8; // 8 bytes produced per round + + do { + if (rounds >= 8) { + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_generic_64_inner(s, o); + enc_loop_generic_64_inner(s, o); + rounds -= 2; + continue; + } + enc_loop_generic_64_inner(s, o); + break; + + } while (rounds > 0); +} diff --git a/mypyc/lib-rt/base64/arch/generic/codec.c b/mypyc/lib-rt/base64/arch/generic/codec.c new file mode 100644 index 0000000000000..1a29be7c836d1 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/codec.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if BASE64_WORDSIZE == 32 +# include "32/enc_loop.c" +#elif BASE64_WORDSIZE == 64 +# include "64/enc_loop.c" +#endif + +#if BASE64_WORDSIZE >= 32 +# include "32/dec_loop.c" +#endif + +void +base64_stream_encode_plain BASE64_ENC_PARAMS +{ + #include "enc_head.c" +#if BASE64_WORDSIZE == 32 + enc_loop_generic_32(&s, &slen, &o, &olen); +#elif BASE64_WORDSIZE == 64 + enc_loop_generic_64(&s, &slen, &o, &olen); +#endif + #include "enc_tail.c" +} + +int +base64_stream_decode_plain BASE64_DEC_PARAMS +{ + #include "dec_head.c" +#if BASE64_WORDSIZE >= 32 + dec_loop_generic_32(&s, &slen, &o, &olen); +#endif + #include "dec_tail.c" +} diff --git a/mypyc/lib-rt/base64/arch/generic/dec_head.c b/mypyc/lib-rt/base64/arch/generic/dec_head.c new file mode 100644 index 0000000000000..179a31b63ff4e --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/dec_head.c @@ -0,0 +1,37 @@ +int ret = 0; +const uint8_t *s = (const uint8_t *) src; +uint8_t *o = (uint8_t *) out; +uint8_t q; + +// Use local temporaries to avoid cache thrashing: +size_t olen = 0; +size_t slen = srclen; +struct base64_state st; +st.eof = state->eof; +st.bytes = state->bytes; +st.carry = state->carry; + +// If we previously saw an EOF or an invalid character, bail out: +if (st.eof) { + *outlen = 0; + ret = 0; + // If there was a trailing '=' to check, check it: + if (slen && (st.eof == BASE64_AEOF)) { + state->bytes = 0; + state->eof = BASE64_EOF; + ret = ((base64_table_dec_8bit[*s++] == 254) && (slen == 1)) ? 1 : 0; + } + return ret; +} + +// Turn four 6-bit numbers into three bytes: +// out[0] = 11111122 +// out[1] = 22223333 +// out[2] = 33444444 + +// Duff's device again: +switch (st.bytes) +{ + for (;;) + { + case 0: diff --git a/mypyc/lib-rt/base64/arch/generic/dec_tail.c b/mypyc/lib-rt/base64/arch/generic/dec_tail.c new file mode 100644 index 0000000000000..e64f7247f3f12 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/dec_tail.c @@ -0,0 +1,91 @@ + if (slen-- == 0) { + ret = 1; + break; + } + if ((q = base64_table_dec_8bit[*s++]) >= 254) { + st.eof = BASE64_EOF; + // Treat character '=' as invalid for byte 0: + break; + } + st.carry = q << 2; + st.bytes++; + + // Deliberate fallthrough: + BASE64_FALLTHROUGH + + case 1: if (slen-- == 0) { + ret = 1; + break; + } + if ((q = base64_table_dec_8bit[*s++]) >= 254) { + st.eof = BASE64_EOF; + // Treat character '=' as invalid for byte 1: + break; + } + *o++ = st.carry | (q >> 4); + st.carry = q << 4; + st.bytes++; + olen++; + + // Deliberate fallthrough: + BASE64_FALLTHROUGH + + case 2: if (slen-- == 0) { + ret = 1; + break; + } + if ((q = base64_table_dec_8bit[*s++]) >= 254) { + st.bytes++; + // When q == 254, the input char is '='. + // Check if next byte is also '=': + if (q == 254) { + if (slen-- != 0) { + st.bytes = 0; + // EOF: + st.eof = BASE64_EOF; + q = base64_table_dec_8bit[*s++]; + ret = ((q == 254) && (slen == 0)) ? 1 : 0; + break; + } + else { + // Almost EOF + st.eof = BASE64_AEOF; + ret = 1; + break; + } + } + // If we get here, there was an error: + break; + } + *o++ = st.carry | (q >> 2); + st.carry = q << 6; + st.bytes++; + olen++; + + // Deliberate fallthrough: + BASE64_FALLTHROUGH + + case 3: if (slen-- == 0) { + ret = 1; + break; + } + if ((q = base64_table_dec_8bit[*s++]) >= 254) { + st.bytes = 0; + st.eof = BASE64_EOF; + // When q == 254, the input char is '='. Return 1 and EOF. + // When q == 255, the input char is invalid. Return 0 and EOF. + ret = ((q == 254) && (slen == 0)) ? 1 : 0; + break; + } + *o++ = st.carry | q; + st.carry = 0; + st.bytes = 0; + olen++; + } +} + +state->eof = st.eof; +state->bytes = st.bytes; +state->carry = st.carry; +*outlen = olen; +return ret; diff --git a/mypyc/lib-rt/base64/arch/generic/enc_head.c b/mypyc/lib-rt/base64/arch/generic/enc_head.c new file mode 100644 index 0000000000000..38d60b2c62b53 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/enc_head.c @@ -0,0 +1,24 @@ +// Assume that *out is large enough to contain the output. +// Theoretically it should be 4/3 the length of src. +const uint8_t *s = (const uint8_t *) src; +uint8_t *o = (uint8_t *) out; + +// Use local temporaries to avoid cache thrashing: +size_t olen = 0; +size_t slen = srclen; +struct base64_state st; +st.bytes = state->bytes; +st.carry = state->carry; + +// Turn three bytes into four 6-bit numbers: +// in[0] = 00111111 +// in[1] = 00112222 +// in[2] = 00222233 +// in[3] = 00333333 + +// Duff's device, a for() loop inside a switch() statement. Legal! +switch (st.bytes) +{ + for (;;) + { + case 0: diff --git a/mypyc/lib-rt/base64/arch/generic/enc_tail.c b/mypyc/lib-rt/base64/arch/generic/enc_tail.c new file mode 100644 index 0000000000000..cbd573376812d --- /dev/null +++ b/mypyc/lib-rt/base64/arch/generic/enc_tail.c @@ -0,0 +1,34 @@ + if (slen-- == 0) { + break; + } + *o++ = base64_table_enc_6bit[*s >> 2]; + st.carry = (*s++ << 4) & 0x30; + st.bytes++; + olen += 1; + + // Deliberate fallthrough: + BASE64_FALLTHROUGH + + case 1: if (slen-- == 0) { + break; + } + *o++ = base64_table_enc_6bit[st.carry | (*s >> 4)]; + st.carry = (*s++ << 2) & 0x3C; + st.bytes++; + olen += 1; + + // Deliberate fallthrough: + BASE64_FALLTHROUGH + + case 2: if (slen-- == 0) { + break; + } + *o++ = base64_table_enc_6bit[st.carry | (*s >> 6)]; + *o++ = base64_table_enc_6bit[*s++ & 0x3F]; + st.bytes = 0; + olen += 2; + } +} +state->bytes = st.bytes; +state->carry = st.carry; +*outlen = olen; diff --git a/mypyc/lib-rt/base64/arch/neon32/codec.c b/mypyc/lib-rt/base64/arch/neon32/codec.c new file mode 100644 index 0000000000000..4a32592b9c469 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon32/codec.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#ifdef __arm__ +# if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && HAVE_NEON32 +# define BASE64_USE_NEON32 +# endif +#endif + +#ifdef BASE64_USE_NEON32 +#include + +// Only enable inline assembly on supported compilers. +#if defined(__GNUC__) || defined(__clang__) +#define BASE64_NEON32_USE_ASM +#endif + +static BASE64_FORCE_INLINE uint8x16_t +vqtbl1q_u8 (const uint8x16_t lut, const uint8x16_t indices) +{ + // NEON32 only supports 64-bit wide lookups in 128-bit tables. Emulate + // the NEON64 `vqtbl1q_u8` intrinsic to do 128-bit wide lookups. + uint8x8x2_t lut2; + uint8x8x2_t result; + + lut2.val[0] = vget_low_u8(lut); + lut2.val[1] = vget_high_u8(lut); + + result.val[0] = vtbl2_u8(lut2, vget_low_u8(indices)); + result.val[1] = vtbl2_u8(lut2, vget_high_u8(indices)); + + return vcombine_u8(result.val[0], result.val[1]); +} + +#include "../generic/32/dec_loop.c" +#include "../generic/32/enc_loop.c" +#include "dec_loop.c" +#include "enc_reshuffle.c" +#include "enc_translate.c" +#include "enc_loop.c" + +#endif // BASE64_USE_NEON32 + +// Stride size is so large on these NEON 32-bit functions +// (48 bytes encode, 32 bytes decode) that we inline the +// uint32 codec to stay performant on smaller inputs. + +void +base64_stream_encode_neon32 BASE64_ENC_PARAMS +{ +#ifdef BASE64_USE_NEON32 + #include "../generic/enc_head.c" + enc_loop_neon32(&s, &slen, &o, &olen); + enc_loop_generic_32(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_neon32 BASE64_DEC_PARAMS +{ +#ifdef BASE64_USE_NEON32 + #include "../generic/dec_head.c" + dec_loop_neon32(&s, &slen, &o, &olen); + dec_loop_generic_32(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/neon32/dec_loop.c b/mypyc/lib-rt/base64/arch/neon32/dec_loop.c new file mode 100644 index 0000000000000..e4caed7a7d1cc --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon32/dec_loop.c @@ -0,0 +1,106 @@ +static BASE64_FORCE_INLINE int +is_nonzero (const uint8x16_t v) +{ + uint64_t u64; + const uint64x2_t v64 = vreinterpretq_u64_u8(v); + const uint32x2_t v32 = vqmovn_u64(v64); + + vst1_u64(&u64, vreinterpret_u64_u32(v32)); + return u64 != 0; +} + +static BASE64_FORCE_INLINE uint8x16_t +delta_lookup (const uint8x16_t v) +{ + const uint8x8_t lut = { + 0, 16, 19, 4, (uint8_t) -65, (uint8_t) -65, (uint8_t) -71, (uint8_t) -71, + }; + + return vcombine_u8( + vtbl1_u8(lut, vget_low_u8(v)), + vtbl1_u8(lut, vget_high_u8(v))); +} + +static BASE64_FORCE_INLINE uint8x16_t +dec_loop_neon32_lane (uint8x16_t *lane) +{ + // See the SSSE3 decoder for an explanation of the algorithm. + const uint8x16_t lut_lo = { + 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A + }; + + const uint8x16_t lut_hi = { + 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 + }; + + const uint8x16_t mask_0F = vdupq_n_u8(0x0F); + const uint8x16_t mask_2F = vdupq_n_u8(0x2F); + + const uint8x16_t hi_nibbles = vshrq_n_u8(*lane, 4); + const uint8x16_t lo_nibbles = vandq_u8(*lane, mask_0F); + const uint8x16_t eq_2F = vceqq_u8(*lane, mask_2F); + + const uint8x16_t hi = vqtbl1q_u8(lut_hi, hi_nibbles); + const uint8x16_t lo = vqtbl1q_u8(lut_lo, lo_nibbles); + + // Now simply add the delta values to the input: + *lane = vaddq_u8(*lane, delta_lookup(vaddq_u8(eq_2F, hi_nibbles))); + + // Return the validity mask: + return vandq_u8(lo, hi); +} + +static inline void +dec_loop_neon32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 64) { + return; + } + + // Process blocks of 64 bytes per round. Unlike the SSE codecs, no + // extra trailing zero bytes are written, so it is not necessary to + // reserve extra input bytes: + size_t rounds = *slen / 64; + + *slen -= rounds * 64; // 64 bytes consumed per round + *olen += rounds * 48; // 48 bytes produced per round + + do { + uint8x16x3_t dec; + + // Load 64 bytes and deinterleave: + uint8x16x4_t str = vld4q_u8(*s); + + // Decode each lane, collect a mask of invalid inputs: + const uint8x16_t classified + = dec_loop_neon32_lane(&str.val[0]) + | dec_loop_neon32_lane(&str.val[1]) + | dec_loop_neon32_lane(&str.val[2]) + | dec_loop_neon32_lane(&str.val[3]); + + // Check for invalid input: if any of the delta values are + // zero, fall back on bytewise code to do error checking and + // reporting: + if (is_nonzero(classified)) { + break; + } + + // Compress four bytes into three: + dec.val[0] = vorrq_u8(vshlq_n_u8(str.val[0], 2), vshrq_n_u8(str.val[1], 4)); + dec.val[1] = vorrq_u8(vshlq_n_u8(str.val[1], 4), vshrq_n_u8(str.val[2], 2)); + dec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]); + + // Interleave and store decoded result: + vst3q_u8(*o, dec); + + *s += 64; + *o += 48; + + } while (--rounds > 0); + + // Adjust for any rounds that were skipped: + *slen += rounds * 64; + *olen -= rounds * 48; +} diff --git a/mypyc/lib-rt/base64/arch/neon32/enc_loop.c b/mypyc/lib-rt/base64/arch/neon32/enc_loop.c new file mode 100644 index 0000000000000..2adff48f2bcc0 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon32/enc_loop.c @@ -0,0 +1,170 @@ +#ifdef BASE64_NEON32_USE_ASM +static BASE64_FORCE_INLINE void +enc_loop_neon32_inner_asm (const uint8_t **s, uint8_t **o) +{ + // This function duplicates the functionality of enc_loop_neon32_inner, + // but entirely with inline assembly. This gives a significant speedup + // over using NEON intrinsics, which do not always generate very good + // code. The logic of the assembly is directly lifted from the + // intrinsics version, so it can be used as a guide to this code. + + // Temporary registers, used as scratch space. + uint8x16_t tmp0, tmp1, tmp2, tmp3; + uint8x16_t mask0, mask1, mask2, mask3; + + // A lookup table containing the absolute offsets for all ranges. + const uint8x16_t lut = { + 65U, 71U, 252U, 252U, + 252U, 252U, 252U, 252U, + 252U, 252U, 252U, 252U, + 237U, 240U, 0U, 0U + }; + + // Numeric constants. + const uint8x16_t n51 = vdupq_n_u8(51); + const uint8x16_t n25 = vdupq_n_u8(25); + const uint8x16_t n63 = vdupq_n_u8(63); + + __asm__ ( + + // Load 48 bytes and deinterleave. The bytes are loaded to + // hard-coded registers q12, q13 and q14, to ensure that they + // are contiguous. Increment the source pointer. + "vld3.8 {d24, d26, d28}, [%[src]]! \n\t" + "vld3.8 {d25, d27, d29}, [%[src]]! \n\t" + + // Reshuffle the bytes using temporaries. + "vshr.u8 %q[t0], q12, #2 \n\t" + "vshr.u8 %q[t1], q13, #4 \n\t" + "vshr.u8 %q[t2], q14, #6 \n\t" + "vsli.8 %q[t1], q12, #4 \n\t" + "vsli.8 %q[t2], q13, #2 \n\t" + "vand.u8 %q[t1], %q[t1], %q[n63] \n\t" + "vand.u8 %q[t2], %q[t2], %q[n63] \n\t" + "vand.u8 %q[t3], q14, %q[n63] \n\t" + + // t0..t3 are the reshuffled inputs. Create LUT indices. + "vqsub.u8 q12, %q[t0], %q[n51] \n\t" + "vqsub.u8 q13, %q[t1], %q[n51] \n\t" + "vqsub.u8 q14, %q[t2], %q[n51] \n\t" + "vqsub.u8 q15, %q[t3], %q[n51] \n\t" + + // Create the mask for range #0. + "vcgt.u8 %q[m0], %q[t0], %q[n25] \n\t" + "vcgt.u8 %q[m1], %q[t1], %q[n25] \n\t" + "vcgt.u8 %q[m2], %q[t2], %q[n25] \n\t" + "vcgt.u8 %q[m3], %q[t3], %q[n25] \n\t" + + // Subtract -1 to correct the LUT indices. + "vsub.u8 q12, %q[m0] \n\t" + "vsub.u8 q13, %q[m1] \n\t" + "vsub.u8 q14, %q[m2] \n\t" + "vsub.u8 q15, %q[m3] \n\t" + + // Lookup the delta values. + "vtbl.u8 d24, {%q[lut]}, d24 \n\t" + "vtbl.u8 d25, {%q[lut]}, d25 \n\t" + "vtbl.u8 d26, {%q[lut]}, d26 \n\t" + "vtbl.u8 d27, {%q[lut]}, d27 \n\t" + "vtbl.u8 d28, {%q[lut]}, d28 \n\t" + "vtbl.u8 d29, {%q[lut]}, d29 \n\t" + "vtbl.u8 d30, {%q[lut]}, d30 \n\t" + "vtbl.u8 d31, {%q[lut]}, d31 \n\t" + + // Add the delta values. + "vadd.u8 q12, %q[t0] \n\t" + "vadd.u8 q13, %q[t1] \n\t" + "vadd.u8 q14, %q[t2] \n\t" + "vadd.u8 q15, %q[t3] \n\t" + + // Store 64 bytes and interleave. Increment the dest pointer. + "vst4.8 {d24, d26, d28, d30}, [%[dst]]! \n\t" + "vst4.8 {d25, d27, d29, d31}, [%[dst]]! \n\t" + + // Outputs (modified). + : [src] "+r" (*s), + [dst] "+r" (*o), + [t0] "=&w" (tmp0), + [t1] "=&w" (tmp1), + [t2] "=&w" (tmp2), + [t3] "=&w" (tmp3), + [m0] "=&w" (mask0), + [m1] "=&w" (mask1), + [m2] "=&w" (mask2), + [m3] "=&w" (mask3) + + // Inputs (not modified). + : [lut] "w" (lut), + [n25] "w" (n25), + [n51] "w" (n51), + [n63] "w" (n63) + + // Clobbers. + : "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", + "cc", "memory" + ); +} +#endif + +static BASE64_FORCE_INLINE void +enc_loop_neon32_inner (const uint8_t **s, uint8_t **o) +{ +#ifdef BASE64_NEON32_USE_ASM + enc_loop_neon32_inner_asm(s, o); +#else + // Load 48 bytes and deinterleave: + uint8x16x3_t src = vld3q_u8(*s); + + // Reshuffle: + uint8x16x4_t out = enc_reshuffle(src); + + // Translate reshuffled bytes to the Base64 alphabet: + out = enc_translate(out); + + // Interleave and store output: + vst4q_u8(*o, out); + + *s += 48; + *o += 64; +#endif +} + +static inline void +enc_loop_neon32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + size_t rounds = *slen / 48; + + *slen -= rounds * 48; // 48 bytes consumed per round + *olen += rounds * 64; // 64 bytes produced per round + + while (rounds > 0) { + if (rounds >= 8) { + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_neon32_inner(s, o); + enc_loop_neon32_inner(s, o); + rounds -= 2; + continue; + } + enc_loop_neon32_inner(s, o); + break; + } +} diff --git a/mypyc/lib-rt/base64/arch/neon32/enc_reshuffle.c b/mypyc/lib-rt/base64/arch/neon32/enc_reshuffle.c new file mode 100644 index 0000000000000..fa94d2799be64 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon32/enc_reshuffle.c @@ -0,0 +1,31 @@ +static BASE64_FORCE_INLINE uint8x16x4_t +enc_reshuffle (uint8x16x3_t in) +{ + uint8x16x4_t out; + + // Input: + // in[0] = a7 a6 a5 a4 a3 a2 a1 a0 + // in[1] = b7 b6 b5 b4 b3 b2 b1 b0 + // in[2] = c7 c6 c5 c4 c3 c2 c1 c0 + + // Output: + // out[0] = 00 00 a7 a6 a5 a4 a3 a2 + // out[1] = 00 00 a1 a0 b7 b6 b5 b4 + // out[2] = 00 00 b3 b2 b1 b0 c7 c6 + // out[3] = 00 00 c5 c4 c3 c2 c1 c0 + + // Move the input bits to where they need to be in the outputs. Except + // for the first output, the high two bits are not cleared. + out.val[0] = vshrq_n_u8(in.val[0], 2); + out.val[1] = vshrq_n_u8(in.val[1], 4); + out.val[2] = vshrq_n_u8(in.val[2], 6); + out.val[1] = vsliq_n_u8(out.val[1], in.val[0], 4); + out.val[2] = vsliq_n_u8(out.val[2], in.val[1], 2); + + // Clear the high two bits in the second, third and fourth output. + out.val[1] = vandq_u8(out.val[1], vdupq_n_u8(0x3F)); + out.val[2] = vandq_u8(out.val[2], vdupq_n_u8(0x3F)); + out.val[3] = vandq_u8(in.val[2], vdupq_n_u8(0x3F)); + + return out; +} diff --git a/mypyc/lib-rt/base64/arch/neon32/enc_translate.c b/mypyc/lib-rt/base64/arch/neon32/enc_translate.c new file mode 100644 index 0000000000000..ff3d88dd152ba --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon32/enc_translate.c @@ -0,0 +1,57 @@ +static BASE64_FORCE_INLINE uint8x16x4_t +enc_translate (const uint8x16x4_t in) +{ + // A lookup table containing the absolute offsets for all ranges: + const uint8x16_t lut = { + 65U, 71U, 252U, 252U, + 252U, 252U, 252U, 252U, + 252U, 252U, 252U, 252U, + 237U, 240U, 0U, 0U + }; + + const uint8x16_t offset = vdupq_n_u8(51); + + uint8x16x4_t indices, mask, delta, out; + + // Translate values 0..63 to the Base64 alphabet. There are five sets: + // # From To Abs Index Characters + // 0 [0..25] [65..90] +65 0 ABCDEFGHIJKLMNOPQRSTUVWXYZ + // 1 [26..51] [97..122] +71 1 abcdefghijklmnopqrstuvwxyz + // 2 [52..61] [48..57] -4 [2..11] 0123456789 + // 3 [62] [43] -19 12 + + // 4 [63] [47] -16 13 / + + // Create LUT indices from input: + // the index for range #0 is right, others are 1 less than expected: + indices.val[0] = vqsubq_u8(in.val[0], offset); + indices.val[1] = vqsubq_u8(in.val[1], offset); + indices.val[2] = vqsubq_u8(in.val[2], offset); + indices.val[3] = vqsubq_u8(in.val[3], offset); + + // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0: + mask.val[0] = vcgtq_u8(in.val[0], vdupq_n_u8(25)); + mask.val[1] = vcgtq_u8(in.val[1], vdupq_n_u8(25)); + mask.val[2] = vcgtq_u8(in.val[2], vdupq_n_u8(25)); + mask.val[3] = vcgtq_u8(in.val[3], vdupq_n_u8(25)); + + // Subtract -1, so add 1 to indices for range #[1..4], All indices are + // now correct: + indices.val[0] = vsubq_u8(indices.val[0], mask.val[0]); + indices.val[1] = vsubq_u8(indices.val[1], mask.val[1]); + indices.val[2] = vsubq_u8(indices.val[2], mask.val[2]); + indices.val[3] = vsubq_u8(indices.val[3], mask.val[3]); + + // Lookup delta values: + delta.val[0] = vqtbl1q_u8(lut, indices.val[0]); + delta.val[1] = vqtbl1q_u8(lut, indices.val[1]); + delta.val[2] = vqtbl1q_u8(lut, indices.val[2]); + delta.val[3] = vqtbl1q_u8(lut, indices.val[3]); + + // Add delta values: + out.val[0] = vaddq_u8(in.val[0], delta.val[0]); + out.val[1] = vaddq_u8(in.val[1], delta.val[1]); + out.val[2] = vaddq_u8(in.val[2], delta.val[2]); + out.val[3] = vaddq_u8(in.val[3], delta.val[3]); + + return out; +} diff --git a/mypyc/lib-rt/base64/arch/neon64/codec.c b/mypyc/lib-rt/base64/arch/neon64/codec.c new file mode 100644 index 0000000000000..70dc463de94f8 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon64/codec.c @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_NEON64 +#include + +// Only enable inline assembly on supported compilers. +#if defined(__GNUC__) || defined(__clang__) +#define BASE64_NEON64_USE_ASM +#endif + +static BASE64_FORCE_INLINE uint8x16x4_t +load_64byte_table (const uint8_t *p) +{ +#ifdef BASE64_NEON64_USE_ASM + + // Force the table to be loaded into contiguous registers. GCC will not + // normally allocate contiguous registers for a `uint8x16x4_t'. These + // registers are chosen to not conflict with the ones in the enc loop. + register uint8x16_t t0 __asm__ ("v8"); + register uint8x16_t t1 __asm__ ("v9"); + register uint8x16_t t2 __asm__ ("v10"); + register uint8x16_t t3 __asm__ ("v11"); + + __asm__ ( + "ld1 {%[t0].16b, %[t1].16b, %[t2].16b, %[t3].16b}, [%[src]], #64 \n\t" + : [src] "+r" (p), + [t0] "=w" (t0), + [t1] "=w" (t1), + [t2] "=w" (t2), + [t3] "=w" (t3) + ); + + return (uint8x16x4_t) { + .val[0] = t0, + .val[1] = t1, + .val[2] = t2, + .val[3] = t3, + }; +#else + return vld1q_u8_x4(p); +#endif +} + +#include "../generic/32/dec_loop.c" +#include "../generic/64/enc_loop.c" +#include "dec_loop.c" + +#ifdef BASE64_NEON64_USE_ASM +# include "enc_loop_asm.c" +#else +# include "enc_reshuffle.c" +# include "enc_loop.c" +#endif + +#endif // HAVE_NEON64 + +// Stride size is so large on these NEON 64-bit functions +// (48 bytes encode, 64 bytes decode) that we inline the +// uint64 codec to stay performant on smaller inputs. + +void +base64_stream_encode_neon64 BASE64_ENC_PARAMS +{ +#if HAVE_NEON64 + #include "../generic/enc_head.c" + enc_loop_neon64(&s, &slen, &o, &olen); + enc_loop_generic_64(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_neon64 BASE64_DEC_PARAMS +{ +#if HAVE_NEON64 + #include "../generic/dec_head.c" + dec_loop_neon64(&s, &slen, &o, &olen); + dec_loop_generic_32(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/neon64/dec_loop.c b/mypyc/lib-rt/base64/arch/neon64/dec_loop.c new file mode 100644 index 0000000000000..428e0651f8e04 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon64/dec_loop.c @@ -0,0 +1,129 @@ +// The input consists of five valid character sets in the Base64 alphabet, +// which we need to map back to the 6-bit values they represent. +// There are three ranges, two singles, and then there's the rest. +// +// # From To LUT Characters +// 1 [0..42] [255] #1 invalid input +// 2 [43] [62] #1 + +// 3 [44..46] [255] #1 invalid input +// 4 [47] [63] #1 / +// 5 [48..57] [52..61] #1 0..9 +// 6 [58..63] [255] #1 invalid input +// 7 [64] [255] #2 invalid input +// 8 [65..90] [0..25] #2 A..Z +// 9 [91..96] [255] #2 invalid input +// 10 [97..122] [26..51] #2 a..z +// 11 [123..126] [255] #2 invalid input +// (12) Everything else => invalid input + +// The first LUT will use the VTBL instruction (out of range indices are set to +// 0 in destination). +static const uint8_t dec_lut1[] = { + 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, + 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, + 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U, 62U, 255U, 255U, 255U, 63U, + 52U, 53U, 54U, 55U, 56U, 57U, 58U, 59U, 60U, 61U, 255U, 255U, 255U, 255U, 255U, 255U, +}; + +// The second LUT will use the VTBX instruction (out of range indices will be +// unchanged in destination). Input [64..126] will be mapped to index [1..63] +// in this LUT. Index 0 means that value comes from LUT #1. +static const uint8_t dec_lut2[] = { + 0U, 255U, 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, + 14U, 15U, 16U, 17U, 18U, 19U, 20U, 21U, 22U, 23U, 24U, 25U, 255U, 255U, 255U, 255U, + 255U, 255U, 26U, 27U, 28U, 29U, 30U, 31U, 32U, 33U, 34U, 35U, 36U, 37U, 38U, 39U, + 40U, 41U, 42U, 43U, 44U, 45U, 46U, 47U, 48U, 49U, 50U, 51U, 255U, 255U, 255U, 255U, +}; + +// All input values in range for the first look-up will be 0U in the second +// look-up result. All input values out of range for the first look-up will be +// 0U in the first look-up result. Thus, the two results can be ORed without +// conflicts. +// +// Invalid characters that are in the valid range for either look-up will be +// set to 255U in the combined result. Other invalid characters will just be +// passed through with the second look-up result (using the VTBX instruction). +// Since the second LUT is 64 bytes, those passed-through values are guaranteed +// to have a value greater than 63U. Therefore, valid characters will be mapped +// to the valid [0..63] range and all invalid characters will be mapped to +// values greater than 63. + +static inline void +dec_loop_neon64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 64) { + return; + } + + // Process blocks of 64 bytes per round. Unlike the SSE codecs, no + // extra trailing zero bytes are written, so it is not necessary to + // reserve extra input bytes: + size_t rounds = *slen / 64; + + *slen -= rounds * 64; // 64 bytes consumed per round + *olen += rounds * 48; // 48 bytes produced per round + + const uint8x16x4_t tbl_dec1 = load_64byte_table(dec_lut1); + const uint8x16x4_t tbl_dec2 = load_64byte_table(dec_lut2); + + do { + const uint8x16_t offset = vdupq_n_u8(63U); + uint8x16x4_t dec1, dec2; + uint8x16x3_t dec; + + // Load 64 bytes and deinterleave: + uint8x16x4_t str = vld4q_u8((uint8_t *) *s); + + // Get indices for second LUT: + dec2.val[0] = vqsubq_u8(str.val[0], offset); + dec2.val[1] = vqsubq_u8(str.val[1], offset); + dec2.val[2] = vqsubq_u8(str.val[2], offset); + dec2.val[3] = vqsubq_u8(str.val[3], offset); + + // Get values from first LUT: + dec1.val[0] = vqtbl4q_u8(tbl_dec1, str.val[0]); + dec1.val[1] = vqtbl4q_u8(tbl_dec1, str.val[1]); + dec1.val[2] = vqtbl4q_u8(tbl_dec1, str.val[2]); + dec1.val[3] = vqtbl4q_u8(tbl_dec1, str.val[3]); + + // Get values from second LUT: + dec2.val[0] = vqtbx4q_u8(dec2.val[0], tbl_dec2, dec2.val[0]); + dec2.val[1] = vqtbx4q_u8(dec2.val[1], tbl_dec2, dec2.val[1]); + dec2.val[2] = vqtbx4q_u8(dec2.val[2], tbl_dec2, dec2.val[2]); + dec2.val[3] = vqtbx4q_u8(dec2.val[3], tbl_dec2, dec2.val[3]); + + // Get final values: + str.val[0] = vorrq_u8(dec1.val[0], dec2.val[0]); + str.val[1] = vorrq_u8(dec1.val[1], dec2.val[1]); + str.val[2] = vorrq_u8(dec1.val[2], dec2.val[2]); + str.val[3] = vorrq_u8(dec1.val[3], dec2.val[3]); + + // Check for invalid input, any value larger than 63: + const uint8x16_t classified + = vorrq_u8( + vorrq_u8(vcgtq_u8(str.val[0], vdupq_n_u8(63)), vcgtq_u8(str.val[1], vdupq_n_u8(63))), + vorrq_u8(vcgtq_u8(str.val[2], vdupq_n_u8(63)), vcgtq_u8(str.val[3], vdupq_n_u8(63))) + ); + + // Check that all bits are zero: + if (vmaxvq_u8(classified) != 0U) { + break; + } + + // Compress four bytes into three: + dec.val[0] = vorrq_u8(vshlq_n_u8(str.val[0], 2), vshrq_n_u8(str.val[1], 4)); + dec.val[1] = vorrq_u8(vshlq_n_u8(str.val[1], 4), vshrq_n_u8(str.val[2], 2)); + dec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]); + + // Interleave and store decoded result: + vst3q_u8((uint8_t *) *o, dec); + + *s += 64; + *o += 48; + + } while (--rounds > 0); + + // Adjust for any rounds that were skipped: + *slen += rounds * 64; + *olen -= rounds * 48; +} diff --git a/mypyc/lib-rt/base64/arch/neon64/enc_loop.c b/mypyc/lib-rt/base64/arch/neon64/enc_loop.c new file mode 100644 index 0000000000000..8bdd088306532 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon64/enc_loop.c @@ -0,0 +1,66 @@ +static BASE64_FORCE_INLINE void +enc_loop_neon64_inner (const uint8_t **s, uint8_t **o, const uint8x16x4_t tbl_enc) +{ + // Load 48 bytes and deinterleave: + uint8x16x3_t src = vld3q_u8(*s); + + // Divide bits of three input bytes over four output bytes: + uint8x16x4_t out = enc_reshuffle(src); + + // The bits have now been shifted to the right locations; + // translate their values 0..63 to the Base64 alphabet. + // Use a 64-byte table lookup: + out.val[0] = vqtbl4q_u8(tbl_enc, out.val[0]); + out.val[1] = vqtbl4q_u8(tbl_enc, out.val[1]); + out.val[2] = vqtbl4q_u8(tbl_enc, out.val[2]); + out.val[3] = vqtbl4q_u8(tbl_enc, out.val[3]); + + // Interleave and store output: + vst4q_u8(*o, out); + + *s += 48; + *o += 64; +} + +static inline void +enc_loop_neon64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + size_t rounds = *slen / 48; + + *slen -= rounds * 48; // 48 bytes consumed per round + *olen += rounds * 64; // 64 bytes produced per round + + // Load the encoding table: + const uint8x16x4_t tbl_enc = load_64byte_table(base64_table_enc_6bit); + + while (rounds > 0) { + if (rounds >= 8) { + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_neon64_inner(s, o, tbl_enc); + enc_loop_neon64_inner(s, o, tbl_enc); + rounds -= 2; + continue; + } + enc_loop_neon64_inner(s, o, tbl_enc); + break; + } +} diff --git a/mypyc/lib-rt/base64/arch/neon64/enc_loop_asm.c b/mypyc/lib-rt/base64/arch/neon64/enc_loop_asm.c new file mode 100644 index 0000000000000..182e9cdf4a17d --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon64/enc_loop_asm.c @@ -0,0 +1,168 @@ +// Apologies in advance for combining the preprocessor with inline assembly, +// two notoriously gnarly parts of C, but it was necessary to avoid a lot of +// code repetition. The preprocessor is used to template large sections of +// inline assembly that differ only in the registers used. If the code was +// written out by hand, it would become very large and hard to audit. + +// Generate a block of inline assembly that loads three user-defined registers +// A, B, C from memory and deinterleaves them, post-incrementing the src +// pointer. The register set should be sequential. +#define LOAD(A, B, C) \ + "ld3 {"A".16b, "B".16b, "C".16b}, [%[src]], #48 \n\t" + +// Generate a block of inline assembly that takes three deinterleaved registers +// and shuffles the bytes. The output is in temporary registers t0..t3. +#define SHUF(A, B, C) \ + "ushr %[t0].16b, "A".16b, #2 \n\t" \ + "ushr %[t1].16b, "B".16b, #4 \n\t" \ + "ushr %[t2].16b, "C".16b, #6 \n\t" \ + "sli %[t1].16b, "A".16b, #4 \n\t" \ + "sli %[t2].16b, "B".16b, #2 \n\t" \ + "and %[t1].16b, %[t1].16b, %[n63].16b \n\t" \ + "and %[t2].16b, %[t2].16b, %[n63].16b \n\t" \ + "and %[t3].16b, "C".16b, %[n63].16b \n\t" + +// Generate a block of inline assembly that takes temporary registers t0..t3 +// and translates them to the base64 alphabet, using a table loaded into +// v8..v11. The output is in user-defined registers A..D. +#define TRAN(A, B, C, D) \ + "tbl "A".16b, {v8.16b-v11.16b}, %[t0].16b \n\t" \ + "tbl "B".16b, {v8.16b-v11.16b}, %[t1].16b \n\t" \ + "tbl "C".16b, {v8.16b-v11.16b}, %[t2].16b \n\t" \ + "tbl "D".16b, {v8.16b-v11.16b}, %[t3].16b \n\t" + +// Generate a block of inline assembly that interleaves four registers and +// stores them, post-incrementing the destination pointer. +#define STOR(A, B, C, D) \ + "st4 {"A".16b, "B".16b, "C".16b, "D".16b}, [%[dst]], #64 \n\t" + +// Generate a block of inline assembly that generates a single self-contained +// encoder round: fetch the data, process it, and store the result. +#define ROUND() \ + LOAD("v12", "v13", "v14") \ + SHUF("v12", "v13", "v14") \ + TRAN("v12", "v13", "v14", "v15") \ + STOR("v12", "v13", "v14", "v15") + +// Generate a block of assembly that generates a type A interleaved encoder +// round. It uses registers that were loaded by the previous type B round, and +// in turn loads registers for the next type B round. +#define ROUND_A() \ + SHUF("v2", "v3", "v4") \ + LOAD("v12", "v13", "v14") \ + TRAN("v2", "v3", "v4", "v5") \ + STOR("v2", "v3", "v4", "v5") + +// Type B interleaved encoder round. Same as type A, but register sets swapped. +#define ROUND_B() \ + SHUF("v12", "v13", "v14") \ + LOAD("v2", "v3", "v4") \ + TRAN("v12", "v13", "v14", "v15") \ + STOR("v12", "v13", "v14", "v15") + +// The first type A round needs to load its own registers. +#define ROUND_A_FIRST() \ + LOAD("v2", "v3", "v4") \ + ROUND_A() + +// The last type B round omits the load for the next step. +#define ROUND_B_LAST() \ + SHUF("v12", "v13", "v14") \ + TRAN("v12", "v13", "v14", "v15") \ + STOR("v12", "v13", "v14", "v15") + +// Suppress clang's warning that the literal string in the asm statement is +// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99 +// compilers). It may be true, but the goal here is not C99 portability. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" + +static inline void +enc_loop_neon64 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + size_t rounds = *slen / 48; + + if (rounds == 0) { + return; + } + + *slen -= rounds * 48; // 48 bytes consumed per round. + *olen += rounds * 64; // 64 bytes produced per round. + + // Number of times to go through the 8x loop. + size_t loops = rounds / 8; + + // Number of rounds remaining after the 8x loop. + rounds %= 8; + + // Temporary registers, used as scratch space. + uint8x16_t tmp0, tmp1, tmp2, tmp3; + + __asm__ volatile ( + + // Load the encoding table into v8..v11. + " ld1 {v8.16b-v11.16b}, [%[tbl]] \n\t" + + // If there are eight rounds or more, enter an 8x unrolled loop + // of interleaved encoding rounds. The rounds interleave memory + // operations (load/store) with data operations to maximize + // pipeline throughput. + " cbz %[loops], 4f \n\t" + + // The SIMD instructions do not touch the flags. + "88: subs %[loops], %[loops], #1 \n\t" + " " ROUND_A_FIRST() + " " ROUND_B() + " " ROUND_A() + " " ROUND_B() + " " ROUND_A() + " " ROUND_B() + " " ROUND_A() + " " ROUND_B_LAST() + " b.ne 88b \n\t" + + // Enter a 4x unrolled loop for rounds of 4 or more. + "4: cmp %[rounds], #4 \n\t" + " b.lt 30f \n\t" + " " ROUND_A_FIRST() + " " ROUND_B() + " " ROUND_A() + " " ROUND_B_LAST() + " sub %[rounds], %[rounds], #4 \n\t" + + // Dispatch the remaining rounds 0..3. + "30: cbz %[rounds], 0f \n\t" + " cmp %[rounds], #2 \n\t" + " b.eq 2f \n\t" + " b.lt 1f \n\t" + + // Block of non-interlaced encoding rounds, which can each + // individually be jumped to. Rounds fall through to the next. + "3: " ROUND() + "2: " ROUND() + "1: " ROUND() + "0: \n\t" + + // Outputs (modified). + : [loops] "+r" (loops), + [src] "+r" (*s), + [dst] "+r" (*o), + [t0] "=&w" (tmp0), + [t1] "=&w" (tmp1), + [t2] "=&w" (tmp2), + [t3] "=&w" (tmp3) + + // Inputs (not modified). + : [rounds] "r" (rounds), + [tbl] "r" (base64_table_enc_6bit), + [n63] "w" (vdupq_n_u8(63)) + + // Clobbers. + : "v2", "v3", "v4", "v5", + "v8", "v9", "v10", "v11", + "v12", "v13", "v14", "v15", + "cc", "memory" + ); +} + +#pragma GCC diagnostic pop diff --git a/mypyc/lib-rt/base64/arch/neon64/enc_reshuffle.c b/mypyc/lib-rt/base64/arch/neon64/enc_reshuffle.c new file mode 100644 index 0000000000000..2655df10f3eb3 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/neon64/enc_reshuffle.c @@ -0,0 +1,31 @@ +static BASE64_FORCE_INLINE uint8x16x4_t +enc_reshuffle (const uint8x16x3_t in) +{ + uint8x16x4_t out; + + // Input: + // in[0] = a7 a6 a5 a4 a3 a2 a1 a0 + // in[1] = b7 b6 b5 b4 b3 b2 b1 b0 + // in[2] = c7 c6 c5 c4 c3 c2 c1 c0 + + // Output: + // out[0] = 00 00 a7 a6 a5 a4 a3 a2 + // out[1] = 00 00 a1 a0 b7 b6 b5 b4 + // out[2] = 00 00 b3 b2 b1 b0 c7 c6 + // out[3] = 00 00 c5 c4 c3 c2 c1 c0 + + // Move the input bits to where they need to be in the outputs. Except + // for the first output, the high two bits are not cleared. + out.val[0] = vshrq_n_u8(in.val[0], 2); + out.val[1] = vshrq_n_u8(in.val[1], 4); + out.val[2] = vshrq_n_u8(in.val[2], 6); + out.val[1] = vsliq_n_u8(out.val[1], in.val[0], 4); + out.val[2] = vsliq_n_u8(out.val[2], in.val[1], 2); + + // Clear the high two bits in the second, third and fourth output. + out.val[1] = vandq_u8(out.val[1], vdupq_n_u8(0x3F)); + out.val[2] = vandq_u8(out.val[2], vdupq_n_u8(0x3F)); + out.val[3] = vandq_u8(in.val[2], vdupq_n_u8(0x3F)); + + return out; +} diff --git a/mypyc/lib-rt/base64/arch/sse41/codec.c b/mypyc/lib-rt/base64/arch/sse41/codec.c new file mode 100644 index 0000000000000..c627db5f726d4 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/sse41/codec.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_SSE41 +#include + +// Only enable inline assembly on supported compilers and on 64-bit CPUs. +#ifndef BASE64_SSE41_USE_ASM +# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64 +# define BASE64_SSE41_USE_ASM 1 +# else +# define BASE64_SSE41_USE_ASM 0 +# endif +#endif + +#include "../ssse3/dec_reshuffle.c" +#include "../ssse3/dec_loop.c" + +#if BASE64_SSE41_USE_ASM +# include "../ssse3/enc_loop_asm.c" +#else +# include "../ssse3/enc_translate.c" +# include "../ssse3/enc_reshuffle.c" +# include "../ssse3/enc_loop.c" +#endif + +#endif // HAVE_SSE41 + +void +base64_stream_encode_sse41 BASE64_ENC_PARAMS +{ +#if HAVE_SSE41 + #include "../generic/enc_head.c" + enc_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_sse41 BASE64_DEC_PARAMS +{ +#if HAVE_SSE41 + #include "../generic/dec_head.c" + dec_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/sse42/codec.c b/mypyc/lib-rt/base64/arch/sse42/codec.c new file mode 100644 index 0000000000000..2fe4e2997aa14 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/sse42/codec.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_SSE42 +#include + +// Only enable inline assembly on supported compilers and on 64-bit CPUs. +#ifndef BASE64_SSE42_USE_ASM +# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64 +# define BASE64_SSE42_USE_ASM 1 +# else +# define BASE64_SSE42_USE_ASM 0 +# endif +#endif + +#include "../ssse3/dec_reshuffle.c" +#include "../ssse3/dec_loop.c" + +#if BASE64_SSE42_USE_ASM +# include "../ssse3/enc_loop_asm.c" +#else +# include "../ssse3/enc_translate.c" +# include "../ssse3/enc_reshuffle.c" +# include "../ssse3/enc_loop.c" +#endif + +#endif // HAVE_SSE42 + +void +base64_stream_encode_sse42 BASE64_ENC_PARAMS +{ +#if HAVE_SSE42 + #include "../generic/enc_head.c" + enc_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_sse42 BASE64_DEC_PARAMS +{ +#if HAVE_SSE42 + #include "../generic/dec_head.c" + dec_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/ssse3/codec.c b/mypyc/lib-rt/base64/arch/ssse3/codec.c new file mode 100644 index 0000000000000..e51b3dfdb1677 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/codec.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "libbase64.h" +#include "../../tables/tables.h" +#include "../../codecs.h" +#include "config.h" +#include "../../env.h" + +#if HAVE_SSSE3 +#include + +// Only enable inline assembly on supported compilers and on 64-bit CPUs. +// 32-bit CPUs with SSSE3 support, such as low-end Atoms, only have eight XMM +// registers, which is not enough to run the inline assembly. +#ifndef BASE64_SSSE3_USE_ASM +# if (defined(__GNUC__) || defined(__clang__)) && BASE64_WORDSIZE == 64 +# define BASE64_SSSE3_USE_ASM 1 +# else +# define BASE64_SSSE3_USE_ASM 0 +# endif +#endif + +#include "dec_reshuffle.c" +#include "dec_loop.c" + +#if BASE64_SSSE3_USE_ASM +# include "enc_loop_asm.c" +#else +# include "enc_reshuffle.c" +# include "enc_translate.c" +# include "enc_loop.c" +#endif + +#endif // HAVE_SSSE3 + +void +base64_stream_encode_ssse3 BASE64_ENC_PARAMS +{ +#if HAVE_SSSE3 + #include "../generic/enc_head.c" + enc_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/enc_tail.c" +#else + base64_enc_stub(state, src, srclen, out, outlen); +#endif +} + +int +base64_stream_decode_ssse3 BASE64_DEC_PARAMS +{ +#if HAVE_SSSE3 + #include "../generic/dec_head.c" + dec_loop_ssse3(&s, &slen, &o, &olen); + #include "../generic/dec_tail.c" +#else + return base64_dec_stub(state, src, srclen, out, outlen); +#endif +} diff --git a/mypyc/lib-rt/base64/arch/ssse3/dec_loop.c b/mypyc/lib-rt/base64/arch/ssse3/dec_loop.c new file mode 100644 index 0000000000000..7ddb73bf88149 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/dec_loop.c @@ -0,0 +1,173 @@ +// The input consists of six character sets in the Base64 alphabet, which we +// need to map back to the 6-bit values they represent. There are three ranges, +// two singles, and then there's the rest. +// +// # From To Add Characters +// 1 [43] [62] +19 + +// 2 [47] [63] +16 / +// 3 [48..57] [52..61] +4 0..9 +// 4 [65..90] [0..25] -65 A..Z +// 5 [97..122] [26..51] -71 a..z +// (6) Everything else => invalid input +// +// We will use lookup tables for character validation and offset computation. +// Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8, this +// allows to mask with 0x2F instead of 0x0F and thus save one constant +// declaration (register and/or memory access). +// +// For offsets: +// Perfect hash for lut = ((src >> 4) & 0x2F) + ((src == 0x2F) ? 0xFF : 0x00) +// 0000 = garbage +// 0001 = / +// 0010 = + +// 0011 = 0-9 +// 0100 = A-Z +// 0101 = A-Z +// 0110 = a-z +// 0111 = a-z +// 1000 >= garbage +// +// For validation, here's the table. +// A character is valid if and only if the AND of the 2 lookups equals 0: +// +// hi \ lo 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 +// LUT 0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A +// +// 0000 0x10 char NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI +// andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// +// 0001 0x10 char DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US +// andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// +// 0010 0x01 char ! " # $ % & ' ( ) * + , - . / +// andlut 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00 +// +// 0011 0x02 char 0 1 2 3 4 5 6 7 8 9 : ; < = > ? +// andlut 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02 +// +// 0100 0x04 char @ A B C D E F G H I J K L M N O +// andlut 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +// +// 0101 0x08 char P Q R S T U V W X Y Z [ \ ] ^ _ +// andlut 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08 +// +// 0110 0x04 char ` a b c d e f g h i j k l m n o +// andlut 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +// 0111 0x08 char p q r s t u v w x y z { | } ~ +// andlut 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08 +// +// 1000 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1001 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1010 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1011 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1100 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1101 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1110 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 +// 1111 0x10 andlut 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + +static BASE64_FORCE_INLINE int +dec_loop_ssse3_inner (const uint8_t **s, uint8_t **o, size_t *rounds) +{ + const __m128i lut_lo = _mm_setr_epi8( + 0x15, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x13, 0x1A, 0x1B, 0x1B, 0x1B, 0x1A); + + const __m128i lut_hi = _mm_setr_epi8( + 0x10, 0x10, 0x01, 0x02, 0x04, 0x08, 0x04, 0x08, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10); + + const __m128i lut_roll = _mm_setr_epi8( + 0, 16, 19, 4, -65, -65, -71, -71, + 0, 0, 0, 0, 0, 0, 0, 0); + + const __m128i mask_2F = _mm_set1_epi8(0x2F); + + // Load input: + __m128i str = _mm_loadu_si128((__m128i *) *s); + + // Table lookups: + const __m128i hi_nibbles = _mm_and_si128(_mm_srli_epi32(str, 4), mask_2F); + const __m128i lo_nibbles = _mm_and_si128(str, mask_2F); + const __m128i hi = _mm_shuffle_epi8(lut_hi, hi_nibbles); + const __m128i lo = _mm_shuffle_epi8(lut_lo, lo_nibbles); + + // Check for invalid input: if any "and" values from lo and hi are not + // zero, fall back on bytewise code to do error checking and reporting: + if (_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_and_si128(lo, hi), _mm_setzero_si128())) != 0) { + return 0; + } + + const __m128i eq_2F = _mm_cmpeq_epi8(str, mask_2F); + const __m128i roll = _mm_shuffle_epi8(lut_roll, _mm_add_epi8(eq_2F, hi_nibbles)); + + // Now simply add the delta values to the input: + str = _mm_add_epi8(str, roll); + + // Reshuffle the input to packed 12-byte output format: + str = dec_reshuffle(str); + + // Store the output: + _mm_storeu_si128((__m128i *) *o, str); + + *s += 16; + *o += 12; + *rounds -= 1; + + return 1; +} + +static inline void +dec_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 24) { + return; + } + + // Process blocks of 16 bytes per round. Because 4 extra zero bytes are + // written after the output, ensure that there will be at least 8 bytes + // of input data left to cover the gap. (6 data bytes and up to two + // end-of-string markers.) + size_t rounds = (*slen - 8) / 16; + + *slen -= rounds * 16; // 16 bytes consumed per round + *olen += rounds * 12; // 12 bytes produced per round + + do { + if (rounds >= 8) { + if (dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds)) { + continue; + } + break; + } + if (rounds >= 4) { + if (dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds)) { + continue; + } + break; + } + if (rounds >= 2) { + if (dec_loop_ssse3_inner(s, o, &rounds) && + dec_loop_ssse3_inner(s, o, &rounds)) { + continue; + } + break; + } + dec_loop_ssse3_inner(s, o, &rounds); + break; + + } while (rounds > 0); + + // Adjust for any rounds that were skipped: + *slen += rounds * 16; + *olen -= rounds * 12; +} diff --git a/mypyc/lib-rt/base64/arch/ssse3/dec_reshuffle.c b/mypyc/lib-rt/base64/arch/ssse3/dec_reshuffle.c new file mode 100644 index 0000000000000..d3dd395427ae0 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/dec_reshuffle.c @@ -0,0 +1,33 @@ +static BASE64_FORCE_INLINE __m128i +dec_reshuffle (const __m128i in) +{ + // in, bits, upper case are most significant bits, lower case are least significant bits + // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ + // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG + // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD + // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA + + const __m128i merge_ab_and_bc = _mm_maddubs_epi16(in, _mm_set1_epi32(0x01400140)); + // 0000kkkk LLllllll 0000JJJJ JJjjKKKK + // 0000hhhh IIiiiiii 0000GGGG GGggHHHH + // 0000eeee FFffffff 0000DDDD DDddEEEE + // 0000bbbb CCcccccc 0000AAAA AAaaBBBB + + const __m128i out = _mm_madd_epi16(merge_ab_and_bc, _mm_set1_epi32(0x00011000)); + // 00000000 JJJJJJjj KKKKkkkk LLllllll + // 00000000 GGGGGGgg HHHHhhhh IIiiiiii + // 00000000 DDDDDDdd EEEEeeee FFffffff + // 00000000 AAAAAAaa BBBBbbbb CCcccccc + + // Pack bytes together: + return _mm_shuffle_epi8(out, _mm_setr_epi8( + 2, 1, 0, + 6, 5, 4, + 10, 9, 8, + 14, 13, 12, + -1, -1, -1, -1)); + // 00000000 00000000 00000000 00000000 + // LLllllll KKKKkkkk JJJJJJjj IIiiiiii + // HHHHhhhh GGGGGGgg FFffffff EEEEeeee + // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa +} diff --git a/mypyc/lib-rt/base64/arch/ssse3/enc_loop.c b/mypyc/lib-rt/base64/arch/ssse3/enc_loop.c new file mode 100644 index 0000000000000..9b67b70db1fd5 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/enc_loop.c @@ -0,0 +1,67 @@ +static BASE64_FORCE_INLINE void +enc_loop_ssse3_inner (const uint8_t **s, uint8_t **o) +{ + // Load input: + __m128i str = _mm_loadu_si128((__m128i *) *s); + + // Reshuffle: + str = enc_reshuffle(str); + + // Translate reshuffled bytes to the Base64 alphabet: + str = enc_translate(str); + + // Store: + _mm_storeu_si128((__m128i *) *o, str); + + *s += 12; + *o += 16; +} + +static inline void +enc_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 16) { + return; + } + + // Process blocks of 12 bytes at a time. Because blocks are loaded 16 + // bytes at a time, ensure that there will be at least 4 remaining + // bytes after the last round, so that the final read will not pass + // beyond the bounds of the input buffer: + size_t rounds = (*slen - 4) / 12; + + *slen -= rounds * 12; // 12 bytes consumed per round + *olen += rounds * 16; // 16 bytes produced per round + + do { + if (rounds >= 8) { + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + rounds -= 8; + continue; + } + if (rounds >= 4) { + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + rounds -= 4; + continue; + } + if (rounds >= 2) { + enc_loop_ssse3_inner(s, o); + enc_loop_ssse3_inner(s, o); + rounds -= 2; + continue; + } + enc_loop_ssse3_inner(s, o); + break; + + } while (rounds > 0); +} diff --git a/mypyc/lib-rt/base64/arch/ssse3/enc_loop_asm.c b/mypyc/lib-rt/base64/arch/ssse3/enc_loop_asm.c new file mode 100644 index 0000000000000..0cdb340a63b7f --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/enc_loop_asm.c @@ -0,0 +1,268 @@ +// Apologies in advance for combining the preprocessor with inline assembly, +// two notoriously gnarly parts of C, but it was necessary to avoid a lot of +// code repetition. The preprocessor is used to template large sections of +// inline assembly that differ only in the registers used. If the code was +// written out by hand, it would become very large and hard to audit. + +// Generate a block of inline assembly that loads register R0 from memory. The +// offset at which the register is loaded is set by the given round. +#define LOAD(R0, ROUND) \ + "lddqu ("#ROUND" * 12)(%[src]), %["R0"] \n\t" + +// Generate a block of inline assembly that deinterleaves and shuffles register +// R0 using preloaded constants. Outputs in R0 and R1. +#define SHUF(R0, R1) \ + "pshufb %[lut0], %["R0"] \n\t" \ + "movdqa %["R0"], %["R1"] \n\t" \ + "pand %[msk0], %["R0"] \n\t" \ + "pand %[msk2], %["R1"] \n\t" \ + "pmulhuw %[msk1], %["R0"] \n\t" \ + "pmullw %[msk3], %["R1"] \n\t" \ + "por %["R1"], %["R0"] \n\t" + +// Generate a block of inline assembly that takes R0 and R1 and translates +// their contents to the base64 alphabet, using preloaded constants. +#define TRAN(R0, R1, R2) \ + "movdqa %["R0"], %["R1"] \n\t" \ + "movdqa %["R0"], %["R2"] \n\t" \ + "psubusb %[n51], %["R1"] \n\t" \ + "pcmpgtb %[n25], %["R2"] \n\t" \ + "psubb %["R2"], %["R1"] \n\t" \ + "movdqa %[lut1], %["R2"] \n\t" \ + "pshufb %["R1"], %["R2"] \n\t" \ + "paddb %["R2"], %["R0"] \n\t" + +// Generate a block of inline assembly that stores the given register R0 at an +// offset set by the given round. +#define STOR(R0, ROUND) \ + "movdqu %["R0"], ("#ROUND" * 16)(%[dst]) \n\t" + +// Generate a block of inline assembly that generates a single self-contained +// encoder round: fetch the data, process it, and store the result. Then update +// the source and destination pointers. +#define ROUND() \ + LOAD("a", 0) \ + SHUF("a", "b") \ + TRAN("a", "b", "c") \ + STOR("a", 0) \ + "add $12, %[src] \n\t" \ + "add $16, %[dst] \n\t" + +// Define a macro that initiates a three-way interleaved encoding round by +// preloading registers a, b and c from memory. +// The register graph shows which registers are in use during each step, and +// is a visual aid for choosing registers for that step. Symbol index: +// +// + indicates that a register is loaded by that step. +// | indicates that a register is in use and must not be touched. +// - indicates that a register is decommissioned by that step. +// x indicates that a register is used as a temporary by that step. +// V indicates that a register is an input or output to the macro. +// +#define ROUND_3_INIT() /* a b c d e f */ \ + LOAD("a", 0) /* + */ \ + SHUF("a", "d") /* | + */ \ + LOAD("b", 1) /* | + | */ \ + TRAN("a", "d", "e") /* | | - x */ \ + LOAD("c", 2) /* V V V */ + +// Define a macro that translates, shuffles and stores the input registers A, B +// and C, and preloads registers D, E and F for the next round. +// This macro can be arbitrarily daisy-chained by feeding output registers D, E +// and F back into the next round as input registers A, B and C. The macro +// carefully interleaves memory operations with data operations for optimal +// pipelined performance. + +#define ROUND_3(ROUND, A,B,C,D,E,F) /* A B C D E F */ \ + LOAD(D, (ROUND + 3)) /* V V V + */ \ + SHUF(B, E) /* | | | | + */ \ + STOR(A, (ROUND + 0)) /* - | | | | */ \ + TRAN(B, E, F) /* | | | - x */ \ + LOAD(E, (ROUND + 4)) /* | | | + */ \ + SHUF(C, A) /* + | | | | */ \ + STOR(B, (ROUND + 1)) /* | - | | | */ \ + TRAN(C, A, F) /* - | | | x */ \ + LOAD(F, (ROUND + 5)) /* | | | + */ \ + SHUF(D, A) /* + | | | | */ \ + STOR(C, (ROUND + 2)) /* | - | | | */ \ + TRAN(D, A, B) /* - x V V V */ + +// Define a macro that terminates a ROUND_3 macro by taking pre-loaded +// registers D, E and F, and translating, shuffling and storing them. +#define ROUND_3_END(ROUND, A,B,C,D,E,F) /* A B C D E F */ \ + SHUF(E, A) /* + V V V */ \ + STOR(D, (ROUND + 3)) /* | - | | */ \ + TRAN(E, A, B) /* - x | | */ \ + SHUF(F, C) /* + | | */ \ + STOR(E, (ROUND + 4)) /* | - | */ \ + TRAN(F, C, D) /* - x | */ \ + STOR(F, (ROUND + 5)) /* - */ + +// Define a type A round. Inputs are a, b, and c, outputs are d, e, and f. +#define ROUND_3_A(ROUND) \ + ROUND_3(ROUND, "a", "b", "c", "d", "e", "f") + +// Define a type B round. Inputs and outputs are swapped with regard to type A. +#define ROUND_3_B(ROUND) \ + ROUND_3(ROUND, "d", "e", "f", "a", "b", "c") + +// Terminating macro for a type A round. +#define ROUND_3_A_LAST(ROUND) \ + ROUND_3_A(ROUND) \ + ROUND_3_END(ROUND, "a", "b", "c", "d", "e", "f") + +// Terminating macro for a type B round. +#define ROUND_3_B_LAST(ROUND) \ + ROUND_3_B(ROUND) \ + ROUND_3_END(ROUND, "d", "e", "f", "a", "b", "c") + +// Suppress clang's warning that the literal string in the asm statement is +// overlong (longer than the ISO-mandated minimum size of 4095 bytes for C99 +// compilers). It may be true, but the goal here is not C99 portability. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" + +static inline void +enc_loop_ssse3 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + // For a clearer explanation of the algorithm used by this function, + // please refer to the plain (not inline assembly) implementation. This + // function follows the same basic logic. + + if (*slen < 16) { + return; + } + + // Process blocks of 12 bytes at a time. Input is read in blocks of 16 + // bytes, so "reserve" four bytes from the input buffer to ensure that + // we never read beyond the end of the input buffer. + size_t rounds = (*slen - 4) / 12; + + *slen -= rounds * 12; // 12 bytes consumed per round + *olen += rounds * 16; // 16 bytes produced per round + + // Number of times to go through the 36x loop. + size_t loops = rounds / 36; + + // Number of rounds remaining after the 36x loop. + rounds %= 36; + + // Lookup tables. + const __m128i lut0 = _mm_set_epi8( + 10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1); + + const __m128i lut1 = _mm_setr_epi8( + 65, 71, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -19, -16, 0, 0); + + // Temporary registers. + __m128i a, b, c, d, e, f; + + __asm__ volatile ( + + // If there are 36 rounds or more, enter a 36x unrolled loop of + // interleaved encoding rounds. The rounds interleave memory + // operations (load/store) with data operations (table lookups, + // etc) to maximize pipeline throughput. + " test %[loops], %[loops] \n\t" + " jz 18f \n\t" + " jmp 36f \n\t" + " \n\t" + ".balign 64 \n\t" + "36: " ROUND_3_INIT() + " " ROUND_3_A( 0) + " " ROUND_3_B( 3) + " " ROUND_3_A( 6) + " " ROUND_3_B( 9) + " " ROUND_3_A(12) + " " ROUND_3_B(15) + " " ROUND_3_A(18) + " " ROUND_3_B(21) + " " ROUND_3_A(24) + " " ROUND_3_B(27) + " " ROUND_3_A_LAST(30) + " add $(12 * 36), %[src] \n\t" + " add $(16 * 36), %[dst] \n\t" + " dec %[loops] \n\t" + " jnz 36b \n\t" + + // Enter an 18x unrolled loop for rounds of 18 or more. + "18: cmp $18, %[rounds] \n\t" + " jl 9f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A(0) + " " ROUND_3_B(3) + " " ROUND_3_A(6) + " " ROUND_3_B(9) + " " ROUND_3_A_LAST(12) + " sub $18, %[rounds] \n\t" + " add $(12 * 18), %[src] \n\t" + " add $(16 * 18), %[dst] \n\t" + + // Enter a 9x unrolled loop for rounds of 9 or more. + "9: cmp $9, %[rounds] \n\t" + " jl 6f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A(0) + " " ROUND_3_B_LAST(3) + " sub $9, %[rounds] \n\t" + " add $(12 * 9), %[src] \n\t" + " add $(16 * 9), %[dst] \n\t" + + // Enter a 6x unrolled loop for rounds of 6 or more. + "6: cmp $6, %[rounds] \n\t" + " jl 55f \n\t" + " " ROUND_3_INIT() + " " ROUND_3_A_LAST(0) + " sub $6, %[rounds] \n\t" + " add $(12 * 6), %[src] \n\t" + " add $(16 * 6), %[dst] \n\t" + + // Dispatch the remaining rounds 0..5. + "55: cmp $3, %[rounds] \n\t" + " jg 45f \n\t" + " je 3f \n\t" + " cmp $1, %[rounds] \n\t" + " jg 2f \n\t" + " je 1f \n\t" + " jmp 0f \n\t" + + "45: cmp $4, %[rounds] \n\t" + " je 4f \n\t" + + // Block of non-interlaced encoding rounds, which can each + // individually be jumped to. Rounds fall through to the next. + "5: " ROUND() + "4: " ROUND() + "3: " ROUND() + "2: " ROUND() + "1: " ROUND() + "0: \n\t" + + // Outputs (modified). + : [rounds] "+r" (rounds), + [loops] "+r" (loops), + [src] "+r" (*s), + [dst] "+r" (*o), + [a] "=&x" (a), + [b] "=&x" (b), + [c] "=&x" (c), + [d] "=&x" (d), + [e] "=&x" (e), + [f] "=&x" (f) + + // Inputs (not modified). + : [lut0] "x" (lut0), + [lut1] "x" (lut1), + [msk0] "x" (_mm_set1_epi32(0x0FC0FC00)), + [msk1] "x" (_mm_set1_epi32(0x04000040)), + [msk2] "x" (_mm_set1_epi32(0x003F03F0)), + [msk3] "x" (_mm_set1_epi32(0x01000010)), + [n51] "x" (_mm_set1_epi8(51)), + [n25] "x" (_mm_set1_epi8(25)) + + // Clobbers. + : "cc", "memory" + ); +} + +#pragma GCC diagnostic pop diff --git a/mypyc/lib-rt/base64/arch/ssse3/enc_reshuffle.c b/mypyc/lib-rt/base64/arch/ssse3/enc_reshuffle.c new file mode 100644 index 0000000000000..f9dc949f255dc --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/enc_reshuffle.c @@ -0,0 +1,48 @@ +static BASE64_FORCE_INLINE __m128i +enc_reshuffle (__m128i in) +{ + // Input, bytes MSB to LSB: + // 0 0 0 0 l k j i h g f e d c b a + + in = _mm_shuffle_epi8(in, _mm_set_epi8( + 10, 11, 9, 10, + 7, 8, 6, 7, + 4, 5, 3, 4, + 1, 2, 0, 1)); + // in, bytes MSB to LSB: + // k l j k + // h i g h + // e f d e + // b c a b + + const __m128i t0 = _mm_and_si128(in, _mm_set1_epi32(0x0FC0FC00)); + // bits, upper case are most significant bits, lower case are least significant bits + // 0000kkkk LL000000 JJJJJJ00 00000000 + // 0000hhhh II000000 GGGGGG00 00000000 + // 0000eeee FF000000 DDDDDD00 00000000 + // 0000bbbb CC000000 AAAAAA00 00000000 + + const __m128i t1 = _mm_mulhi_epu16(t0, _mm_set1_epi32(0x04000040)); + // 00000000 00kkkkLL 00000000 00JJJJJJ + // 00000000 00hhhhII 00000000 00GGGGGG + // 00000000 00eeeeFF 00000000 00DDDDDD + // 00000000 00bbbbCC 00000000 00AAAAAA + + const __m128i t2 = _mm_and_si128(in, _mm_set1_epi32(0x003F03F0)); + // 00000000 00llllll 000000jj KKKK0000 + // 00000000 00iiiiii 000000gg HHHH0000 + // 00000000 00ffffff 000000dd EEEE0000 + // 00000000 00cccccc 000000aa BBBB0000 + + const __m128i t3 = _mm_mullo_epi16(t2, _mm_set1_epi32(0x01000010)); + // 00llllll 00000000 00jjKKKK 00000000 + // 00iiiiii 00000000 00ggHHHH 00000000 + // 00ffffff 00000000 00ddEEEE 00000000 + // 00cccccc 00000000 00aaBBBB 00000000 + + return _mm_or_si128(t1, t3); + // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ + // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG + // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD + // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA +} diff --git a/mypyc/lib-rt/base64/arch/ssse3/enc_translate.c b/mypyc/lib-rt/base64/arch/ssse3/enc_translate.c new file mode 100644 index 0000000000000..60d9a42b8a303 --- /dev/null +++ b/mypyc/lib-rt/base64/arch/ssse3/enc_translate.c @@ -0,0 +1,33 @@ +static BASE64_FORCE_INLINE __m128i +enc_translate (const __m128i in) +{ + // A lookup table containing the absolute offsets for all ranges: + const __m128i lut = _mm_setr_epi8( + 65, 71, -4, -4, + -4, -4, -4, -4, + -4, -4, -4, -4, + -19, -16, 0, 0 + ); + + // Translate values 0..63 to the Base64 alphabet. There are five sets: + // # From To Abs Index Characters + // 0 [0..25] [65..90] +65 0 ABCDEFGHIJKLMNOPQRSTUVWXYZ + // 1 [26..51] [97..122] +71 1 abcdefghijklmnopqrstuvwxyz + // 2 [52..61] [48..57] -4 [2..11] 0123456789 + // 3 [62] [43] -19 12 + + // 4 [63] [47] -16 13 / + + // Create LUT indices from the input. The index for range #0 is right, + // others are 1 less than expected: + __m128i indices = _mm_subs_epu8(in, _mm_set1_epi8(51)); + + // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0: + __m128i mask = _mm_cmpgt_epi8(in, _mm_set1_epi8(25)); + + // Subtract -1, so add 1 to indices for range #[1..4]. All indices are + // now correct: + indices = _mm_sub_epi8(indices, mask); + + // Add offsets to input values: + return _mm_add_epi8(in, _mm_shuffle_epi8(lut, indices)); +} diff --git a/mypyc/lib-rt/base64/codec_choose.c b/mypyc/lib-rt/base64/codec_choose.c new file mode 100644 index 0000000000000..74b0aac7b2d29 --- /dev/null +++ b/mypyc/lib-rt/base64/codec_choose.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include +#include + +#include "libbase64.h" +#include "codecs.h" +#include "config.h" +#include "env.h" + +#if (__x86_64__ || __i386__ || _M_X86 || _M_X64) + #define BASE64_X86 + #if (HAVE_SSSE3 || HAVE_SSE41 || HAVE_SSE42 || HAVE_AVX || HAVE_AVX2 || HAVE_AVX512) + #define BASE64_X86_SIMD + #endif +#endif + +#ifdef BASE64_X86 +#ifdef _MSC_VER + #include + #define __cpuid_count(__level, __count, __eax, __ebx, __ecx, __edx) \ + { \ + int info[4]; \ + __cpuidex(info, __level, __count); \ + __eax = info[0]; \ + __ebx = info[1]; \ + __ecx = info[2]; \ + __edx = info[3]; \ + } + #define __cpuid(__level, __eax, __ebx, __ecx, __edx) \ + __cpuid_count(__level, 0, __eax, __ebx, __ecx, __edx) +#else + #include + #if HAVE_AVX512 || HAVE_AVX2 || HAVE_AVX + #if ((__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 2) || (__clang_major__ >= 3)) + static inline uint64_t _xgetbv (uint32_t index) + { + uint32_t eax, edx; + __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); + return ((uint64_t)edx << 32) | eax; + } + #else + #error "Platform not supported" + #endif + #endif +#endif + +#ifndef bit_AVX512vl +#define bit_AVX512vl (1 << 31) +#endif +#ifndef bit_AVX512vbmi +#define bit_AVX512vbmi (1 << 1) +#endif +#ifndef bit_AVX2 +#define bit_AVX2 (1 << 5) +#endif +#ifndef bit_SSSE3 +#define bit_SSSE3 (1 << 9) +#endif +#ifndef bit_SSE41 +#define bit_SSE41 (1 << 19) +#endif +#ifndef bit_SSE42 +#define bit_SSE42 (1 << 20) +#endif +#ifndef bit_AVX +#define bit_AVX (1 << 28) +#endif + +#define bit_XSAVE_XRSTORE (1 << 27) + +#ifndef _XCR_XFEATURE_ENABLED_MASK +#define _XCR_XFEATURE_ENABLED_MASK 0 +#endif + +#define bit_XMM (1 << 1) +#define bit_YMM (1 << 2) +#define bit_OPMASK (1 << 5) +#define bit_ZMM (1 << 6) +#define bit_HIGH_ZMM (1 << 7) + +#define _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS (bit_XMM | bit_YMM) + +#define _AVX_512_ENABLED_BY_OS (bit_XMM | bit_YMM | bit_OPMASK | bit_ZMM | bit_HIGH_ZMM) + +#endif + +// Function declarations: +#define BASE64_CODEC_FUNCS(arch) \ + extern void base64_stream_encode_ ## arch BASE64_ENC_PARAMS; \ + extern int base64_stream_decode_ ## arch BASE64_DEC_PARAMS; + +BASE64_CODEC_FUNCS(avx512) +BASE64_CODEC_FUNCS(avx2) +BASE64_CODEC_FUNCS(neon32) +BASE64_CODEC_FUNCS(neon64) +BASE64_CODEC_FUNCS(plain) +BASE64_CODEC_FUNCS(ssse3) +BASE64_CODEC_FUNCS(sse41) +BASE64_CODEC_FUNCS(sse42) +BASE64_CODEC_FUNCS(avx) + +static bool +codec_choose_forced (struct codec *codec, int flags) +{ + // If the user wants to use a certain codec, + // always allow it, even if the codec is a no-op. + // For testing purposes. + + if (!(flags & 0xFFFF)) { + return false; + } + + if (flags & BASE64_FORCE_AVX2) { + codec->enc = base64_stream_encode_avx2; + codec->dec = base64_stream_decode_avx2; + return true; + } + if (flags & BASE64_FORCE_NEON32) { + codec->enc = base64_stream_encode_neon32; + codec->dec = base64_stream_decode_neon32; + return true; + } + if (flags & BASE64_FORCE_NEON64) { + codec->enc = base64_stream_encode_neon64; + codec->dec = base64_stream_decode_neon64; + return true; + } + if (flags & BASE64_FORCE_PLAIN) { + codec->enc = base64_stream_encode_plain; + codec->dec = base64_stream_decode_plain; + return true; + } + if (flags & BASE64_FORCE_SSSE3) { + codec->enc = base64_stream_encode_ssse3; + codec->dec = base64_stream_decode_ssse3; + return true; + } + if (flags & BASE64_FORCE_SSE41) { + codec->enc = base64_stream_encode_sse41; + codec->dec = base64_stream_decode_sse41; + return true; + } + if (flags & BASE64_FORCE_SSE42) { + codec->enc = base64_stream_encode_sse42; + codec->dec = base64_stream_decode_sse42; + return true; + } + if (flags & BASE64_FORCE_AVX) { + codec->enc = base64_stream_encode_avx; + codec->dec = base64_stream_decode_avx; + return true; + } + if (flags & BASE64_FORCE_AVX512) { + codec->enc = base64_stream_encode_avx512; + codec->dec = base64_stream_decode_avx512; + return true; + } + return false; +} + +static bool +codec_choose_arm (struct codec *codec) +{ +#if HAVE_NEON64 || ((defined(__ARM_NEON__) || defined(__ARM_NEON)) && HAVE_NEON32) + + // Unfortunately there is no portable way to check for NEON + // support at runtime from userland in the same way that x86 + // has cpuid, so just stick to the compile-time configuration: + + #if HAVE_NEON64 + codec->enc = base64_stream_encode_neon64; + codec->dec = base64_stream_decode_neon64; + #else + codec->enc = base64_stream_encode_neon32; + codec->dec = base64_stream_decode_neon32; + #endif + + return true; + +#else + (void)codec; + return false; +#endif +} + +static bool +codec_choose_x86 (struct codec *codec) +{ +#ifdef BASE64_X86_SIMD + + unsigned int eax, ebx = 0, ecx = 0, edx; + unsigned int max_level; + + #ifdef _MSC_VER + int info[4]; + __cpuidex(info, 0, 0); + max_level = info[0]; + #else + max_level = __get_cpuid_max(0, NULL); + #endif + + #if HAVE_AVX512 || HAVE_AVX2 || HAVE_AVX + // Check for AVX/AVX2/AVX512 support: + // Checking for AVX requires 3 things: + // 1) CPUID indicates that the OS uses XSAVE and XRSTORE instructions + // (allowing saving YMM registers on context switch) + // 2) CPUID indicates support for AVX + // 3) XGETBV indicates the AVX registers will be saved and restored on + // context switch + // + // Note that XGETBV is only available on 686 or later CPUs, so the + // instruction needs to be conditionally run. + if (max_level >= 1) { + __cpuid_count(1, 0, eax, ebx, ecx, edx); + if (ecx & bit_XSAVE_XRSTORE) { + uint64_t xcr_mask; + xcr_mask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK); + if ((xcr_mask & _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS) == _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS) { // check multiple bits at once + #if HAVE_AVX512 + if (max_level >= 7 && ((xcr_mask & _AVX_512_ENABLED_BY_OS) == _AVX_512_ENABLED_BY_OS)) { + __cpuid_count(7, 0, eax, ebx, ecx, edx); + if ((ebx & bit_AVX512vl) && (ecx & bit_AVX512vbmi)) { + codec->enc = base64_stream_encode_avx512; + codec->dec = base64_stream_decode_avx512; + return true; + } + } + #endif + #if HAVE_AVX2 + if (max_level >= 7) { + __cpuid_count(7, 0, eax, ebx, ecx, edx); + if (ebx & bit_AVX2) { + codec->enc = base64_stream_encode_avx2; + codec->dec = base64_stream_decode_avx2; + return true; + } + } + #endif + #if HAVE_AVX + __cpuid_count(1, 0, eax, ebx, ecx, edx); + if (ecx & bit_AVX) { + codec->enc = base64_stream_encode_avx; + codec->dec = base64_stream_decode_avx; + return true; + } + #endif + } + } + } + #endif + + #if HAVE_SSE42 + // Check for SSE42 support: + if (max_level >= 1) { + __cpuid(1, eax, ebx, ecx, edx); + if (ecx & bit_SSE42) { + codec->enc = base64_stream_encode_sse42; + codec->dec = base64_stream_decode_sse42; + return true; + } + } + #endif + + #if HAVE_SSE41 + // Check for SSE41 support: + if (max_level >= 1) { + __cpuid(1, eax, ebx, ecx, edx); + if (ecx & bit_SSE41) { + codec->enc = base64_stream_encode_sse41; + codec->dec = base64_stream_decode_sse41; + return true; + } + } + #endif + + #if HAVE_SSSE3 + // Check for SSSE3 support: + if (max_level >= 1) { + __cpuid(1, eax, ebx, ecx, edx); + if (ecx & bit_SSSE3) { + codec->enc = base64_stream_encode_ssse3; + codec->dec = base64_stream_decode_ssse3; + return true; + } + } + #endif + +#else + (void)codec; +#endif + + return false; +} + +void +codec_choose (struct codec *codec, int flags) +{ + // User forced a codec: + if (codec_choose_forced(codec, flags)) { + return; + } + + // Runtime feature detection: + if (codec_choose_arm(codec)) { + return; + } + if (codec_choose_x86(codec)) { + return; + } + codec->enc = base64_stream_encode_plain; + codec->dec = base64_stream_decode_plain; +} diff --git a/mypyc/lib-rt/base64/codecs.h b/mypyc/lib-rt/base64/codecs.h new file mode 100644 index 0000000000000..34d54dc8fde94 --- /dev/null +++ b/mypyc/lib-rt/base64/codecs.h @@ -0,0 +1,57 @@ +#include "libbase64.h" + +// Function parameters for encoding functions: +#define BASE64_ENC_PARAMS \ + ( struct base64_state *state \ + , const char *src \ + , size_t srclen \ + , char *out \ + , size_t *outlen \ + ) + +// Function parameters for decoding functions: +#define BASE64_DEC_PARAMS \ + ( struct base64_state *state \ + , const char *src \ + , size_t srclen \ + , char *out \ + , size_t *outlen \ + ) + +// This function is used as a stub when a certain encoder is not compiled in. +// It discards the inputs and returns zero output bytes. +static inline void +base64_enc_stub BASE64_ENC_PARAMS +{ + (void) state; + (void) src; + (void) srclen; + (void) out; + + *outlen = 0; +} + +// This function is used as a stub when a certain decoder is not compiled in. +// It discards the inputs and returns an invalid decoding result. +static inline int +base64_dec_stub BASE64_DEC_PARAMS +{ + (void) state; + (void) src; + (void) srclen; + (void) out; + (void) outlen; + + return -1; +} + +typedef void (* base64_enc_fn) BASE64_ENC_PARAMS; +typedef int (* base64_dec_fn) BASE64_DEC_PARAMS; + +struct codec +{ + base64_enc_fn enc; + base64_dec_fn dec; +}; + +extern void codec_choose (struct codec *, int flags); diff --git a/mypyc/lib-rt/base64/config.h b/mypyc/lib-rt/base64/config.h new file mode 100644 index 0000000000000..467a722c2f117 --- /dev/null +++ b/mypyc/lib-rt/base64/config.h @@ -0,0 +1,24 @@ +#ifndef BASE64_CONFIG_H +#define BASE64_CONFIG_H + +#if !defined(__APPLE__) && ((defined(__x86_64__) && defined(__LP64__)) || defined(_M_X64)) + #define HAVE_SSSE3 1 + #define HAVE_SSE41 1 + #define HAVE_SSE42 1 + #define HAVE_AVX 1 + #define HAVE_AVX2 1 + #define HAVE_AVX512 0 +#endif + +#define BASE64_WITH_NEON32 0 +#define HAVE_NEON32 BASE64_WITH_NEON32 + +#if defined(__APPLE__) && defined(__aarch64__) +#define BASE64_WITH_NEON64 1 +#else +#define BASE64_WITH_NEON64 0 +#endif + +#define HAVE_NEON64 BASE64_WITH_NEON64 + +#endif // BASE64_CONFIG_H diff --git a/mypyc/lib-rt/base64/env.h b/mypyc/lib-rt/base64/env.h new file mode 100644 index 0000000000000..083706507900b --- /dev/null +++ b/mypyc/lib-rt/base64/env.h @@ -0,0 +1,84 @@ +#ifndef BASE64_ENV_H +#define BASE64_ENV_H + +#include + +// This header file contains macro definitions that describe certain aspects of +// the compile-time environment. Compatibility and portability macros go here. + +// Define machine endianness. This is for GCC: +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define BASE64_LITTLE_ENDIAN 1 +#else +# define BASE64_LITTLE_ENDIAN 0 +#endif + +// This is for Clang: +#ifdef __LITTLE_ENDIAN__ +# define BASE64_LITTLE_ENDIAN 1 +#endif + +#ifdef __BIG_ENDIAN__ +# define BASE64_LITTLE_ENDIAN 0 +#endif + +// MSVC++ needs intrin.h for _byteswap_uint64 (issue #68): +#if BASE64_LITTLE_ENDIAN && defined(_MSC_VER) +# include +#endif + +// Endian conversion functions: +#if BASE64_LITTLE_ENDIAN +# ifdef _MSC_VER +// Microsoft Visual C++: +# define BASE64_HTOBE32(x) _byteswap_ulong(x) +# define BASE64_HTOBE64(x) _byteswap_uint64(x) +# else +// GCC and Clang: +# define BASE64_HTOBE32(x) __builtin_bswap32(x) +# define BASE64_HTOBE64(x) __builtin_bswap64(x) +# endif +#else +// No conversion needed: +# define BASE64_HTOBE32(x) (x) +# define BASE64_HTOBE64(x) (x) +#endif + +// Detect word size: +#if defined (__x86_64__) +// This also works for the x32 ABI, which has a 64-bit word size. +# define BASE64_WORDSIZE 64 +#elif SIZE_MAX == UINT32_MAX +# define BASE64_WORDSIZE 32 +#elif SIZE_MAX == UINT64_MAX +# define BASE64_WORDSIZE 64 +#else +# error BASE64_WORDSIZE_NOT_DEFINED +#endif + +// End-of-file definitions. +// Almost end-of-file when waiting for the last '=' character: +#define BASE64_AEOF 1 +// End-of-file when stream end has been reached or invalid input provided: +#define BASE64_EOF 2 + +// GCC 7 defaults to issuing a warning for fallthrough in switch statements, +// unless the fallthrough cases are marked with an attribute. As we use +// fallthrough deliberately, define an alias for the attribute: +#if __GNUC__ >= 7 +# define BASE64_FALLTHROUGH __attribute__((fallthrough)); +#else +# define BASE64_FALLTHROUGH +#endif + +// Declare macros to ensure that functions that are intended to be inlined, are +// actually inlined, even when no optimization is applied. A lot of inner loop +// code is factored into separate functions for reasons of readability, but +// that code should always be inlined (and optimized) in the main loop. +#ifdef _MSC_VER +# define BASE64_FORCE_INLINE __forceinline +#else +# define BASE64_FORCE_INLINE inline __attribute__((always_inline)) +#endif + +#endif // BASE64_ENV_H diff --git a/mypyc/lib-rt/base64/lib.c b/mypyc/lib-rt/base64/lib.c new file mode 100644 index 0000000000000..0f24d52e99912 --- /dev/null +++ b/mypyc/lib-rt/base64/lib.c @@ -0,0 +1,164 @@ +#include +#include +#ifdef _OPENMP +#include +#endif + +#include "libbase64.h" +#include "tables/tables.h" +#include "codecs.h" +#include "env.h" + +// These static function pointers are initialized once when the library is +// first used, and remain in use for the remaining lifetime of the program. +// The idea being that CPU features don't change at runtime. +static struct codec codec = { NULL, NULL }; + +void +base64_stream_encode_init (struct base64_state *state, int flags) +{ + // If any of the codec flags are set, redo choice: + if (codec.enc == NULL || flags & 0xFF) { + codec_choose(&codec, flags); + } + state->eof = 0; + state->bytes = 0; + state->carry = 0; + state->flags = flags; +} + +void +base64_stream_encode + ( struct base64_state *state + , const char *src + , size_t srclen + , char *out + , size_t *outlen + ) +{ + codec.enc(state, src, srclen, out, outlen); +} + +void +base64_stream_encode_final + ( struct base64_state *state + , char *out + , size_t *outlen + ) +{ + uint8_t *o = (uint8_t *)out; + + if (state->bytes == 1) { + *o++ = base64_table_enc_6bit[state->carry]; + *o++ = '='; + *o++ = '='; + *outlen = 3; + return; + } + if (state->bytes == 2) { + *o++ = base64_table_enc_6bit[state->carry]; + *o++ = '='; + *outlen = 2; + return; + } + *outlen = 0; +} + +void +base64_stream_decode_init (struct base64_state *state, int flags) +{ + // If any of the codec flags are set, redo choice: + if (codec.dec == NULL || flags & 0xFFFF) { + codec_choose(&codec, flags); + } + state->eof = 0; + state->bytes = 0; + state->carry = 0; + state->flags = flags; +} + +int +base64_stream_decode + ( struct base64_state *state + , const char *src + , size_t srclen + , char *out + , size_t *outlen + ) +{ + return codec.dec(state, src, srclen, out, outlen); +} + +#ifdef _OPENMP + + // Due to the overhead of initializing OpenMP and creating a team of + // threads, we require the data length to be larger than a threshold: + #define OMP_THRESHOLD 20000 + + // Conditionally include OpenMP-accelerated codec implementations: + #include "lib_openmp.c" +#endif + +void +base64_encode + ( const char *src + , size_t srclen + , char *out + , size_t *outlen + , int flags + ) +{ + size_t s; + size_t t; + struct base64_state state; + + #ifdef _OPENMP + if (srclen >= OMP_THRESHOLD) { + base64_encode_openmp(src, srclen, out, outlen, flags); + return; + } + #endif + + // Init the stream reader: + base64_stream_encode_init(&state, flags); + + // Feed the whole string to the stream reader: + base64_stream_encode(&state, src, srclen, out, &s); + + // Finalize the stream by writing trailer if any: + base64_stream_encode_final(&state, out + s, &t); + + // Final output length is stream length plus tail: + *outlen = s + t; +} + +int +base64_decode + ( const char *src + , size_t srclen + , char *out + , size_t *outlen + , int flags + ) +{ + int ret; + struct base64_state state; + + #ifdef _OPENMP + if (srclen >= OMP_THRESHOLD) { + return base64_decode_openmp(src, srclen, out, outlen, flags); + } + #endif + + // Init the stream reader: + base64_stream_decode_init(&state, flags); + + // Feed the whole string to the stream reader: + ret = base64_stream_decode(&state, src, srclen, out, outlen); + + // If when decoding a whole block, we're still waiting for input then fail: + if (ret && (state.bytes == 0)) { + return ret; + } + return 0; +} diff --git a/mypyc/lib-rt/base64/libbase64.h b/mypyc/lib-rt/base64/libbase64.h new file mode 100644 index 0000000000000..c5908973c5e73 --- /dev/null +++ b/mypyc/lib-rt/base64/libbase64.h @@ -0,0 +1,146 @@ +#ifndef LIBBASE64_H +#define LIBBASE64_H + +#include /* size_t */ + + +#if defined(_WIN32) || defined(__CYGWIN__) +#define BASE64_SYMBOL_IMPORT __declspec(dllimport) +#define BASE64_SYMBOL_EXPORT __declspec(dllexport) +#define BASE64_SYMBOL_PRIVATE + +#elif __GNUC__ >= 4 +#define BASE64_SYMBOL_IMPORT __attribute__ ((visibility ("default"))) +#define BASE64_SYMBOL_EXPORT __attribute__ ((visibility ("default"))) +#define BASE64_SYMBOL_PRIVATE __attribute__ ((visibility ("hidden"))) + +#else +#define BASE64_SYMBOL_IMPORT +#define BASE64_SYMBOL_EXPORT +#define BASE64_SYMBOL_PRIVATE +#endif + +#if defined(BASE64_STATIC_DEFINE) +#define BASE64_EXPORT +#define BASE64_NO_EXPORT + +#else +#if defined(BASE64_EXPORTS) // defined if we are building the shared library +#define BASE64_EXPORT BASE64_SYMBOL_EXPORT + +#else +#define BASE64_EXPORT BASE64_SYMBOL_IMPORT +#endif + +#define BASE64_NO_EXPORT BASE64_SYMBOL_PRIVATE +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* These are the flags that can be passed in the `flags` argument. The values + * below force the use of a given codec, even if that codec is a no-op in the + * current build. Used in testing. Set to 0 for the default behavior, which is + * runtime feature detection on x86, a compile-time fixed codec on ARM, and + * the plain codec on other platforms: */ +#define BASE64_FORCE_AVX2 (1 << 0) +#define BASE64_FORCE_NEON32 (1 << 1) +#define BASE64_FORCE_NEON64 (1 << 2) +#define BASE64_FORCE_PLAIN (1 << 3) +#define BASE64_FORCE_SSSE3 (1 << 4) +#define BASE64_FORCE_SSE41 (1 << 5) +#define BASE64_FORCE_SSE42 (1 << 6) +#define BASE64_FORCE_AVX (1 << 7) +#define BASE64_FORCE_AVX512 (1 << 8) + +struct base64_state { + int eof; + int bytes; + int flags; + unsigned char carry; +}; + +/* Wrapper function to encode a plain string of given length. Output is written + * to *out without trailing zero. Output length in bytes is written to *outlen. + * The buffer in `out` has been allocated by the caller and is at least 4/3 the + * size of the input. See above for `flags`; set to 0 for default operation: */ +void BASE64_EXPORT base64_encode + ( const char *src + , size_t srclen + , char *out + , size_t *outlen + , int flags + ) ; + +/* Call this before calling base64_stream_encode() to init the state. See above + * for `flags`; set to 0 for default operation: */ +void BASE64_EXPORT base64_stream_encode_init + ( struct base64_state *state + , int flags + ) ; + +/* Encodes the block of data of given length at `src`, into the buffer at + * `out`. Caller is responsible for allocating a large enough out-buffer; it + * must be at least 4/3 the size of the in-buffer, but take some margin. Places + * the number of new bytes written into `outlen` (which is set to zero when the + * function starts). Does not zero-terminate or finalize the output. */ +void BASE64_EXPORT base64_stream_encode + ( struct base64_state *state + , const char *src + , size_t srclen + , char *out + , size_t *outlen + ) ; + +/* Finalizes the output begun by previous calls to `base64_stream_encode()`. + * Adds the required end-of-stream markers if appropriate. `outlen` is modified + * and will contain the number of new bytes written at `out` (which will quite + * often be zero). */ +void BASE64_EXPORT base64_stream_encode_final + ( struct base64_state *state + , char *out + , size_t *outlen + ) ; + +/* Wrapper function to decode a plain string of given length. Output is written + * to *out without trailing zero. Output length in bytes is written to *outlen. + * The buffer in `out` has been allocated by the caller and is at least 3/4 the + * size of the input. See above for `flags`, set to 0 for default operation: */ +int BASE64_EXPORT base64_decode + ( const char *src + , size_t srclen + , char *out + , size_t *outlen + , int flags + ) ; + +/* Call this before calling base64_stream_decode() to init the state. See above + * for `flags`; set to 0 for default operation: */ +void BASE64_EXPORT base64_stream_decode_init + ( struct base64_state *state + , int flags + ) ; + +/* Decodes the block of data of given length at `src`, into the buffer at + * `out`. Caller is responsible for allocating a large enough out-buffer; it + * must be at least 3/4 the size of the in-buffer, but take some margin. Places + * the number of new bytes written into `outlen` (which is set to zero when the + * function starts). Does not zero-terminate the output. Returns 1 if all is + * well, and 0 if a decoding error was found, such as an invalid character. + * Returns -1 if the chosen codec is not included in the current build. Used by + * the test harness to check whether a codec is available for testing. */ +int BASE64_EXPORT base64_stream_decode + ( struct base64_state *state + , const char *src + , size_t srclen + , char *out + , size_t *outlen + ) ; + +#ifdef __cplusplus +} +#endif + +#endif /* LIBBASE64_H */ diff --git a/mypyc/lib-rt/base64/tables/table_dec_32bit.h b/mypyc/lib-rt/base64/tables/table_dec_32bit.h new file mode 100644 index 0000000000000..f5d951fa79c71 --- /dev/null +++ b/mypyc/lib-rt/base64/tables/table_dec_32bit.h @@ -0,0 +1,393 @@ +#include +#define CHAR62 '+' +#define CHAR63 '/' +#define CHARPAD '=' + + +#if BASE64_LITTLE_ENDIAN + + +/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */ + +const uint32_t base64_table_dec_32bit_d0[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x000000f8, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000fc, +0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, +0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, +0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, +0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, +0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, +0x00000064, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, +0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, +0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, +0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, +0x000000c4, 0x000000c8, 0x000000cc, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +const uint32_t base64_table_dec_32bit_d1[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x0000e003, 0xffffffff, 0xffffffff, 0xffffffff, 0x0000f003, +0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, +0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, +0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, +0x00009001, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, +0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, +0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, +0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, +0x00001003, 0x00002003, 0x00003003, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +const uint32_t base64_table_dec_32bit_d2[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x00800f00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00c00f00, +0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, +0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, +0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, +0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, +0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, +0x00400600, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, +0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, +0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, +0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, +0x00400c00, 0x00800c00, 0x00c00c00, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +const uint32_t base64_table_dec_32bit_d3[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x003e0000, 0xffffffff, 0xffffffff, 0xffffffff, 0x003f0000, +0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, +0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, +0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, +0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, +0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, +0x00190000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, +0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, +0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, +0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, +0x00310000, 0x00320000, 0x00330000, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +#else + + +/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */ + +const uint32_t base64_table_dec_32bit_d0[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xf8000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfc000000, +0xd0000000, 0xd4000000, 0xd8000000, 0xdc000000, 0xe0000000, 0xe4000000, +0xe8000000, 0xec000000, 0xf0000000, 0xf4000000, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x04000000, 0x08000000, 0x0c000000, 0x10000000, 0x14000000, 0x18000000, +0x1c000000, 0x20000000, 0x24000000, 0x28000000, 0x2c000000, 0x30000000, +0x34000000, 0x38000000, 0x3c000000, 0x40000000, 0x44000000, 0x48000000, +0x4c000000, 0x50000000, 0x54000000, 0x58000000, 0x5c000000, 0x60000000, +0x64000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x68000000, 0x6c000000, 0x70000000, 0x74000000, 0x78000000, +0x7c000000, 0x80000000, 0x84000000, 0x88000000, 0x8c000000, 0x90000000, +0x94000000, 0x98000000, 0x9c000000, 0xa0000000, 0xa4000000, 0xa8000000, +0xac000000, 0xb0000000, 0xb4000000, 0xb8000000, 0xbc000000, 0xc0000000, +0xc4000000, 0xc8000000, 0xcc000000, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +const uint32_t base64_table_dec_32bit_d1[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x03e00000, 0xffffffff, 0xffffffff, 0xffffffff, 0x03f00000, +0x03400000, 0x03500000, 0x03600000, 0x03700000, 0x03800000, 0x03900000, +0x03a00000, 0x03b00000, 0x03c00000, 0x03d00000, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00100000, 0x00200000, 0x00300000, 0x00400000, 0x00500000, 0x00600000, +0x00700000, 0x00800000, 0x00900000, 0x00a00000, 0x00b00000, 0x00c00000, +0x00d00000, 0x00e00000, 0x00f00000, 0x01000000, 0x01100000, 0x01200000, +0x01300000, 0x01400000, 0x01500000, 0x01600000, 0x01700000, 0x01800000, +0x01900000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x01a00000, 0x01b00000, 0x01c00000, 0x01d00000, 0x01e00000, +0x01f00000, 0x02000000, 0x02100000, 0x02200000, 0x02300000, 0x02400000, +0x02500000, 0x02600000, 0x02700000, 0x02800000, 0x02900000, 0x02a00000, +0x02b00000, 0x02c00000, 0x02d00000, 0x02e00000, 0x02f00000, 0x03000000, +0x03100000, 0x03200000, 0x03300000, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +const uint32_t base64_table_dec_32bit_d2[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x000f8000, 0xffffffff, 0xffffffff, 0xffffffff, 0x000fc000, +0x000d0000, 0x000d4000, 0x000d8000, 0x000dc000, 0x000e0000, 0x000e4000, +0x000e8000, 0x000ec000, 0x000f0000, 0x000f4000, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00004000, 0x00008000, 0x0000c000, 0x00010000, 0x00014000, 0x00018000, +0x0001c000, 0x00020000, 0x00024000, 0x00028000, 0x0002c000, 0x00030000, +0x00034000, 0x00038000, 0x0003c000, 0x00040000, 0x00044000, 0x00048000, +0x0004c000, 0x00050000, 0x00054000, 0x00058000, 0x0005c000, 0x00060000, +0x00064000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x00068000, 0x0006c000, 0x00070000, 0x00074000, 0x00078000, +0x0007c000, 0x00080000, 0x00084000, 0x00088000, 0x0008c000, 0x00090000, +0x00094000, 0x00098000, 0x0009c000, 0x000a0000, 0x000a4000, 0x000a8000, +0x000ac000, 0x000b0000, 0x000b4000, 0x000b8000, 0x000bc000, 0x000c0000, +0x000c4000, 0x000c8000, 0x000cc000, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +const uint32_t base64_table_dec_32bit_d3[256] = { +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x00003e00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00003f00, +0x00003400, 0x00003500, 0x00003600, 0x00003700, 0x00003800, 0x00003900, +0x00003a00, 0x00003b00, 0x00003c00, 0x00003d00, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, +0x00000100, 0x00000200, 0x00000300, 0x00000400, 0x00000500, 0x00000600, +0x00000700, 0x00000800, 0x00000900, 0x00000a00, 0x00000b00, 0x00000c00, +0x00000d00, 0x00000e00, 0x00000f00, 0x00001000, 0x00001100, 0x00001200, +0x00001300, 0x00001400, 0x00001500, 0x00001600, 0x00001700, 0x00001800, +0x00001900, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0x00001a00, 0x00001b00, 0x00001c00, 0x00001d00, 0x00001e00, +0x00001f00, 0x00002000, 0x00002100, 0x00002200, 0x00002300, 0x00002400, +0x00002500, 0x00002600, 0x00002700, 0x00002800, 0x00002900, 0x00002a00, +0x00002b00, 0x00002c00, 0x00002d00, 0x00002e00, 0x00002f00, 0x00003000, +0x00003100, 0x00003200, 0x00003300, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + + +#endif diff --git a/mypyc/lib-rt/base64/tables/table_enc_12bit.h b/mypyc/lib-rt/base64/tables/table_enc_12bit.h new file mode 100644 index 0000000000000..2bc0d23068753 --- /dev/null +++ b/mypyc/lib-rt/base64/tables/table_enc_12bit.h @@ -0,0 +1,1031 @@ +#include + +const uint16_t base64_table_enc_12bit[] = { +#if BASE64_LITTLE_ENDIAN + 0x4141U, 0x4241U, 0x4341U, 0x4441U, 0x4541U, 0x4641U, 0x4741U, 0x4841U, + 0x4941U, 0x4A41U, 0x4B41U, 0x4C41U, 0x4D41U, 0x4E41U, 0x4F41U, 0x5041U, + 0x5141U, 0x5241U, 0x5341U, 0x5441U, 0x5541U, 0x5641U, 0x5741U, 0x5841U, + 0x5941U, 0x5A41U, 0x6141U, 0x6241U, 0x6341U, 0x6441U, 0x6541U, 0x6641U, + 0x6741U, 0x6841U, 0x6941U, 0x6A41U, 0x6B41U, 0x6C41U, 0x6D41U, 0x6E41U, + 0x6F41U, 0x7041U, 0x7141U, 0x7241U, 0x7341U, 0x7441U, 0x7541U, 0x7641U, + 0x7741U, 0x7841U, 0x7941U, 0x7A41U, 0x3041U, 0x3141U, 0x3241U, 0x3341U, + 0x3441U, 0x3541U, 0x3641U, 0x3741U, 0x3841U, 0x3941U, 0x2B41U, 0x2F41U, + 0x4142U, 0x4242U, 0x4342U, 0x4442U, 0x4542U, 0x4642U, 0x4742U, 0x4842U, + 0x4942U, 0x4A42U, 0x4B42U, 0x4C42U, 0x4D42U, 0x4E42U, 0x4F42U, 0x5042U, + 0x5142U, 0x5242U, 0x5342U, 0x5442U, 0x5542U, 0x5642U, 0x5742U, 0x5842U, + 0x5942U, 0x5A42U, 0x6142U, 0x6242U, 0x6342U, 0x6442U, 0x6542U, 0x6642U, + 0x6742U, 0x6842U, 0x6942U, 0x6A42U, 0x6B42U, 0x6C42U, 0x6D42U, 0x6E42U, + 0x6F42U, 0x7042U, 0x7142U, 0x7242U, 0x7342U, 0x7442U, 0x7542U, 0x7642U, + 0x7742U, 0x7842U, 0x7942U, 0x7A42U, 0x3042U, 0x3142U, 0x3242U, 0x3342U, + 0x3442U, 0x3542U, 0x3642U, 0x3742U, 0x3842U, 0x3942U, 0x2B42U, 0x2F42U, + 0x4143U, 0x4243U, 0x4343U, 0x4443U, 0x4543U, 0x4643U, 0x4743U, 0x4843U, + 0x4943U, 0x4A43U, 0x4B43U, 0x4C43U, 0x4D43U, 0x4E43U, 0x4F43U, 0x5043U, + 0x5143U, 0x5243U, 0x5343U, 0x5443U, 0x5543U, 0x5643U, 0x5743U, 0x5843U, + 0x5943U, 0x5A43U, 0x6143U, 0x6243U, 0x6343U, 0x6443U, 0x6543U, 0x6643U, + 0x6743U, 0x6843U, 0x6943U, 0x6A43U, 0x6B43U, 0x6C43U, 0x6D43U, 0x6E43U, + 0x6F43U, 0x7043U, 0x7143U, 0x7243U, 0x7343U, 0x7443U, 0x7543U, 0x7643U, + 0x7743U, 0x7843U, 0x7943U, 0x7A43U, 0x3043U, 0x3143U, 0x3243U, 0x3343U, + 0x3443U, 0x3543U, 0x3643U, 0x3743U, 0x3843U, 0x3943U, 0x2B43U, 0x2F43U, + 0x4144U, 0x4244U, 0x4344U, 0x4444U, 0x4544U, 0x4644U, 0x4744U, 0x4844U, + 0x4944U, 0x4A44U, 0x4B44U, 0x4C44U, 0x4D44U, 0x4E44U, 0x4F44U, 0x5044U, + 0x5144U, 0x5244U, 0x5344U, 0x5444U, 0x5544U, 0x5644U, 0x5744U, 0x5844U, + 0x5944U, 0x5A44U, 0x6144U, 0x6244U, 0x6344U, 0x6444U, 0x6544U, 0x6644U, + 0x6744U, 0x6844U, 0x6944U, 0x6A44U, 0x6B44U, 0x6C44U, 0x6D44U, 0x6E44U, + 0x6F44U, 0x7044U, 0x7144U, 0x7244U, 0x7344U, 0x7444U, 0x7544U, 0x7644U, + 0x7744U, 0x7844U, 0x7944U, 0x7A44U, 0x3044U, 0x3144U, 0x3244U, 0x3344U, + 0x3444U, 0x3544U, 0x3644U, 0x3744U, 0x3844U, 0x3944U, 0x2B44U, 0x2F44U, + 0x4145U, 0x4245U, 0x4345U, 0x4445U, 0x4545U, 0x4645U, 0x4745U, 0x4845U, + 0x4945U, 0x4A45U, 0x4B45U, 0x4C45U, 0x4D45U, 0x4E45U, 0x4F45U, 0x5045U, + 0x5145U, 0x5245U, 0x5345U, 0x5445U, 0x5545U, 0x5645U, 0x5745U, 0x5845U, + 0x5945U, 0x5A45U, 0x6145U, 0x6245U, 0x6345U, 0x6445U, 0x6545U, 0x6645U, + 0x6745U, 0x6845U, 0x6945U, 0x6A45U, 0x6B45U, 0x6C45U, 0x6D45U, 0x6E45U, + 0x6F45U, 0x7045U, 0x7145U, 0x7245U, 0x7345U, 0x7445U, 0x7545U, 0x7645U, + 0x7745U, 0x7845U, 0x7945U, 0x7A45U, 0x3045U, 0x3145U, 0x3245U, 0x3345U, + 0x3445U, 0x3545U, 0x3645U, 0x3745U, 0x3845U, 0x3945U, 0x2B45U, 0x2F45U, + 0x4146U, 0x4246U, 0x4346U, 0x4446U, 0x4546U, 0x4646U, 0x4746U, 0x4846U, + 0x4946U, 0x4A46U, 0x4B46U, 0x4C46U, 0x4D46U, 0x4E46U, 0x4F46U, 0x5046U, + 0x5146U, 0x5246U, 0x5346U, 0x5446U, 0x5546U, 0x5646U, 0x5746U, 0x5846U, + 0x5946U, 0x5A46U, 0x6146U, 0x6246U, 0x6346U, 0x6446U, 0x6546U, 0x6646U, + 0x6746U, 0x6846U, 0x6946U, 0x6A46U, 0x6B46U, 0x6C46U, 0x6D46U, 0x6E46U, + 0x6F46U, 0x7046U, 0x7146U, 0x7246U, 0x7346U, 0x7446U, 0x7546U, 0x7646U, + 0x7746U, 0x7846U, 0x7946U, 0x7A46U, 0x3046U, 0x3146U, 0x3246U, 0x3346U, + 0x3446U, 0x3546U, 0x3646U, 0x3746U, 0x3846U, 0x3946U, 0x2B46U, 0x2F46U, + 0x4147U, 0x4247U, 0x4347U, 0x4447U, 0x4547U, 0x4647U, 0x4747U, 0x4847U, + 0x4947U, 0x4A47U, 0x4B47U, 0x4C47U, 0x4D47U, 0x4E47U, 0x4F47U, 0x5047U, + 0x5147U, 0x5247U, 0x5347U, 0x5447U, 0x5547U, 0x5647U, 0x5747U, 0x5847U, + 0x5947U, 0x5A47U, 0x6147U, 0x6247U, 0x6347U, 0x6447U, 0x6547U, 0x6647U, + 0x6747U, 0x6847U, 0x6947U, 0x6A47U, 0x6B47U, 0x6C47U, 0x6D47U, 0x6E47U, + 0x6F47U, 0x7047U, 0x7147U, 0x7247U, 0x7347U, 0x7447U, 0x7547U, 0x7647U, + 0x7747U, 0x7847U, 0x7947U, 0x7A47U, 0x3047U, 0x3147U, 0x3247U, 0x3347U, + 0x3447U, 0x3547U, 0x3647U, 0x3747U, 0x3847U, 0x3947U, 0x2B47U, 0x2F47U, + 0x4148U, 0x4248U, 0x4348U, 0x4448U, 0x4548U, 0x4648U, 0x4748U, 0x4848U, + 0x4948U, 0x4A48U, 0x4B48U, 0x4C48U, 0x4D48U, 0x4E48U, 0x4F48U, 0x5048U, + 0x5148U, 0x5248U, 0x5348U, 0x5448U, 0x5548U, 0x5648U, 0x5748U, 0x5848U, + 0x5948U, 0x5A48U, 0x6148U, 0x6248U, 0x6348U, 0x6448U, 0x6548U, 0x6648U, + 0x6748U, 0x6848U, 0x6948U, 0x6A48U, 0x6B48U, 0x6C48U, 0x6D48U, 0x6E48U, + 0x6F48U, 0x7048U, 0x7148U, 0x7248U, 0x7348U, 0x7448U, 0x7548U, 0x7648U, + 0x7748U, 0x7848U, 0x7948U, 0x7A48U, 0x3048U, 0x3148U, 0x3248U, 0x3348U, + 0x3448U, 0x3548U, 0x3648U, 0x3748U, 0x3848U, 0x3948U, 0x2B48U, 0x2F48U, + 0x4149U, 0x4249U, 0x4349U, 0x4449U, 0x4549U, 0x4649U, 0x4749U, 0x4849U, + 0x4949U, 0x4A49U, 0x4B49U, 0x4C49U, 0x4D49U, 0x4E49U, 0x4F49U, 0x5049U, + 0x5149U, 0x5249U, 0x5349U, 0x5449U, 0x5549U, 0x5649U, 0x5749U, 0x5849U, + 0x5949U, 0x5A49U, 0x6149U, 0x6249U, 0x6349U, 0x6449U, 0x6549U, 0x6649U, + 0x6749U, 0x6849U, 0x6949U, 0x6A49U, 0x6B49U, 0x6C49U, 0x6D49U, 0x6E49U, + 0x6F49U, 0x7049U, 0x7149U, 0x7249U, 0x7349U, 0x7449U, 0x7549U, 0x7649U, + 0x7749U, 0x7849U, 0x7949U, 0x7A49U, 0x3049U, 0x3149U, 0x3249U, 0x3349U, + 0x3449U, 0x3549U, 0x3649U, 0x3749U, 0x3849U, 0x3949U, 0x2B49U, 0x2F49U, + 0x414AU, 0x424AU, 0x434AU, 0x444AU, 0x454AU, 0x464AU, 0x474AU, 0x484AU, + 0x494AU, 0x4A4AU, 0x4B4AU, 0x4C4AU, 0x4D4AU, 0x4E4AU, 0x4F4AU, 0x504AU, + 0x514AU, 0x524AU, 0x534AU, 0x544AU, 0x554AU, 0x564AU, 0x574AU, 0x584AU, + 0x594AU, 0x5A4AU, 0x614AU, 0x624AU, 0x634AU, 0x644AU, 0x654AU, 0x664AU, + 0x674AU, 0x684AU, 0x694AU, 0x6A4AU, 0x6B4AU, 0x6C4AU, 0x6D4AU, 0x6E4AU, + 0x6F4AU, 0x704AU, 0x714AU, 0x724AU, 0x734AU, 0x744AU, 0x754AU, 0x764AU, + 0x774AU, 0x784AU, 0x794AU, 0x7A4AU, 0x304AU, 0x314AU, 0x324AU, 0x334AU, + 0x344AU, 0x354AU, 0x364AU, 0x374AU, 0x384AU, 0x394AU, 0x2B4AU, 0x2F4AU, + 0x414BU, 0x424BU, 0x434BU, 0x444BU, 0x454BU, 0x464BU, 0x474BU, 0x484BU, + 0x494BU, 0x4A4BU, 0x4B4BU, 0x4C4BU, 0x4D4BU, 0x4E4BU, 0x4F4BU, 0x504BU, + 0x514BU, 0x524BU, 0x534BU, 0x544BU, 0x554BU, 0x564BU, 0x574BU, 0x584BU, + 0x594BU, 0x5A4BU, 0x614BU, 0x624BU, 0x634BU, 0x644BU, 0x654BU, 0x664BU, + 0x674BU, 0x684BU, 0x694BU, 0x6A4BU, 0x6B4BU, 0x6C4BU, 0x6D4BU, 0x6E4BU, + 0x6F4BU, 0x704BU, 0x714BU, 0x724BU, 0x734BU, 0x744BU, 0x754BU, 0x764BU, + 0x774BU, 0x784BU, 0x794BU, 0x7A4BU, 0x304BU, 0x314BU, 0x324BU, 0x334BU, + 0x344BU, 0x354BU, 0x364BU, 0x374BU, 0x384BU, 0x394BU, 0x2B4BU, 0x2F4BU, + 0x414CU, 0x424CU, 0x434CU, 0x444CU, 0x454CU, 0x464CU, 0x474CU, 0x484CU, + 0x494CU, 0x4A4CU, 0x4B4CU, 0x4C4CU, 0x4D4CU, 0x4E4CU, 0x4F4CU, 0x504CU, + 0x514CU, 0x524CU, 0x534CU, 0x544CU, 0x554CU, 0x564CU, 0x574CU, 0x584CU, + 0x594CU, 0x5A4CU, 0x614CU, 0x624CU, 0x634CU, 0x644CU, 0x654CU, 0x664CU, + 0x674CU, 0x684CU, 0x694CU, 0x6A4CU, 0x6B4CU, 0x6C4CU, 0x6D4CU, 0x6E4CU, + 0x6F4CU, 0x704CU, 0x714CU, 0x724CU, 0x734CU, 0x744CU, 0x754CU, 0x764CU, + 0x774CU, 0x784CU, 0x794CU, 0x7A4CU, 0x304CU, 0x314CU, 0x324CU, 0x334CU, + 0x344CU, 0x354CU, 0x364CU, 0x374CU, 0x384CU, 0x394CU, 0x2B4CU, 0x2F4CU, + 0x414DU, 0x424DU, 0x434DU, 0x444DU, 0x454DU, 0x464DU, 0x474DU, 0x484DU, + 0x494DU, 0x4A4DU, 0x4B4DU, 0x4C4DU, 0x4D4DU, 0x4E4DU, 0x4F4DU, 0x504DU, + 0x514DU, 0x524DU, 0x534DU, 0x544DU, 0x554DU, 0x564DU, 0x574DU, 0x584DU, + 0x594DU, 0x5A4DU, 0x614DU, 0x624DU, 0x634DU, 0x644DU, 0x654DU, 0x664DU, + 0x674DU, 0x684DU, 0x694DU, 0x6A4DU, 0x6B4DU, 0x6C4DU, 0x6D4DU, 0x6E4DU, + 0x6F4DU, 0x704DU, 0x714DU, 0x724DU, 0x734DU, 0x744DU, 0x754DU, 0x764DU, + 0x774DU, 0x784DU, 0x794DU, 0x7A4DU, 0x304DU, 0x314DU, 0x324DU, 0x334DU, + 0x344DU, 0x354DU, 0x364DU, 0x374DU, 0x384DU, 0x394DU, 0x2B4DU, 0x2F4DU, + 0x414EU, 0x424EU, 0x434EU, 0x444EU, 0x454EU, 0x464EU, 0x474EU, 0x484EU, + 0x494EU, 0x4A4EU, 0x4B4EU, 0x4C4EU, 0x4D4EU, 0x4E4EU, 0x4F4EU, 0x504EU, + 0x514EU, 0x524EU, 0x534EU, 0x544EU, 0x554EU, 0x564EU, 0x574EU, 0x584EU, + 0x594EU, 0x5A4EU, 0x614EU, 0x624EU, 0x634EU, 0x644EU, 0x654EU, 0x664EU, + 0x674EU, 0x684EU, 0x694EU, 0x6A4EU, 0x6B4EU, 0x6C4EU, 0x6D4EU, 0x6E4EU, + 0x6F4EU, 0x704EU, 0x714EU, 0x724EU, 0x734EU, 0x744EU, 0x754EU, 0x764EU, + 0x774EU, 0x784EU, 0x794EU, 0x7A4EU, 0x304EU, 0x314EU, 0x324EU, 0x334EU, + 0x344EU, 0x354EU, 0x364EU, 0x374EU, 0x384EU, 0x394EU, 0x2B4EU, 0x2F4EU, + 0x414FU, 0x424FU, 0x434FU, 0x444FU, 0x454FU, 0x464FU, 0x474FU, 0x484FU, + 0x494FU, 0x4A4FU, 0x4B4FU, 0x4C4FU, 0x4D4FU, 0x4E4FU, 0x4F4FU, 0x504FU, + 0x514FU, 0x524FU, 0x534FU, 0x544FU, 0x554FU, 0x564FU, 0x574FU, 0x584FU, + 0x594FU, 0x5A4FU, 0x614FU, 0x624FU, 0x634FU, 0x644FU, 0x654FU, 0x664FU, + 0x674FU, 0x684FU, 0x694FU, 0x6A4FU, 0x6B4FU, 0x6C4FU, 0x6D4FU, 0x6E4FU, + 0x6F4FU, 0x704FU, 0x714FU, 0x724FU, 0x734FU, 0x744FU, 0x754FU, 0x764FU, + 0x774FU, 0x784FU, 0x794FU, 0x7A4FU, 0x304FU, 0x314FU, 0x324FU, 0x334FU, + 0x344FU, 0x354FU, 0x364FU, 0x374FU, 0x384FU, 0x394FU, 0x2B4FU, 0x2F4FU, + 0x4150U, 0x4250U, 0x4350U, 0x4450U, 0x4550U, 0x4650U, 0x4750U, 0x4850U, + 0x4950U, 0x4A50U, 0x4B50U, 0x4C50U, 0x4D50U, 0x4E50U, 0x4F50U, 0x5050U, + 0x5150U, 0x5250U, 0x5350U, 0x5450U, 0x5550U, 0x5650U, 0x5750U, 0x5850U, + 0x5950U, 0x5A50U, 0x6150U, 0x6250U, 0x6350U, 0x6450U, 0x6550U, 0x6650U, + 0x6750U, 0x6850U, 0x6950U, 0x6A50U, 0x6B50U, 0x6C50U, 0x6D50U, 0x6E50U, + 0x6F50U, 0x7050U, 0x7150U, 0x7250U, 0x7350U, 0x7450U, 0x7550U, 0x7650U, + 0x7750U, 0x7850U, 0x7950U, 0x7A50U, 0x3050U, 0x3150U, 0x3250U, 0x3350U, + 0x3450U, 0x3550U, 0x3650U, 0x3750U, 0x3850U, 0x3950U, 0x2B50U, 0x2F50U, + 0x4151U, 0x4251U, 0x4351U, 0x4451U, 0x4551U, 0x4651U, 0x4751U, 0x4851U, + 0x4951U, 0x4A51U, 0x4B51U, 0x4C51U, 0x4D51U, 0x4E51U, 0x4F51U, 0x5051U, + 0x5151U, 0x5251U, 0x5351U, 0x5451U, 0x5551U, 0x5651U, 0x5751U, 0x5851U, + 0x5951U, 0x5A51U, 0x6151U, 0x6251U, 0x6351U, 0x6451U, 0x6551U, 0x6651U, + 0x6751U, 0x6851U, 0x6951U, 0x6A51U, 0x6B51U, 0x6C51U, 0x6D51U, 0x6E51U, + 0x6F51U, 0x7051U, 0x7151U, 0x7251U, 0x7351U, 0x7451U, 0x7551U, 0x7651U, + 0x7751U, 0x7851U, 0x7951U, 0x7A51U, 0x3051U, 0x3151U, 0x3251U, 0x3351U, + 0x3451U, 0x3551U, 0x3651U, 0x3751U, 0x3851U, 0x3951U, 0x2B51U, 0x2F51U, + 0x4152U, 0x4252U, 0x4352U, 0x4452U, 0x4552U, 0x4652U, 0x4752U, 0x4852U, + 0x4952U, 0x4A52U, 0x4B52U, 0x4C52U, 0x4D52U, 0x4E52U, 0x4F52U, 0x5052U, + 0x5152U, 0x5252U, 0x5352U, 0x5452U, 0x5552U, 0x5652U, 0x5752U, 0x5852U, + 0x5952U, 0x5A52U, 0x6152U, 0x6252U, 0x6352U, 0x6452U, 0x6552U, 0x6652U, + 0x6752U, 0x6852U, 0x6952U, 0x6A52U, 0x6B52U, 0x6C52U, 0x6D52U, 0x6E52U, + 0x6F52U, 0x7052U, 0x7152U, 0x7252U, 0x7352U, 0x7452U, 0x7552U, 0x7652U, + 0x7752U, 0x7852U, 0x7952U, 0x7A52U, 0x3052U, 0x3152U, 0x3252U, 0x3352U, + 0x3452U, 0x3552U, 0x3652U, 0x3752U, 0x3852U, 0x3952U, 0x2B52U, 0x2F52U, + 0x4153U, 0x4253U, 0x4353U, 0x4453U, 0x4553U, 0x4653U, 0x4753U, 0x4853U, + 0x4953U, 0x4A53U, 0x4B53U, 0x4C53U, 0x4D53U, 0x4E53U, 0x4F53U, 0x5053U, + 0x5153U, 0x5253U, 0x5353U, 0x5453U, 0x5553U, 0x5653U, 0x5753U, 0x5853U, + 0x5953U, 0x5A53U, 0x6153U, 0x6253U, 0x6353U, 0x6453U, 0x6553U, 0x6653U, + 0x6753U, 0x6853U, 0x6953U, 0x6A53U, 0x6B53U, 0x6C53U, 0x6D53U, 0x6E53U, + 0x6F53U, 0x7053U, 0x7153U, 0x7253U, 0x7353U, 0x7453U, 0x7553U, 0x7653U, + 0x7753U, 0x7853U, 0x7953U, 0x7A53U, 0x3053U, 0x3153U, 0x3253U, 0x3353U, + 0x3453U, 0x3553U, 0x3653U, 0x3753U, 0x3853U, 0x3953U, 0x2B53U, 0x2F53U, + 0x4154U, 0x4254U, 0x4354U, 0x4454U, 0x4554U, 0x4654U, 0x4754U, 0x4854U, + 0x4954U, 0x4A54U, 0x4B54U, 0x4C54U, 0x4D54U, 0x4E54U, 0x4F54U, 0x5054U, + 0x5154U, 0x5254U, 0x5354U, 0x5454U, 0x5554U, 0x5654U, 0x5754U, 0x5854U, + 0x5954U, 0x5A54U, 0x6154U, 0x6254U, 0x6354U, 0x6454U, 0x6554U, 0x6654U, + 0x6754U, 0x6854U, 0x6954U, 0x6A54U, 0x6B54U, 0x6C54U, 0x6D54U, 0x6E54U, + 0x6F54U, 0x7054U, 0x7154U, 0x7254U, 0x7354U, 0x7454U, 0x7554U, 0x7654U, + 0x7754U, 0x7854U, 0x7954U, 0x7A54U, 0x3054U, 0x3154U, 0x3254U, 0x3354U, + 0x3454U, 0x3554U, 0x3654U, 0x3754U, 0x3854U, 0x3954U, 0x2B54U, 0x2F54U, + 0x4155U, 0x4255U, 0x4355U, 0x4455U, 0x4555U, 0x4655U, 0x4755U, 0x4855U, + 0x4955U, 0x4A55U, 0x4B55U, 0x4C55U, 0x4D55U, 0x4E55U, 0x4F55U, 0x5055U, + 0x5155U, 0x5255U, 0x5355U, 0x5455U, 0x5555U, 0x5655U, 0x5755U, 0x5855U, + 0x5955U, 0x5A55U, 0x6155U, 0x6255U, 0x6355U, 0x6455U, 0x6555U, 0x6655U, + 0x6755U, 0x6855U, 0x6955U, 0x6A55U, 0x6B55U, 0x6C55U, 0x6D55U, 0x6E55U, + 0x6F55U, 0x7055U, 0x7155U, 0x7255U, 0x7355U, 0x7455U, 0x7555U, 0x7655U, + 0x7755U, 0x7855U, 0x7955U, 0x7A55U, 0x3055U, 0x3155U, 0x3255U, 0x3355U, + 0x3455U, 0x3555U, 0x3655U, 0x3755U, 0x3855U, 0x3955U, 0x2B55U, 0x2F55U, + 0x4156U, 0x4256U, 0x4356U, 0x4456U, 0x4556U, 0x4656U, 0x4756U, 0x4856U, + 0x4956U, 0x4A56U, 0x4B56U, 0x4C56U, 0x4D56U, 0x4E56U, 0x4F56U, 0x5056U, + 0x5156U, 0x5256U, 0x5356U, 0x5456U, 0x5556U, 0x5656U, 0x5756U, 0x5856U, + 0x5956U, 0x5A56U, 0x6156U, 0x6256U, 0x6356U, 0x6456U, 0x6556U, 0x6656U, + 0x6756U, 0x6856U, 0x6956U, 0x6A56U, 0x6B56U, 0x6C56U, 0x6D56U, 0x6E56U, + 0x6F56U, 0x7056U, 0x7156U, 0x7256U, 0x7356U, 0x7456U, 0x7556U, 0x7656U, + 0x7756U, 0x7856U, 0x7956U, 0x7A56U, 0x3056U, 0x3156U, 0x3256U, 0x3356U, + 0x3456U, 0x3556U, 0x3656U, 0x3756U, 0x3856U, 0x3956U, 0x2B56U, 0x2F56U, + 0x4157U, 0x4257U, 0x4357U, 0x4457U, 0x4557U, 0x4657U, 0x4757U, 0x4857U, + 0x4957U, 0x4A57U, 0x4B57U, 0x4C57U, 0x4D57U, 0x4E57U, 0x4F57U, 0x5057U, + 0x5157U, 0x5257U, 0x5357U, 0x5457U, 0x5557U, 0x5657U, 0x5757U, 0x5857U, + 0x5957U, 0x5A57U, 0x6157U, 0x6257U, 0x6357U, 0x6457U, 0x6557U, 0x6657U, + 0x6757U, 0x6857U, 0x6957U, 0x6A57U, 0x6B57U, 0x6C57U, 0x6D57U, 0x6E57U, + 0x6F57U, 0x7057U, 0x7157U, 0x7257U, 0x7357U, 0x7457U, 0x7557U, 0x7657U, + 0x7757U, 0x7857U, 0x7957U, 0x7A57U, 0x3057U, 0x3157U, 0x3257U, 0x3357U, + 0x3457U, 0x3557U, 0x3657U, 0x3757U, 0x3857U, 0x3957U, 0x2B57U, 0x2F57U, + 0x4158U, 0x4258U, 0x4358U, 0x4458U, 0x4558U, 0x4658U, 0x4758U, 0x4858U, + 0x4958U, 0x4A58U, 0x4B58U, 0x4C58U, 0x4D58U, 0x4E58U, 0x4F58U, 0x5058U, + 0x5158U, 0x5258U, 0x5358U, 0x5458U, 0x5558U, 0x5658U, 0x5758U, 0x5858U, + 0x5958U, 0x5A58U, 0x6158U, 0x6258U, 0x6358U, 0x6458U, 0x6558U, 0x6658U, + 0x6758U, 0x6858U, 0x6958U, 0x6A58U, 0x6B58U, 0x6C58U, 0x6D58U, 0x6E58U, + 0x6F58U, 0x7058U, 0x7158U, 0x7258U, 0x7358U, 0x7458U, 0x7558U, 0x7658U, + 0x7758U, 0x7858U, 0x7958U, 0x7A58U, 0x3058U, 0x3158U, 0x3258U, 0x3358U, + 0x3458U, 0x3558U, 0x3658U, 0x3758U, 0x3858U, 0x3958U, 0x2B58U, 0x2F58U, + 0x4159U, 0x4259U, 0x4359U, 0x4459U, 0x4559U, 0x4659U, 0x4759U, 0x4859U, + 0x4959U, 0x4A59U, 0x4B59U, 0x4C59U, 0x4D59U, 0x4E59U, 0x4F59U, 0x5059U, + 0x5159U, 0x5259U, 0x5359U, 0x5459U, 0x5559U, 0x5659U, 0x5759U, 0x5859U, + 0x5959U, 0x5A59U, 0x6159U, 0x6259U, 0x6359U, 0x6459U, 0x6559U, 0x6659U, + 0x6759U, 0x6859U, 0x6959U, 0x6A59U, 0x6B59U, 0x6C59U, 0x6D59U, 0x6E59U, + 0x6F59U, 0x7059U, 0x7159U, 0x7259U, 0x7359U, 0x7459U, 0x7559U, 0x7659U, + 0x7759U, 0x7859U, 0x7959U, 0x7A59U, 0x3059U, 0x3159U, 0x3259U, 0x3359U, + 0x3459U, 0x3559U, 0x3659U, 0x3759U, 0x3859U, 0x3959U, 0x2B59U, 0x2F59U, + 0x415AU, 0x425AU, 0x435AU, 0x445AU, 0x455AU, 0x465AU, 0x475AU, 0x485AU, + 0x495AU, 0x4A5AU, 0x4B5AU, 0x4C5AU, 0x4D5AU, 0x4E5AU, 0x4F5AU, 0x505AU, + 0x515AU, 0x525AU, 0x535AU, 0x545AU, 0x555AU, 0x565AU, 0x575AU, 0x585AU, + 0x595AU, 0x5A5AU, 0x615AU, 0x625AU, 0x635AU, 0x645AU, 0x655AU, 0x665AU, + 0x675AU, 0x685AU, 0x695AU, 0x6A5AU, 0x6B5AU, 0x6C5AU, 0x6D5AU, 0x6E5AU, + 0x6F5AU, 0x705AU, 0x715AU, 0x725AU, 0x735AU, 0x745AU, 0x755AU, 0x765AU, + 0x775AU, 0x785AU, 0x795AU, 0x7A5AU, 0x305AU, 0x315AU, 0x325AU, 0x335AU, + 0x345AU, 0x355AU, 0x365AU, 0x375AU, 0x385AU, 0x395AU, 0x2B5AU, 0x2F5AU, + 0x4161U, 0x4261U, 0x4361U, 0x4461U, 0x4561U, 0x4661U, 0x4761U, 0x4861U, + 0x4961U, 0x4A61U, 0x4B61U, 0x4C61U, 0x4D61U, 0x4E61U, 0x4F61U, 0x5061U, + 0x5161U, 0x5261U, 0x5361U, 0x5461U, 0x5561U, 0x5661U, 0x5761U, 0x5861U, + 0x5961U, 0x5A61U, 0x6161U, 0x6261U, 0x6361U, 0x6461U, 0x6561U, 0x6661U, + 0x6761U, 0x6861U, 0x6961U, 0x6A61U, 0x6B61U, 0x6C61U, 0x6D61U, 0x6E61U, + 0x6F61U, 0x7061U, 0x7161U, 0x7261U, 0x7361U, 0x7461U, 0x7561U, 0x7661U, + 0x7761U, 0x7861U, 0x7961U, 0x7A61U, 0x3061U, 0x3161U, 0x3261U, 0x3361U, + 0x3461U, 0x3561U, 0x3661U, 0x3761U, 0x3861U, 0x3961U, 0x2B61U, 0x2F61U, + 0x4162U, 0x4262U, 0x4362U, 0x4462U, 0x4562U, 0x4662U, 0x4762U, 0x4862U, + 0x4962U, 0x4A62U, 0x4B62U, 0x4C62U, 0x4D62U, 0x4E62U, 0x4F62U, 0x5062U, + 0x5162U, 0x5262U, 0x5362U, 0x5462U, 0x5562U, 0x5662U, 0x5762U, 0x5862U, + 0x5962U, 0x5A62U, 0x6162U, 0x6262U, 0x6362U, 0x6462U, 0x6562U, 0x6662U, + 0x6762U, 0x6862U, 0x6962U, 0x6A62U, 0x6B62U, 0x6C62U, 0x6D62U, 0x6E62U, + 0x6F62U, 0x7062U, 0x7162U, 0x7262U, 0x7362U, 0x7462U, 0x7562U, 0x7662U, + 0x7762U, 0x7862U, 0x7962U, 0x7A62U, 0x3062U, 0x3162U, 0x3262U, 0x3362U, + 0x3462U, 0x3562U, 0x3662U, 0x3762U, 0x3862U, 0x3962U, 0x2B62U, 0x2F62U, + 0x4163U, 0x4263U, 0x4363U, 0x4463U, 0x4563U, 0x4663U, 0x4763U, 0x4863U, + 0x4963U, 0x4A63U, 0x4B63U, 0x4C63U, 0x4D63U, 0x4E63U, 0x4F63U, 0x5063U, + 0x5163U, 0x5263U, 0x5363U, 0x5463U, 0x5563U, 0x5663U, 0x5763U, 0x5863U, + 0x5963U, 0x5A63U, 0x6163U, 0x6263U, 0x6363U, 0x6463U, 0x6563U, 0x6663U, + 0x6763U, 0x6863U, 0x6963U, 0x6A63U, 0x6B63U, 0x6C63U, 0x6D63U, 0x6E63U, + 0x6F63U, 0x7063U, 0x7163U, 0x7263U, 0x7363U, 0x7463U, 0x7563U, 0x7663U, + 0x7763U, 0x7863U, 0x7963U, 0x7A63U, 0x3063U, 0x3163U, 0x3263U, 0x3363U, + 0x3463U, 0x3563U, 0x3663U, 0x3763U, 0x3863U, 0x3963U, 0x2B63U, 0x2F63U, + 0x4164U, 0x4264U, 0x4364U, 0x4464U, 0x4564U, 0x4664U, 0x4764U, 0x4864U, + 0x4964U, 0x4A64U, 0x4B64U, 0x4C64U, 0x4D64U, 0x4E64U, 0x4F64U, 0x5064U, + 0x5164U, 0x5264U, 0x5364U, 0x5464U, 0x5564U, 0x5664U, 0x5764U, 0x5864U, + 0x5964U, 0x5A64U, 0x6164U, 0x6264U, 0x6364U, 0x6464U, 0x6564U, 0x6664U, + 0x6764U, 0x6864U, 0x6964U, 0x6A64U, 0x6B64U, 0x6C64U, 0x6D64U, 0x6E64U, + 0x6F64U, 0x7064U, 0x7164U, 0x7264U, 0x7364U, 0x7464U, 0x7564U, 0x7664U, + 0x7764U, 0x7864U, 0x7964U, 0x7A64U, 0x3064U, 0x3164U, 0x3264U, 0x3364U, + 0x3464U, 0x3564U, 0x3664U, 0x3764U, 0x3864U, 0x3964U, 0x2B64U, 0x2F64U, + 0x4165U, 0x4265U, 0x4365U, 0x4465U, 0x4565U, 0x4665U, 0x4765U, 0x4865U, + 0x4965U, 0x4A65U, 0x4B65U, 0x4C65U, 0x4D65U, 0x4E65U, 0x4F65U, 0x5065U, + 0x5165U, 0x5265U, 0x5365U, 0x5465U, 0x5565U, 0x5665U, 0x5765U, 0x5865U, + 0x5965U, 0x5A65U, 0x6165U, 0x6265U, 0x6365U, 0x6465U, 0x6565U, 0x6665U, + 0x6765U, 0x6865U, 0x6965U, 0x6A65U, 0x6B65U, 0x6C65U, 0x6D65U, 0x6E65U, + 0x6F65U, 0x7065U, 0x7165U, 0x7265U, 0x7365U, 0x7465U, 0x7565U, 0x7665U, + 0x7765U, 0x7865U, 0x7965U, 0x7A65U, 0x3065U, 0x3165U, 0x3265U, 0x3365U, + 0x3465U, 0x3565U, 0x3665U, 0x3765U, 0x3865U, 0x3965U, 0x2B65U, 0x2F65U, + 0x4166U, 0x4266U, 0x4366U, 0x4466U, 0x4566U, 0x4666U, 0x4766U, 0x4866U, + 0x4966U, 0x4A66U, 0x4B66U, 0x4C66U, 0x4D66U, 0x4E66U, 0x4F66U, 0x5066U, + 0x5166U, 0x5266U, 0x5366U, 0x5466U, 0x5566U, 0x5666U, 0x5766U, 0x5866U, + 0x5966U, 0x5A66U, 0x6166U, 0x6266U, 0x6366U, 0x6466U, 0x6566U, 0x6666U, + 0x6766U, 0x6866U, 0x6966U, 0x6A66U, 0x6B66U, 0x6C66U, 0x6D66U, 0x6E66U, + 0x6F66U, 0x7066U, 0x7166U, 0x7266U, 0x7366U, 0x7466U, 0x7566U, 0x7666U, + 0x7766U, 0x7866U, 0x7966U, 0x7A66U, 0x3066U, 0x3166U, 0x3266U, 0x3366U, + 0x3466U, 0x3566U, 0x3666U, 0x3766U, 0x3866U, 0x3966U, 0x2B66U, 0x2F66U, + 0x4167U, 0x4267U, 0x4367U, 0x4467U, 0x4567U, 0x4667U, 0x4767U, 0x4867U, + 0x4967U, 0x4A67U, 0x4B67U, 0x4C67U, 0x4D67U, 0x4E67U, 0x4F67U, 0x5067U, + 0x5167U, 0x5267U, 0x5367U, 0x5467U, 0x5567U, 0x5667U, 0x5767U, 0x5867U, + 0x5967U, 0x5A67U, 0x6167U, 0x6267U, 0x6367U, 0x6467U, 0x6567U, 0x6667U, + 0x6767U, 0x6867U, 0x6967U, 0x6A67U, 0x6B67U, 0x6C67U, 0x6D67U, 0x6E67U, + 0x6F67U, 0x7067U, 0x7167U, 0x7267U, 0x7367U, 0x7467U, 0x7567U, 0x7667U, + 0x7767U, 0x7867U, 0x7967U, 0x7A67U, 0x3067U, 0x3167U, 0x3267U, 0x3367U, + 0x3467U, 0x3567U, 0x3667U, 0x3767U, 0x3867U, 0x3967U, 0x2B67U, 0x2F67U, + 0x4168U, 0x4268U, 0x4368U, 0x4468U, 0x4568U, 0x4668U, 0x4768U, 0x4868U, + 0x4968U, 0x4A68U, 0x4B68U, 0x4C68U, 0x4D68U, 0x4E68U, 0x4F68U, 0x5068U, + 0x5168U, 0x5268U, 0x5368U, 0x5468U, 0x5568U, 0x5668U, 0x5768U, 0x5868U, + 0x5968U, 0x5A68U, 0x6168U, 0x6268U, 0x6368U, 0x6468U, 0x6568U, 0x6668U, + 0x6768U, 0x6868U, 0x6968U, 0x6A68U, 0x6B68U, 0x6C68U, 0x6D68U, 0x6E68U, + 0x6F68U, 0x7068U, 0x7168U, 0x7268U, 0x7368U, 0x7468U, 0x7568U, 0x7668U, + 0x7768U, 0x7868U, 0x7968U, 0x7A68U, 0x3068U, 0x3168U, 0x3268U, 0x3368U, + 0x3468U, 0x3568U, 0x3668U, 0x3768U, 0x3868U, 0x3968U, 0x2B68U, 0x2F68U, + 0x4169U, 0x4269U, 0x4369U, 0x4469U, 0x4569U, 0x4669U, 0x4769U, 0x4869U, + 0x4969U, 0x4A69U, 0x4B69U, 0x4C69U, 0x4D69U, 0x4E69U, 0x4F69U, 0x5069U, + 0x5169U, 0x5269U, 0x5369U, 0x5469U, 0x5569U, 0x5669U, 0x5769U, 0x5869U, + 0x5969U, 0x5A69U, 0x6169U, 0x6269U, 0x6369U, 0x6469U, 0x6569U, 0x6669U, + 0x6769U, 0x6869U, 0x6969U, 0x6A69U, 0x6B69U, 0x6C69U, 0x6D69U, 0x6E69U, + 0x6F69U, 0x7069U, 0x7169U, 0x7269U, 0x7369U, 0x7469U, 0x7569U, 0x7669U, + 0x7769U, 0x7869U, 0x7969U, 0x7A69U, 0x3069U, 0x3169U, 0x3269U, 0x3369U, + 0x3469U, 0x3569U, 0x3669U, 0x3769U, 0x3869U, 0x3969U, 0x2B69U, 0x2F69U, + 0x416AU, 0x426AU, 0x436AU, 0x446AU, 0x456AU, 0x466AU, 0x476AU, 0x486AU, + 0x496AU, 0x4A6AU, 0x4B6AU, 0x4C6AU, 0x4D6AU, 0x4E6AU, 0x4F6AU, 0x506AU, + 0x516AU, 0x526AU, 0x536AU, 0x546AU, 0x556AU, 0x566AU, 0x576AU, 0x586AU, + 0x596AU, 0x5A6AU, 0x616AU, 0x626AU, 0x636AU, 0x646AU, 0x656AU, 0x666AU, + 0x676AU, 0x686AU, 0x696AU, 0x6A6AU, 0x6B6AU, 0x6C6AU, 0x6D6AU, 0x6E6AU, + 0x6F6AU, 0x706AU, 0x716AU, 0x726AU, 0x736AU, 0x746AU, 0x756AU, 0x766AU, + 0x776AU, 0x786AU, 0x796AU, 0x7A6AU, 0x306AU, 0x316AU, 0x326AU, 0x336AU, + 0x346AU, 0x356AU, 0x366AU, 0x376AU, 0x386AU, 0x396AU, 0x2B6AU, 0x2F6AU, + 0x416BU, 0x426BU, 0x436BU, 0x446BU, 0x456BU, 0x466BU, 0x476BU, 0x486BU, + 0x496BU, 0x4A6BU, 0x4B6BU, 0x4C6BU, 0x4D6BU, 0x4E6BU, 0x4F6BU, 0x506BU, + 0x516BU, 0x526BU, 0x536BU, 0x546BU, 0x556BU, 0x566BU, 0x576BU, 0x586BU, + 0x596BU, 0x5A6BU, 0x616BU, 0x626BU, 0x636BU, 0x646BU, 0x656BU, 0x666BU, + 0x676BU, 0x686BU, 0x696BU, 0x6A6BU, 0x6B6BU, 0x6C6BU, 0x6D6BU, 0x6E6BU, + 0x6F6BU, 0x706BU, 0x716BU, 0x726BU, 0x736BU, 0x746BU, 0x756BU, 0x766BU, + 0x776BU, 0x786BU, 0x796BU, 0x7A6BU, 0x306BU, 0x316BU, 0x326BU, 0x336BU, + 0x346BU, 0x356BU, 0x366BU, 0x376BU, 0x386BU, 0x396BU, 0x2B6BU, 0x2F6BU, + 0x416CU, 0x426CU, 0x436CU, 0x446CU, 0x456CU, 0x466CU, 0x476CU, 0x486CU, + 0x496CU, 0x4A6CU, 0x4B6CU, 0x4C6CU, 0x4D6CU, 0x4E6CU, 0x4F6CU, 0x506CU, + 0x516CU, 0x526CU, 0x536CU, 0x546CU, 0x556CU, 0x566CU, 0x576CU, 0x586CU, + 0x596CU, 0x5A6CU, 0x616CU, 0x626CU, 0x636CU, 0x646CU, 0x656CU, 0x666CU, + 0x676CU, 0x686CU, 0x696CU, 0x6A6CU, 0x6B6CU, 0x6C6CU, 0x6D6CU, 0x6E6CU, + 0x6F6CU, 0x706CU, 0x716CU, 0x726CU, 0x736CU, 0x746CU, 0x756CU, 0x766CU, + 0x776CU, 0x786CU, 0x796CU, 0x7A6CU, 0x306CU, 0x316CU, 0x326CU, 0x336CU, + 0x346CU, 0x356CU, 0x366CU, 0x376CU, 0x386CU, 0x396CU, 0x2B6CU, 0x2F6CU, + 0x416DU, 0x426DU, 0x436DU, 0x446DU, 0x456DU, 0x466DU, 0x476DU, 0x486DU, + 0x496DU, 0x4A6DU, 0x4B6DU, 0x4C6DU, 0x4D6DU, 0x4E6DU, 0x4F6DU, 0x506DU, + 0x516DU, 0x526DU, 0x536DU, 0x546DU, 0x556DU, 0x566DU, 0x576DU, 0x586DU, + 0x596DU, 0x5A6DU, 0x616DU, 0x626DU, 0x636DU, 0x646DU, 0x656DU, 0x666DU, + 0x676DU, 0x686DU, 0x696DU, 0x6A6DU, 0x6B6DU, 0x6C6DU, 0x6D6DU, 0x6E6DU, + 0x6F6DU, 0x706DU, 0x716DU, 0x726DU, 0x736DU, 0x746DU, 0x756DU, 0x766DU, + 0x776DU, 0x786DU, 0x796DU, 0x7A6DU, 0x306DU, 0x316DU, 0x326DU, 0x336DU, + 0x346DU, 0x356DU, 0x366DU, 0x376DU, 0x386DU, 0x396DU, 0x2B6DU, 0x2F6DU, + 0x416EU, 0x426EU, 0x436EU, 0x446EU, 0x456EU, 0x466EU, 0x476EU, 0x486EU, + 0x496EU, 0x4A6EU, 0x4B6EU, 0x4C6EU, 0x4D6EU, 0x4E6EU, 0x4F6EU, 0x506EU, + 0x516EU, 0x526EU, 0x536EU, 0x546EU, 0x556EU, 0x566EU, 0x576EU, 0x586EU, + 0x596EU, 0x5A6EU, 0x616EU, 0x626EU, 0x636EU, 0x646EU, 0x656EU, 0x666EU, + 0x676EU, 0x686EU, 0x696EU, 0x6A6EU, 0x6B6EU, 0x6C6EU, 0x6D6EU, 0x6E6EU, + 0x6F6EU, 0x706EU, 0x716EU, 0x726EU, 0x736EU, 0x746EU, 0x756EU, 0x766EU, + 0x776EU, 0x786EU, 0x796EU, 0x7A6EU, 0x306EU, 0x316EU, 0x326EU, 0x336EU, + 0x346EU, 0x356EU, 0x366EU, 0x376EU, 0x386EU, 0x396EU, 0x2B6EU, 0x2F6EU, + 0x416FU, 0x426FU, 0x436FU, 0x446FU, 0x456FU, 0x466FU, 0x476FU, 0x486FU, + 0x496FU, 0x4A6FU, 0x4B6FU, 0x4C6FU, 0x4D6FU, 0x4E6FU, 0x4F6FU, 0x506FU, + 0x516FU, 0x526FU, 0x536FU, 0x546FU, 0x556FU, 0x566FU, 0x576FU, 0x586FU, + 0x596FU, 0x5A6FU, 0x616FU, 0x626FU, 0x636FU, 0x646FU, 0x656FU, 0x666FU, + 0x676FU, 0x686FU, 0x696FU, 0x6A6FU, 0x6B6FU, 0x6C6FU, 0x6D6FU, 0x6E6FU, + 0x6F6FU, 0x706FU, 0x716FU, 0x726FU, 0x736FU, 0x746FU, 0x756FU, 0x766FU, + 0x776FU, 0x786FU, 0x796FU, 0x7A6FU, 0x306FU, 0x316FU, 0x326FU, 0x336FU, + 0x346FU, 0x356FU, 0x366FU, 0x376FU, 0x386FU, 0x396FU, 0x2B6FU, 0x2F6FU, + 0x4170U, 0x4270U, 0x4370U, 0x4470U, 0x4570U, 0x4670U, 0x4770U, 0x4870U, + 0x4970U, 0x4A70U, 0x4B70U, 0x4C70U, 0x4D70U, 0x4E70U, 0x4F70U, 0x5070U, + 0x5170U, 0x5270U, 0x5370U, 0x5470U, 0x5570U, 0x5670U, 0x5770U, 0x5870U, + 0x5970U, 0x5A70U, 0x6170U, 0x6270U, 0x6370U, 0x6470U, 0x6570U, 0x6670U, + 0x6770U, 0x6870U, 0x6970U, 0x6A70U, 0x6B70U, 0x6C70U, 0x6D70U, 0x6E70U, + 0x6F70U, 0x7070U, 0x7170U, 0x7270U, 0x7370U, 0x7470U, 0x7570U, 0x7670U, + 0x7770U, 0x7870U, 0x7970U, 0x7A70U, 0x3070U, 0x3170U, 0x3270U, 0x3370U, + 0x3470U, 0x3570U, 0x3670U, 0x3770U, 0x3870U, 0x3970U, 0x2B70U, 0x2F70U, + 0x4171U, 0x4271U, 0x4371U, 0x4471U, 0x4571U, 0x4671U, 0x4771U, 0x4871U, + 0x4971U, 0x4A71U, 0x4B71U, 0x4C71U, 0x4D71U, 0x4E71U, 0x4F71U, 0x5071U, + 0x5171U, 0x5271U, 0x5371U, 0x5471U, 0x5571U, 0x5671U, 0x5771U, 0x5871U, + 0x5971U, 0x5A71U, 0x6171U, 0x6271U, 0x6371U, 0x6471U, 0x6571U, 0x6671U, + 0x6771U, 0x6871U, 0x6971U, 0x6A71U, 0x6B71U, 0x6C71U, 0x6D71U, 0x6E71U, + 0x6F71U, 0x7071U, 0x7171U, 0x7271U, 0x7371U, 0x7471U, 0x7571U, 0x7671U, + 0x7771U, 0x7871U, 0x7971U, 0x7A71U, 0x3071U, 0x3171U, 0x3271U, 0x3371U, + 0x3471U, 0x3571U, 0x3671U, 0x3771U, 0x3871U, 0x3971U, 0x2B71U, 0x2F71U, + 0x4172U, 0x4272U, 0x4372U, 0x4472U, 0x4572U, 0x4672U, 0x4772U, 0x4872U, + 0x4972U, 0x4A72U, 0x4B72U, 0x4C72U, 0x4D72U, 0x4E72U, 0x4F72U, 0x5072U, + 0x5172U, 0x5272U, 0x5372U, 0x5472U, 0x5572U, 0x5672U, 0x5772U, 0x5872U, + 0x5972U, 0x5A72U, 0x6172U, 0x6272U, 0x6372U, 0x6472U, 0x6572U, 0x6672U, + 0x6772U, 0x6872U, 0x6972U, 0x6A72U, 0x6B72U, 0x6C72U, 0x6D72U, 0x6E72U, + 0x6F72U, 0x7072U, 0x7172U, 0x7272U, 0x7372U, 0x7472U, 0x7572U, 0x7672U, + 0x7772U, 0x7872U, 0x7972U, 0x7A72U, 0x3072U, 0x3172U, 0x3272U, 0x3372U, + 0x3472U, 0x3572U, 0x3672U, 0x3772U, 0x3872U, 0x3972U, 0x2B72U, 0x2F72U, + 0x4173U, 0x4273U, 0x4373U, 0x4473U, 0x4573U, 0x4673U, 0x4773U, 0x4873U, + 0x4973U, 0x4A73U, 0x4B73U, 0x4C73U, 0x4D73U, 0x4E73U, 0x4F73U, 0x5073U, + 0x5173U, 0x5273U, 0x5373U, 0x5473U, 0x5573U, 0x5673U, 0x5773U, 0x5873U, + 0x5973U, 0x5A73U, 0x6173U, 0x6273U, 0x6373U, 0x6473U, 0x6573U, 0x6673U, + 0x6773U, 0x6873U, 0x6973U, 0x6A73U, 0x6B73U, 0x6C73U, 0x6D73U, 0x6E73U, + 0x6F73U, 0x7073U, 0x7173U, 0x7273U, 0x7373U, 0x7473U, 0x7573U, 0x7673U, + 0x7773U, 0x7873U, 0x7973U, 0x7A73U, 0x3073U, 0x3173U, 0x3273U, 0x3373U, + 0x3473U, 0x3573U, 0x3673U, 0x3773U, 0x3873U, 0x3973U, 0x2B73U, 0x2F73U, + 0x4174U, 0x4274U, 0x4374U, 0x4474U, 0x4574U, 0x4674U, 0x4774U, 0x4874U, + 0x4974U, 0x4A74U, 0x4B74U, 0x4C74U, 0x4D74U, 0x4E74U, 0x4F74U, 0x5074U, + 0x5174U, 0x5274U, 0x5374U, 0x5474U, 0x5574U, 0x5674U, 0x5774U, 0x5874U, + 0x5974U, 0x5A74U, 0x6174U, 0x6274U, 0x6374U, 0x6474U, 0x6574U, 0x6674U, + 0x6774U, 0x6874U, 0x6974U, 0x6A74U, 0x6B74U, 0x6C74U, 0x6D74U, 0x6E74U, + 0x6F74U, 0x7074U, 0x7174U, 0x7274U, 0x7374U, 0x7474U, 0x7574U, 0x7674U, + 0x7774U, 0x7874U, 0x7974U, 0x7A74U, 0x3074U, 0x3174U, 0x3274U, 0x3374U, + 0x3474U, 0x3574U, 0x3674U, 0x3774U, 0x3874U, 0x3974U, 0x2B74U, 0x2F74U, + 0x4175U, 0x4275U, 0x4375U, 0x4475U, 0x4575U, 0x4675U, 0x4775U, 0x4875U, + 0x4975U, 0x4A75U, 0x4B75U, 0x4C75U, 0x4D75U, 0x4E75U, 0x4F75U, 0x5075U, + 0x5175U, 0x5275U, 0x5375U, 0x5475U, 0x5575U, 0x5675U, 0x5775U, 0x5875U, + 0x5975U, 0x5A75U, 0x6175U, 0x6275U, 0x6375U, 0x6475U, 0x6575U, 0x6675U, + 0x6775U, 0x6875U, 0x6975U, 0x6A75U, 0x6B75U, 0x6C75U, 0x6D75U, 0x6E75U, + 0x6F75U, 0x7075U, 0x7175U, 0x7275U, 0x7375U, 0x7475U, 0x7575U, 0x7675U, + 0x7775U, 0x7875U, 0x7975U, 0x7A75U, 0x3075U, 0x3175U, 0x3275U, 0x3375U, + 0x3475U, 0x3575U, 0x3675U, 0x3775U, 0x3875U, 0x3975U, 0x2B75U, 0x2F75U, + 0x4176U, 0x4276U, 0x4376U, 0x4476U, 0x4576U, 0x4676U, 0x4776U, 0x4876U, + 0x4976U, 0x4A76U, 0x4B76U, 0x4C76U, 0x4D76U, 0x4E76U, 0x4F76U, 0x5076U, + 0x5176U, 0x5276U, 0x5376U, 0x5476U, 0x5576U, 0x5676U, 0x5776U, 0x5876U, + 0x5976U, 0x5A76U, 0x6176U, 0x6276U, 0x6376U, 0x6476U, 0x6576U, 0x6676U, + 0x6776U, 0x6876U, 0x6976U, 0x6A76U, 0x6B76U, 0x6C76U, 0x6D76U, 0x6E76U, + 0x6F76U, 0x7076U, 0x7176U, 0x7276U, 0x7376U, 0x7476U, 0x7576U, 0x7676U, + 0x7776U, 0x7876U, 0x7976U, 0x7A76U, 0x3076U, 0x3176U, 0x3276U, 0x3376U, + 0x3476U, 0x3576U, 0x3676U, 0x3776U, 0x3876U, 0x3976U, 0x2B76U, 0x2F76U, + 0x4177U, 0x4277U, 0x4377U, 0x4477U, 0x4577U, 0x4677U, 0x4777U, 0x4877U, + 0x4977U, 0x4A77U, 0x4B77U, 0x4C77U, 0x4D77U, 0x4E77U, 0x4F77U, 0x5077U, + 0x5177U, 0x5277U, 0x5377U, 0x5477U, 0x5577U, 0x5677U, 0x5777U, 0x5877U, + 0x5977U, 0x5A77U, 0x6177U, 0x6277U, 0x6377U, 0x6477U, 0x6577U, 0x6677U, + 0x6777U, 0x6877U, 0x6977U, 0x6A77U, 0x6B77U, 0x6C77U, 0x6D77U, 0x6E77U, + 0x6F77U, 0x7077U, 0x7177U, 0x7277U, 0x7377U, 0x7477U, 0x7577U, 0x7677U, + 0x7777U, 0x7877U, 0x7977U, 0x7A77U, 0x3077U, 0x3177U, 0x3277U, 0x3377U, + 0x3477U, 0x3577U, 0x3677U, 0x3777U, 0x3877U, 0x3977U, 0x2B77U, 0x2F77U, + 0x4178U, 0x4278U, 0x4378U, 0x4478U, 0x4578U, 0x4678U, 0x4778U, 0x4878U, + 0x4978U, 0x4A78U, 0x4B78U, 0x4C78U, 0x4D78U, 0x4E78U, 0x4F78U, 0x5078U, + 0x5178U, 0x5278U, 0x5378U, 0x5478U, 0x5578U, 0x5678U, 0x5778U, 0x5878U, + 0x5978U, 0x5A78U, 0x6178U, 0x6278U, 0x6378U, 0x6478U, 0x6578U, 0x6678U, + 0x6778U, 0x6878U, 0x6978U, 0x6A78U, 0x6B78U, 0x6C78U, 0x6D78U, 0x6E78U, + 0x6F78U, 0x7078U, 0x7178U, 0x7278U, 0x7378U, 0x7478U, 0x7578U, 0x7678U, + 0x7778U, 0x7878U, 0x7978U, 0x7A78U, 0x3078U, 0x3178U, 0x3278U, 0x3378U, + 0x3478U, 0x3578U, 0x3678U, 0x3778U, 0x3878U, 0x3978U, 0x2B78U, 0x2F78U, + 0x4179U, 0x4279U, 0x4379U, 0x4479U, 0x4579U, 0x4679U, 0x4779U, 0x4879U, + 0x4979U, 0x4A79U, 0x4B79U, 0x4C79U, 0x4D79U, 0x4E79U, 0x4F79U, 0x5079U, + 0x5179U, 0x5279U, 0x5379U, 0x5479U, 0x5579U, 0x5679U, 0x5779U, 0x5879U, + 0x5979U, 0x5A79U, 0x6179U, 0x6279U, 0x6379U, 0x6479U, 0x6579U, 0x6679U, + 0x6779U, 0x6879U, 0x6979U, 0x6A79U, 0x6B79U, 0x6C79U, 0x6D79U, 0x6E79U, + 0x6F79U, 0x7079U, 0x7179U, 0x7279U, 0x7379U, 0x7479U, 0x7579U, 0x7679U, + 0x7779U, 0x7879U, 0x7979U, 0x7A79U, 0x3079U, 0x3179U, 0x3279U, 0x3379U, + 0x3479U, 0x3579U, 0x3679U, 0x3779U, 0x3879U, 0x3979U, 0x2B79U, 0x2F79U, + 0x417AU, 0x427AU, 0x437AU, 0x447AU, 0x457AU, 0x467AU, 0x477AU, 0x487AU, + 0x497AU, 0x4A7AU, 0x4B7AU, 0x4C7AU, 0x4D7AU, 0x4E7AU, 0x4F7AU, 0x507AU, + 0x517AU, 0x527AU, 0x537AU, 0x547AU, 0x557AU, 0x567AU, 0x577AU, 0x587AU, + 0x597AU, 0x5A7AU, 0x617AU, 0x627AU, 0x637AU, 0x647AU, 0x657AU, 0x667AU, + 0x677AU, 0x687AU, 0x697AU, 0x6A7AU, 0x6B7AU, 0x6C7AU, 0x6D7AU, 0x6E7AU, + 0x6F7AU, 0x707AU, 0x717AU, 0x727AU, 0x737AU, 0x747AU, 0x757AU, 0x767AU, + 0x777AU, 0x787AU, 0x797AU, 0x7A7AU, 0x307AU, 0x317AU, 0x327AU, 0x337AU, + 0x347AU, 0x357AU, 0x367AU, 0x377AU, 0x387AU, 0x397AU, 0x2B7AU, 0x2F7AU, + 0x4130U, 0x4230U, 0x4330U, 0x4430U, 0x4530U, 0x4630U, 0x4730U, 0x4830U, + 0x4930U, 0x4A30U, 0x4B30U, 0x4C30U, 0x4D30U, 0x4E30U, 0x4F30U, 0x5030U, + 0x5130U, 0x5230U, 0x5330U, 0x5430U, 0x5530U, 0x5630U, 0x5730U, 0x5830U, + 0x5930U, 0x5A30U, 0x6130U, 0x6230U, 0x6330U, 0x6430U, 0x6530U, 0x6630U, + 0x6730U, 0x6830U, 0x6930U, 0x6A30U, 0x6B30U, 0x6C30U, 0x6D30U, 0x6E30U, + 0x6F30U, 0x7030U, 0x7130U, 0x7230U, 0x7330U, 0x7430U, 0x7530U, 0x7630U, + 0x7730U, 0x7830U, 0x7930U, 0x7A30U, 0x3030U, 0x3130U, 0x3230U, 0x3330U, + 0x3430U, 0x3530U, 0x3630U, 0x3730U, 0x3830U, 0x3930U, 0x2B30U, 0x2F30U, + 0x4131U, 0x4231U, 0x4331U, 0x4431U, 0x4531U, 0x4631U, 0x4731U, 0x4831U, + 0x4931U, 0x4A31U, 0x4B31U, 0x4C31U, 0x4D31U, 0x4E31U, 0x4F31U, 0x5031U, + 0x5131U, 0x5231U, 0x5331U, 0x5431U, 0x5531U, 0x5631U, 0x5731U, 0x5831U, + 0x5931U, 0x5A31U, 0x6131U, 0x6231U, 0x6331U, 0x6431U, 0x6531U, 0x6631U, + 0x6731U, 0x6831U, 0x6931U, 0x6A31U, 0x6B31U, 0x6C31U, 0x6D31U, 0x6E31U, + 0x6F31U, 0x7031U, 0x7131U, 0x7231U, 0x7331U, 0x7431U, 0x7531U, 0x7631U, + 0x7731U, 0x7831U, 0x7931U, 0x7A31U, 0x3031U, 0x3131U, 0x3231U, 0x3331U, + 0x3431U, 0x3531U, 0x3631U, 0x3731U, 0x3831U, 0x3931U, 0x2B31U, 0x2F31U, + 0x4132U, 0x4232U, 0x4332U, 0x4432U, 0x4532U, 0x4632U, 0x4732U, 0x4832U, + 0x4932U, 0x4A32U, 0x4B32U, 0x4C32U, 0x4D32U, 0x4E32U, 0x4F32U, 0x5032U, + 0x5132U, 0x5232U, 0x5332U, 0x5432U, 0x5532U, 0x5632U, 0x5732U, 0x5832U, + 0x5932U, 0x5A32U, 0x6132U, 0x6232U, 0x6332U, 0x6432U, 0x6532U, 0x6632U, + 0x6732U, 0x6832U, 0x6932U, 0x6A32U, 0x6B32U, 0x6C32U, 0x6D32U, 0x6E32U, + 0x6F32U, 0x7032U, 0x7132U, 0x7232U, 0x7332U, 0x7432U, 0x7532U, 0x7632U, + 0x7732U, 0x7832U, 0x7932U, 0x7A32U, 0x3032U, 0x3132U, 0x3232U, 0x3332U, + 0x3432U, 0x3532U, 0x3632U, 0x3732U, 0x3832U, 0x3932U, 0x2B32U, 0x2F32U, + 0x4133U, 0x4233U, 0x4333U, 0x4433U, 0x4533U, 0x4633U, 0x4733U, 0x4833U, + 0x4933U, 0x4A33U, 0x4B33U, 0x4C33U, 0x4D33U, 0x4E33U, 0x4F33U, 0x5033U, + 0x5133U, 0x5233U, 0x5333U, 0x5433U, 0x5533U, 0x5633U, 0x5733U, 0x5833U, + 0x5933U, 0x5A33U, 0x6133U, 0x6233U, 0x6333U, 0x6433U, 0x6533U, 0x6633U, + 0x6733U, 0x6833U, 0x6933U, 0x6A33U, 0x6B33U, 0x6C33U, 0x6D33U, 0x6E33U, + 0x6F33U, 0x7033U, 0x7133U, 0x7233U, 0x7333U, 0x7433U, 0x7533U, 0x7633U, + 0x7733U, 0x7833U, 0x7933U, 0x7A33U, 0x3033U, 0x3133U, 0x3233U, 0x3333U, + 0x3433U, 0x3533U, 0x3633U, 0x3733U, 0x3833U, 0x3933U, 0x2B33U, 0x2F33U, + 0x4134U, 0x4234U, 0x4334U, 0x4434U, 0x4534U, 0x4634U, 0x4734U, 0x4834U, + 0x4934U, 0x4A34U, 0x4B34U, 0x4C34U, 0x4D34U, 0x4E34U, 0x4F34U, 0x5034U, + 0x5134U, 0x5234U, 0x5334U, 0x5434U, 0x5534U, 0x5634U, 0x5734U, 0x5834U, + 0x5934U, 0x5A34U, 0x6134U, 0x6234U, 0x6334U, 0x6434U, 0x6534U, 0x6634U, + 0x6734U, 0x6834U, 0x6934U, 0x6A34U, 0x6B34U, 0x6C34U, 0x6D34U, 0x6E34U, + 0x6F34U, 0x7034U, 0x7134U, 0x7234U, 0x7334U, 0x7434U, 0x7534U, 0x7634U, + 0x7734U, 0x7834U, 0x7934U, 0x7A34U, 0x3034U, 0x3134U, 0x3234U, 0x3334U, + 0x3434U, 0x3534U, 0x3634U, 0x3734U, 0x3834U, 0x3934U, 0x2B34U, 0x2F34U, + 0x4135U, 0x4235U, 0x4335U, 0x4435U, 0x4535U, 0x4635U, 0x4735U, 0x4835U, + 0x4935U, 0x4A35U, 0x4B35U, 0x4C35U, 0x4D35U, 0x4E35U, 0x4F35U, 0x5035U, + 0x5135U, 0x5235U, 0x5335U, 0x5435U, 0x5535U, 0x5635U, 0x5735U, 0x5835U, + 0x5935U, 0x5A35U, 0x6135U, 0x6235U, 0x6335U, 0x6435U, 0x6535U, 0x6635U, + 0x6735U, 0x6835U, 0x6935U, 0x6A35U, 0x6B35U, 0x6C35U, 0x6D35U, 0x6E35U, + 0x6F35U, 0x7035U, 0x7135U, 0x7235U, 0x7335U, 0x7435U, 0x7535U, 0x7635U, + 0x7735U, 0x7835U, 0x7935U, 0x7A35U, 0x3035U, 0x3135U, 0x3235U, 0x3335U, + 0x3435U, 0x3535U, 0x3635U, 0x3735U, 0x3835U, 0x3935U, 0x2B35U, 0x2F35U, + 0x4136U, 0x4236U, 0x4336U, 0x4436U, 0x4536U, 0x4636U, 0x4736U, 0x4836U, + 0x4936U, 0x4A36U, 0x4B36U, 0x4C36U, 0x4D36U, 0x4E36U, 0x4F36U, 0x5036U, + 0x5136U, 0x5236U, 0x5336U, 0x5436U, 0x5536U, 0x5636U, 0x5736U, 0x5836U, + 0x5936U, 0x5A36U, 0x6136U, 0x6236U, 0x6336U, 0x6436U, 0x6536U, 0x6636U, + 0x6736U, 0x6836U, 0x6936U, 0x6A36U, 0x6B36U, 0x6C36U, 0x6D36U, 0x6E36U, + 0x6F36U, 0x7036U, 0x7136U, 0x7236U, 0x7336U, 0x7436U, 0x7536U, 0x7636U, + 0x7736U, 0x7836U, 0x7936U, 0x7A36U, 0x3036U, 0x3136U, 0x3236U, 0x3336U, + 0x3436U, 0x3536U, 0x3636U, 0x3736U, 0x3836U, 0x3936U, 0x2B36U, 0x2F36U, + 0x4137U, 0x4237U, 0x4337U, 0x4437U, 0x4537U, 0x4637U, 0x4737U, 0x4837U, + 0x4937U, 0x4A37U, 0x4B37U, 0x4C37U, 0x4D37U, 0x4E37U, 0x4F37U, 0x5037U, + 0x5137U, 0x5237U, 0x5337U, 0x5437U, 0x5537U, 0x5637U, 0x5737U, 0x5837U, + 0x5937U, 0x5A37U, 0x6137U, 0x6237U, 0x6337U, 0x6437U, 0x6537U, 0x6637U, + 0x6737U, 0x6837U, 0x6937U, 0x6A37U, 0x6B37U, 0x6C37U, 0x6D37U, 0x6E37U, + 0x6F37U, 0x7037U, 0x7137U, 0x7237U, 0x7337U, 0x7437U, 0x7537U, 0x7637U, + 0x7737U, 0x7837U, 0x7937U, 0x7A37U, 0x3037U, 0x3137U, 0x3237U, 0x3337U, + 0x3437U, 0x3537U, 0x3637U, 0x3737U, 0x3837U, 0x3937U, 0x2B37U, 0x2F37U, + 0x4138U, 0x4238U, 0x4338U, 0x4438U, 0x4538U, 0x4638U, 0x4738U, 0x4838U, + 0x4938U, 0x4A38U, 0x4B38U, 0x4C38U, 0x4D38U, 0x4E38U, 0x4F38U, 0x5038U, + 0x5138U, 0x5238U, 0x5338U, 0x5438U, 0x5538U, 0x5638U, 0x5738U, 0x5838U, + 0x5938U, 0x5A38U, 0x6138U, 0x6238U, 0x6338U, 0x6438U, 0x6538U, 0x6638U, + 0x6738U, 0x6838U, 0x6938U, 0x6A38U, 0x6B38U, 0x6C38U, 0x6D38U, 0x6E38U, + 0x6F38U, 0x7038U, 0x7138U, 0x7238U, 0x7338U, 0x7438U, 0x7538U, 0x7638U, + 0x7738U, 0x7838U, 0x7938U, 0x7A38U, 0x3038U, 0x3138U, 0x3238U, 0x3338U, + 0x3438U, 0x3538U, 0x3638U, 0x3738U, 0x3838U, 0x3938U, 0x2B38U, 0x2F38U, + 0x4139U, 0x4239U, 0x4339U, 0x4439U, 0x4539U, 0x4639U, 0x4739U, 0x4839U, + 0x4939U, 0x4A39U, 0x4B39U, 0x4C39U, 0x4D39U, 0x4E39U, 0x4F39U, 0x5039U, + 0x5139U, 0x5239U, 0x5339U, 0x5439U, 0x5539U, 0x5639U, 0x5739U, 0x5839U, + 0x5939U, 0x5A39U, 0x6139U, 0x6239U, 0x6339U, 0x6439U, 0x6539U, 0x6639U, + 0x6739U, 0x6839U, 0x6939U, 0x6A39U, 0x6B39U, 0x6C39U, 0x6D39U, 0x6E39U, + 0x6F39U, 0x7039U, 0x7139U, 0x7239U, 0x7339U, 0x7439U, 0x7539U, 0x7639U, + 0x7739U, 0x7839U, 0x7939U, 0x7A39U, 0x3039U, 0x3139U, 0x3239U, 0x3339U, + 0x3439U, 0x3539U, 0x3639U, 0x3739U, 0x3839U, 0x3939U, 0x2B39U, 0x2F39U, + 0x412BU, 0x422BU, 0x432BU, 0x442BU, 0x452BU, 0x462BU, 0x472BU, 0x482BU, + 0x492BU, 0x4A2BU, 0x4B2BU, 0x4C2BU, 0x4D2BU, 0x4E2BU, 0x4F2BU, 0x502BU, + 0x512BU, 0x522BU, 0x532BU, 0x542BU, 0x552BU, 0x562BU, 0x572BU, 0x582BU, + 0x592BU, 0x5A2BU, 0x612BU, 0x622BU, 0x632BU, 0x642BU, 0x652BU, 0x662BU, + 0x672BU, 0x682BU, 0x692BU, 0x6A2BU, 0x6B2BU, 0x6C2BU, 0x6D2BU, 0x6E2BU, + 0x6F2BU, 0x702BU, 0x712BU, 0x722BU, 0x732BU, 0x742BU, 0x752BU, 0x762BU, + 0x772BU, 0x782BU, 0x792BU, 0x7A2BU, 0x302BU, 0x312BU, 0x322BU, 0x332BU, + 0x342BU, 0x352BU, 0x362BU, 0x372BU, 0x382BU, 0x392BU, 0x2B2BU, 0x2F2BU, + 0x412FU, 0x422FU, 0x432FU, 0x442FU, 0x452FU, 0x462FU, 0x472FU, 0x482FU, + 0x492FU, 0x4A2FU, 0x4B2FU, 0x4C2FU, 0x4D2FU, 0x4E2FU, 0x4F2FU, 0x502FU, + 0x512FU, 0x522FU, 0x532FU, 0x542FU, 0x552FU, 0x562FU, 0x572FU, 0x582FU, + 0x592FU, 0x5A2FU, 0x612FU, 0x622FU, 0x632FU, 0x642FU, 0x652FU, 0x662FU, + 0x672FU, 0x682FU, 0x692FU, 0x6A2FU, 0x6B2FU, 0x6C2FU, 0x6D2FU, 0x6E2FU, + 0x6F2FU, 0x702FU, 0x712FU, 0x722FU, 0x732FU, 0x742FU, 0x752FU, 0x762FU, + 0x772FU, 0x782FU, 0x792FU, 0x7A2FU, 0x302FU, 0x312FU, 0x322FU, 0x332FU, + 0x342FU, 0x352FU, 0x362FU, 0x372FU, 0x382FU, 0x392FU, 0x2B2FU, 0x2F2FU, +#else + 0x4141U, 0x4142U, 0x4143U, 0x4144U, 0x4145U, 0x4146U, 0x4147U, 0x4148U, + 0x4149U, 0x414AU, 0x414BU, 0x414CU, 0x414DU, 0x414EU, 0x414FU, 0x4150U, + 0x4151U, 0x4152U, 0x4153U, 0x4154U, 0x4155U, 0x4156U, 0x4157U, 0x4158U, + 0x4159U, 0x415AU, 0x4161U, 0x4162U, 0x4163U, 0x4164U, 0x4165U, 0x4166U, + 0x4167U, 0x4168U, 0x4169U, 0x416AU, 0x416BU, 0x416CU, 0x416DU, 0x416EU, + 0x416FU, 0x4170U, 0x4171U, 0x4172U, 0x4173U, 0x4174U, 0x4175U, 0x4176U, + 0x4177U, 0x4178U, 0x4179U, 0x417AU, 0x4130U, 0x4131U, 0x4132U, 0x4133U, + 0x4134U, 0x4135U, 0x4136U, 0x4137U, 0x4138U, 0x4139U, 0x412BU, 0x412FU, + 0x4241U, 0x4242U, 0x4243U, 0x4244U, 0x4245U, 0x4246U, 0x4247U, 0x4248U, + 0x4249U, 0x424AU, 0x424BU, 0x424CU, 0x424DU, 0x424EU, 0x424FU, 0x4250U, + 0x4251U, 0x4252U, 0x4253U, 0x4254U, 0x4255U, 0x4256U, 0x4257U, 0x4258U, + 0x4259U, 0x425AU, 0x4261U, 0x4262U, 0x4263U, 0x4264U, 0x4265U, 0x4266U, + 0x4267U, 0x4268U, 0x4269U, 0x426AU, 0x426BU, 0x426CU, 0x426DU, 0x426EU, + 0x426FU, 0x4270U, 0x4271U, 0x4272U, 0x4273U, 0x4274U, 0x4275U, 0x4276U, + 0x4277U, 0x4278U, 0x4279U, 0x427AU, 0x4230U, 0x4231U, 0x4232U, 0x4233U, + 0x4234U, 0x4235U, 0x4236U, 0x4237U, 0x4238U, 0x4239U, 0x422BU, 0x422FU, + 0x4341U, 0x4342U, 0x4343U, 0x4344U, 0x4345U, 0x4346U, 0x4347U, 0x4348U, + 0x4349U, 0x434AU, 0x434BU, 0x434CU, 0x434DU, 0x434EU, 0x434FU, 0x4350U, + 0x4351U, 0x4352U, 0x4353U, 0x4354U, 0x4355U, 0x4356U, 0x4357U, 0x4358U, + 0x4359U, 0x435AU, 0x4361U, 0x4362U, 0x4363U, 0x4364U, 0x4365U, 0x4366U, + 0x4367U, 0x4368U, 0x4369U, 0x436AU, 0x436BU, 0x436CU, 0x436DU, 0x436EU, + 0x436FU, 0x4370U, 0x4371U, 0x4372U, 0x4373U, 0x4374U, 0x4375U, 0x4376U, + 0x4377U, 0x4378U, 0x4379U, 0x437AU, 0x4330U, 0x4331U, 0x4332U, 0x4333U, + 0x4334U, 0x4335U, 0x4336U, 0x4337U, 0x4338U, 0x4339U, 0x432BU, 0x432FU, + 0x4441U, 0x4442U, 0x4443U, 0x4444U, 0x4445U, 0x4446U, 0x4447U, 0x4448U, + 0x4449U, 0x444AU, 0x444BU, 0x444CU, 0x444DU, 0x444EU, 0x444FU, 0x4450U, + 0x4451U, 0x4452U, 0x4453U, 0x4454U, 0x4455U, 0x4456U, 0x4457U, 0x4458U, + 0x4459U, 0x445AU, 0x4461U, 0x4462U, 0x4463U, 0x4464U, 0x4465U, 0x4466U, + 0x4467U, 0x4468U, 0x4469U, 0x446AU, 0x446BU, 0x446CU, 0x446DU, 0x446EU, + 0x446FU, 0x4470U, 0x4471U, 0x4472U, 0x4473U, 0x4474U, 0x4475U, 0x4476U, + 0x4477U, 0x4478U, 0x4479U, 0x447AU, 0x4430U, 0x4431U, 0x4432U, 0x4433U, + 0x4434U, 0x4435U, 0x4436U, 0x4437U, 0x4438U, 0x4439U, 0x442BU, 0x442FU, + 0x4541U, 0x4542U, 0x4543U, 0x4544U, 0x4545U, 0x4546U, 0x4547U, 0x4548U, + 0x4549U, 0x454AU, 0x454BU, 0x454CU, 0x454DU, 0x454EU, 0x454FU, 0x4550U, + 0x4551U, 0x4552U, 0x4553U, 0x4554U, 0x4555U, 0x4556U, 0x4557U, 0x4558U, + 0x4559U, 0x455AU, 0x4561U, 0x4562U, 0x4563U, 0x4564U, 0x4565U, 0x4566U, + 0x4567U, 0x4568U, 0x4569U, 0x456AU, 0x456BU, 0x456CU, 0x456DU, 0x456EU, + 0x456FU, 0x4570U, 0x4571U, 0x4572U, 0x4573U, 0x4574U, 0x4575U, 0x4576U, + 0x4577U, 0x4578U, 0x4579U, 0x457AU, 0x4530U, 0x4531U, 0x4532U, 0x4533U, + 0x4534U, 0x4535U, 0x4536U, 0x4537U, 0x4538U, 0x4539U, 0x452BU, 0x452FU, + 0x4641U, 0x4642U, 0x4643U, 0x4644U, 0x4645U, 0x4646U, 0x4647U, 0x4648U, + 0x4649U, 0x464AU, 0x464BU, 0x464CU, 0x464DU, 0x464EU, 0x464FU, 0x4650U, + 0x4651U, 0x4652U, 0x4653U, 0x4654U, 0x4655U, 0x4656U, 0x4657U, 0x4658U, + 0x4659U, 0x465AU, 0x4661U, 0x4662U, 0x4663U, 0x4664U, 0x4665U, 0x4666U, + 0x4667U, 0x4668U, 0x4669U, 0x466AU, 0x466BU, 0x466CU, 0x466DU, 0x466EU, + 0x466FU, 0x4670U, 0x4671U, 0x4672U, 0x4673U, 0x4674U, 0x4675U, 0x4676U, + 0x4677U, 0x4678U, 0x4679U, 0x467AU, 0x4630U, 0x4631U, 0x4632U, 0x4633U, + 0x4634U, 0x4635U, 0x4636U, 0x4637U, 0x4638U, 0x4639U, 0x462BU, 0x462FU, + 0x4741U, 0x4742U, 0x4743U, 0x4744U, 0x4745U, 0x4746U, 0x4747U, 0x4748U, + 0x4749U, 0x474AU, 0x474BU, 0x474CU, 0x474DU, 0x474EU, 0x474FU, 0x4750U, + 0x4751U, 0x4752U, 0x4753U, 0x4754U, 0x4755U, 0x4756U, 0x4757U, 0x4758U, + 0x4759U, 0x475AU, 0x4761U, 0x4762U, 0x4763U, 0x4764U, 0x4765U, 0x4766U, + 0x4767U, 0x4768U, 0x4769U, 0x476AU, 0x476BU, 0x476CU, 0x476DU, 0x476EU, + 0x476FU, 0x4770U, 0x4771U, 0x4772U, 0x4773U, 0x4774U, 0x4775U, 0x4776U, + 0x4777U, 0x4778U, 0x4779U, 0x477AU, 0x4730U, 0x4731U, 0x4732U, 0x4733U, + 0x4734U, 0x4735U, 0x4736U, 0x4737U, 0x4738U, 0x4739U, 0x472BU, 0x472FU, + 0x4841U, 0x4842U, 0x4843U, 0x4844U, 0x4845U, 0x4846U, 0x4847U, 0x4848U, + 0x4849U, 0x484AU, 0x484BU, 0x484CU, 0x484DU, 0x484EU, 0x484FU, 0x4850U, + 0x4851U, 0x4852U, 0x4853U, 0x4854U, 0x4855U, 0x4856U, 0x4857U, 0x4858U, + 0x4859U, 0x485AU, 0x4861U, 0x4862U, 0x4863U, 0x4864U, 0x4865U, 0x4866U, + 0x4867U, 0x4868U, 0x4869U, 0x486AU, 0x486BU, 0x486CU, 0x486DU, 0x486EU, + 0x486FU, 0x4870U, 0x4871U, 0x4872U, 0x4873U, 0x4874U, 0x4875U, 0x4876U, + 0x4877U, 0x4878U, 0x4879U, 0x487AU, 0x4830U, 0x4831U, 0x4832U, 0x4833U, + 0x4834U, 0x4835U, 0x4836U, 0x4837U, 0x4838U, 0x4839U, 0x482BU, 0x482FU, + 0x4941U, 0x4942U, 0x4943U, 0x4944U, 0x4945U, 0x4946U, 0x4947U, 0x4948U, + 0x4949U, 0x494AU, 0x494BU, 0x494CU, 0x494DU, 0x494EU, 0x494FU, 0x4950U, + 0x4951U, 0x4952U, 0x4953U, 0x4954U, 0x4955U, 0x4956U, 0x4957U, 0x4958U, + 0x4959U, 0x495AU, 0x4961U, 0x4962U, 0x4963U, 0x4964U, 0x4965U, 0x4966U, + 0x4967U, 0x4968U, 0x4969U, 0x496AU, 0x496BU, 0x496CU, 0x496DU, 0x496EU, + 0x496FU, 0x4970U, 0x4971U, 0x4972U, 0x4973U, 0x4974U, 0x4975U, 0x4976U, + 0x4977U, 0x4978U, 0x4979U, 0x497AU, 0x4930U, 0x4931U, 0x4932U, 0x4933U, + 0x4934U, 0x4935U, 0x4936U, 0x4937U, 0x4938U, 0x4939U, 0x492BU, 0x492FU, + 0x4A41U, 0x4A42U, 0x4A43U, 0x4A44U, 0x4A45U, 0x4A46U, 0x4A47U, 0x4A48U, + 0x4A49U, 0x4A4AU, 0x4A4BU, 0x4A4CU, 0x4A4DU, 0x4A4EU, 0x4A4FU, 0x4A50U, + 0x4A51U, 0x4A52U, 0x4A53U, 0x4A54U, 0x4A55U, 0x4A56U, 0x4A57U, 0x4A58U, + 0x4A59U, 0x4A5AU, 0x4A61U, 0x4A62U, 0x4A63U, 0x4A64U, 0x4A65U, 0x4A66U, + 0x4A67U, 0x4A68U, 0x4A69U, 0x4A6AU, 0x4A6BU, 0x4A6CU, 0x4A6DU, 0x4A6EU, + 0x4A6FU, 0x4A70U, 0x4A71U, 0x4A72U, 0x4A73U, 0x4A74U, 0x4A75U, 0x4A76U, + 0x4A77U, 0x4A78U, 0x4A79U, 0x4A7AU, 0x4A30U, 0x4A31U, 0x4A32U, 0x4A33U, + 0x4A34U, 0x4A35U, 0x4A36U, 0x4A37U, 0x4A38U, 0x4A39U, 0x4A2BU, 0x4A2FU, + 0x4B41U, 0x4B42U, 0x4B43U, 0x4B44U, 0x4B45U, 0x4B46U, 0x4B47U, 0x4B48U, + 0x4B49U, 0x4B4AU, 0x4B4BU, 0x4B4CU, 0x4B4DU, 0x4B4EU, 0x4B4FU, 0x4B50U, + 0x4B51U, 0x4B52U, 0x4B53U, 0x4B54U, 0x4B55U, 0x4B56U, 0x4B57U, 0x4B58U, + 0x4B59U, 0x4B5AU, 0x4B61U, 0x4B62U, 0x4B63U, 0x4B64U, 0x4B65U, 0x4B66U, + 0x4B67U, 0x4B68U, 0x4B69U, 0x4B6AU, 0x4B6BU, 0x4B6CU, 0x4B6DU, 0x4B6EU, + 0x4B6FU, 0x4B70U, 0x4B71U, 0x4B72U, 0x4B73U, 0x4B74U, 0x4B75U, 0x4B76U, + 0x4B77U, 0x4B78U, 0x4B79U, 0x4B7AU, 0x4B30U, 0x4B31U, 0x4B32U, 0x4B33U, + 0x4B34U, 0x4B35U, 0x4B36U, 0x4B37U, 0x4B38U, 0x4B39U, 0x4B2BU, 0x4B2FU, + 0x4C41U, 0x4C42U, 0x4C43U, 0x4C44U, 0x4C45U, 0x4C46U, 0x4C47U, 0x4C48U, + 0x4C49U, 0x4C4AU, 0x4C4BU, 0x4C4CU, 0x4C4DU, 0x4C4EU, 0x4C4FU, 0x4C50U, + 0x4C51U, 0x4C52U, 0x4C53U, 0x4C54U, 0x4C55U, 0x4C56U, 0x4C57U, 0x4C58U, + 0x4C59U, 0x4C5AU, 0x4C61U, 0x4C62U, 0x4C63U, 0x4C64U, 0x4C65U, 0x4C66U, + 0x4C67U, 0x4C68U, 0x4C69U, 0x4C6AU, 0x4C6BU, 0x4C6CU, 0x4C6DU, 0x4C6EU, + 0x4C6FU, 0x4C70U, 0x4C71U, 0x4C72U, 0x4C73U, 0x4C74U, 0x4C75U, 0x4C76U, + 0x4C77U, 0x4C78U, 0x4C79U, 0x4C7AU, 0x4C30U, 0x4C31U, 0x4C32U, 0x4C33U, + 0x4C34U, 0x4C35U, 0x4C36U, 0x4C37U, 0x4C38U, 0x4C39U, 0x4C2BU, 0x4C2FU, + 0x4D41U, 0x4D42U, 0x4D43U, 0x4D44U, 0x4D45U, 0x4D46U, 0x4D47U, 0x4D48U, + 0x4D49U, 0x4D4AU, 0x4D4BU, 0x4D4CU, 0x4D4DU, 0x4D4EU, 0x4D4FU, 0x4D50U, + 0x4D51U, 0x4D52U, 0x4D53U, 0x4D54U, 0x4D55U, 0x4D56U, 0x4D57U, 0x4D58U, + 0x4D59U, 0x4D5AU, 0x4D61U, 0x4D62U, 0x4D63U, 0x4D64U, 0x4D65U, 0x4D66U, + 0x4D67U, 0x4D68U, 0x4D69U, 0x4D6AU, 0x4D6BU, 0x4D6CU, 0x4D6DU, 0x4D6EU, + 0x4D6FU, 0x4D70U, 0x4D71U, 0x4D72U, 0x4D73U, 0x4D74U, 0x4D75U, 0x4D76U, + 0x4D77U, 0x4D78U, 0x4D79U, 0x4D7AU, 0x4D30U, 0x4D31U, 0x4D32U, 0x4D33U, + 0x4D34U, 0x4D35U, 0x4D36U, 0x4D37U, 0x4D38U, 0x4D39U, 0x4D2BU, 0x4D2FU, + 0x4E41U, 0x4E42U, 0x4E43U, 0x4E44U, 0x4E45U, 0x4E46U, 0x4E47U, 0x4E48U, + 0x4E49U, 0x4E4AU, 0x4E4BU, 0x4E4CU, 0x4E4DU, 0x4E4EU, 0x4E4FU, 0x4E50U, + 0x4E51U, 0x4E52U, 0x4E53U, 0x4E54U, 0x4E55U, 0x4E56U, 0x4E57U, 0x4E58U, + 0x4E59U, 0x4E5AU, 0x4E61U, 0x4E62U, 0x4E63U, 0x4E64U, 0x4E65U, 0x4E66U, + 0x4E67U, 0x4E68U, 0x4E69U, 0x4E6AU, 0x4E6BU, 0x4E6CU, 0x4E6DU, 0x4E6EU, + 0x4E6FU, 0x4E70U, 0x4E71U, 0x4E72U, 0x4E73U, 0x4E74U, 0x4E75U, 0x4E76U, + 0x4E77U, 0x4E78U, 0x4E79U, 0x4E7AU, 0x4E30U, 0x4E31U, 0x4E32U, 0x4E33U, + 0x4E34U, 0x4E35U, 0x4E36U, 0x4E37U, 0x4E38U, 0x4E39U, 0x4E2BU, 0x4E2FU, + 0x4F41U, 0x4F42U, 0x4F43U, 0x4F44U, 0x4F45U, 0x4F46U, 0x4F47U, 0x4F48U, + 0x4F49U, 0x4F4AU, 0x4F4BU, 0x4F4CU, 0x4F4DU, 0x4F4EU, 0x4F4FU, 0x4F50U, + 0x4F51U, 0x4F52U, 0x4F53U, 0x4F54U, 0x4F55U, 0x4F56U, 0x4F57U, 0x4F58U, + 0x4F59U, 0x4F5AU, 0x4F61U, 0x4F62U, 0x4F63U, 0x4F64U, 0x4F65U, 0x4F66U, + 0x4F67U, 0x4F68U, 0x4F69U, 0x4F6AU, 0x4F6BU, 0x4F6CU, 0x4F6DU, 0x4F6EU, + 0x4F6FU, 0x4F70U, 0x4F71U, 0x4F72U, 0x4F73U, 0x4F74U, 0x4F75U, 0x4F76U, + 0x4F77U, 0x4F78U, 0x4F79U, 0x4F7AU, 0x4F30U, 0x4F31U, 0x4F32U, 0x4F33U, + 0x4F34U, 0x4F35U, 0x4F36U, 0x4F37U, 0x4F38U, 0x4F39U, 0x4F2BU, 0x4F2FU, + 0x5041U, 0x5042U, 0x5043U, 0x5044U, 0x5045U, 0x5046U, 0x5047U, 0x5048U, + 0x5049U, 0x504AU, 0x504BU, 0x504CU, 0x504DU, 0x504EU, 0x504FU, 0x5050U, + 0x5051U, 0x5052U, 0x5053U, 0x5054U, 0x5055U, 0x5056U, 0x5057U, 0x5058U, + 0x5059U, 0x505AU, 0x5061U, 0x5062U, 0x5063U, 0x5064U, 0x5065U, 0x5066U, + 0x5067U, 0x5068U, 0x5069U, 0x506AU, 0x506BU, 0x506CU, 0x506DU, 0x506EU, + 0x506FU, 0x5070U, 0x5071U, 0x5072U, 0x5073U, 0x5074U, 0x5075U, 0x5076U, + 0x5077U, 0x5078U, 0x5079U, 0x507AU, 0x5030U, 0x5031U, 0x5032U, 0x5033U, + 0x5034U, 0x5035U, 0x5036U, 0x5037U, 0x5038U, 0x5039U, 0x502BU, 0x502FU, + 0x5141U, 0x5142U, 0x5143U, 0x5144U, 0x5145U, 0x5146U, 0x5147U, 0x5148U, + 0x5149U, 0x514AU, 0x514BU, 0x514CU, 0x514DU, 0x514EU, 0x514FU, 0x5150U, + 0x5151U, 0x5152U, 0x5153U, 0x5154U, 0x5155U, 0x5156U, 0x5157U, 0x5158U, + 0x5159U, 0x515AU, 0x5161U, 0x5162U, 0x5163U, 0x5164U, 0x5165U, 0x5166U, + 0x5167U, 0x5168U, 0x5169U, 0x516AU, 0x516BU, 0x516CU, 0x516DU, 0x516EU, + 0x516FU, 0x5170U, 0x5171U, 0x5172U, 0x5173U, 0x5174U, 0x5175U, 0x5176U, + 0x5177U, 0x5178U, 0x5179U, 0x517AU, 0x5130U, 0x5131U, 0x5132U, 0x5133U, + 0x5134U, 0x5135U, 0x5136U, 0x5137U, 0x5138U, 0x5139U, 0x512BU, 0x512FU, + 0x5241U, 0x5242U, 0x5243U, 0x5244U, 0x5245U, 0x5246U, 0x5247U, 0x5248U, + 0x5249U, 0x524AU, 0x524BU, 0x524CU, 0x524DU, 0x524EU, 0x524FU, 0x5250U, + 0x5251U, 0x5252U, 0x5253U, 0x5254U, 0x5255U, 0x5256U, 0x5257U, 0x5258U, + 0x5259U, 0x525AU, 0x5261U, 0x5262U, 0x5263U, 0x5264U, 0x5265U, 0x5266U, + 0x5267U, 0x5268U, 0x5269U, 0x526AU, 0x526BU, 0x526CU, 0x526DU, 0x526EU, + 0x526FU, 0x5270U, 0x5271U, 0x5272U, 0x5273U, 0x5274U, 0x5275U, 0x5276U, + 0x5277U, 0x5278U, 0x5279U, 0x527AU, 0x5230U, 0x5231U, 0x5232U, 0x5233U, + 0x5234U, 0x5235U, 0x5236U, 0x5237U, 0x5238U, 0x5239U, 0x522BU, 0x522FU, + 0x5341U, 0x5342U, 0x5343U, 0x5344U, 0x5345U, 0x5346U, 0x5347U, 0x5348U, + 0x5349U, 0x534AU, 0x534BU, 0x534CU, 0x534DU, 0x534EU, 0x534FU, 0x5350U, + 0x5351U, 0x5352U, 0x5353U, 0x5354U, 0x5355U, 0x5356U, 0x5357U, 0x5358U, + 0x5359U, 0x535AU, 0x5361U, 0x5362U, 0x5363U, 0x5364U, 0x5365U, 0x5366U, + 0x5367U, 0x5368U, 0x5369U, 0x536AU, 0x536BU, 0x536CU, 0x536DU, 0x536EU, + 0x536FU, 0x5370U, 0x5371U, 0x5372U, 0x5373U, 0x5374U, 0x5375U, 0x5376U, + 0x5377U, 0x5378U, 0x5379U, 0x537AU, 0x5330U, 0x5331U, 0x5332U, 0x5333U, + 0x5334U, 0x5335U, 0x5336U, 0x5337U, 0x5338U, 0x5339U, 0x532BU, 0x532FU, + 0x5441U, 0x5442U, 0x5443U, 0x5444U, 0x5445U, 0x5446U, 0x5447U, 0x5448U, + 0x5449U, 0x544AU, 0x544BU, 0x544CU, 0x544DU, 0x544EU, 0x544FU, 0x5450U, + 0x5451U, 0x5452U, 0x5453U, 0x5454U, 0x5455U, 0x5456U, 0x5457U, 0x5458U, + 0x5459U, 0x545AU, 0x5461U, 0x5462U, 0x5463U, 0x5464U, 0x5465U, 0x5466U, + 0x5467U, 0x5468U, 0x5469U, 0x546AU, 0x546BU, 0x546CU, 0x546DU, 0x546EU, + 0x546FU, 0x5470U, 0x5471U, 0x5472U, 0x5473U, 0x5474U, 0x5475U, 0x5476U, + 0x5477U, 0x5478U, 0x5479U, 0x547AU, 0x5430U, 0x5431U, 0x5432U, 0x5433U, + 0x5434U, 0x5435U, 0x5436U, 0x5437U, 0x5438U, 0x5439U, 0x542BU, 0x542FU, + 0x5541U, 0x5542U, 0x5543U, 0x5544U, 0x5545U, 0x5546U, 0x5547U, 0x5548U, + 0x5549U, 0x554AU, 0x554BU, 0x554CU, 0x554DU, 0x554EU, 0x554FU, 0x5550U, + 0x5551U, 0x5552U, 0x5553U, 0x5554U, 0x5555U, 0x5556U, 0x5557U, 0x5558U, + 0x5559U, 0x555AU, 0x5561U, 0x5562U, 0x5563U, 0x5564U, 0x5565U, 0x5566U, + 0x5567U, 0x5568U, 0x5569U, 0x556AU, 0x556BU, 0x556CU, 0x556DU, 0x556EU, + 0x556FU, 0x5570U, 0x5571U, 0x5572U, 0x5573U, 0x5574U, 0x5575U, 0x5576U, + 0x5577U, 0x5578U, 0x5579U, 0x557AU, 0x5530U, 0x5531U, 0x5532U, 0x5533U, + 0x5534U, 0x5535U, 0x5536U, 0x5537U, 0x5538U, 0x5539U, 0x552BU, 0x552FU, + 0x5641U, 0x5642U, 0x5643U, 0x5644U, 0x5645U, 0x5646U, 0x5647U, 0x5648U, + 0x5649U, 0x564AU, 0x564BU, 0x564CU, 0x564DU, 0x564EU, 0x564FU, 0x5650U, + 0x5651U, 0x5652U, 0x5653U, 0x5654U, 0x5655U, 0x5656U, 0x5657U, 0x5658U, + 0x5659U, 0x565AU, 0x5661U, 0x5662U, 0x5663U, 0x5664U, 0x5665U, 0x5666U, + 0x5667U, 0x5668U, 0x5669U, 0x566AU, 0x566BU, 0x566CU, 0x566DU, 0x566EU, + 0x566FU, 0x5670U, 0x5671U, 0x5672U, 0x5673U, 0x5674U, 0x5675U, 0x5676U, + 0x5677U, 0x5678U, 0x5679U, 0x567AU, 0x5630U, 0x5631U, 0x5632U, 0x5633U, + 0x5634U, 0x5635U, 0x5636U, 0x5637U, 0x5638U, 0x5639U, 0x562BU, 0x562FU, + 0x5741U, 0x5742U, 0x5743U, 0x5744U, 0x5745U, 0x5746U, 0x5747U, 0x5748U, + 0x5749U, 0x574AU, 0x574BU, 0x574CU, 0x574DU, 0x574EU, 0x574FU, 0x5750U, + 0x5751U, 0x5752U, 0x5753U, 0x5754U, 0x5755U, 0x5756U, 0x5757U, 0x5758U, + 0x5759U, 0x575AU, 0x5761U, 0x5762U, 0x5763U, 0x5764U, 0x5765U, 0x5766U, + 0x5767U, 0x5768U, 0x5769U, 0x576AU, 0x576BU, 0x576CU, 0x576DU, 0x576EU, + 0x576FU, 0x5770U, 0x5771U, 0x5772U, 0x5773U, 0x5774U, 0x5775U, 0x5776U, + 0x5777U, 0x5778U, 0x5779U, 0x577AU, 0x5730U, 0x5731U, 0x5732U, 0x5733U, + 0x5734U, 0x5735U, 0x5736U, 0x5737U, 0x5738U, 0x5739U, 0x572BU, 0x572FU, + 0x5841U, 0x5842U, 0x5843U, 0x5844U, 0x5845U, 0x5846U, 0x5847U, 0x5848U, + 0x5849U, 0x584AU, 0x584BU, 0x584CU, 0x584DU, 0x584EU, 0x584FU, 0x5850U, + 0x5851U, 0x5852U, 0x5853U, 0x5854U, 0x5855U, 0x5856U, 0x5857U, 0x5858U, + 0x5859U, 0x585AU, 0x5861U, 0x5862U, 0x5863U, 0x5864U, 0x5865U, 0x5866U, + 0x5867U, 0x5868U, 0x5869U, 0x586AU, 0x586BU, 0x586CU, 0x586DU, 0x586EU, + 0x586FU, 0x5870U, 0x5871U, 0x5872U, 0x5873U, 0x5874U, 0x5875U, 0x5876U, + 0x5877U, 0x5878U, 0x5879U, 0x587AU, 0x5830U, 0x5831U, 0x5832U, 0x5833U, + 0x5834U, 0x5835U, 0x5836U, 0x5837U, 0x5838U, 0x5839U, 0x582BU, 0x582FU, + 0x5941U, 0x5942U, 0x5943U, 0x5944U, 0x5945U, 0x5946U, 0x5947U, 0x5948U, + 0x5949U, 0x594AU, 0x594BU, 0x594CU, 0x594DU, 0x594EU, 0x594FU, 0x5950U, + 0x5951U, 0x5952U, 0x5953U, 0x5954U, 0x5955U, 0x5956U, 0x5957U, 0x5958U, + 0x5959U, 0x595AU, 0x5961U, 0x5962U, 0x5963U, 0x5964U, 0x5965U, 0x5966U, + 0x5967U, 0x5968U, 0x5969U, 0x596AU, 0x596BU, 0x596CU, 0x596DU, 0x596EU, + 0x596FU, 0x5970U, 0x5971U, 0x5972U, 0x5973U, 0x5974U, 0x5975U, 0x5976U, + 0x5977U, 0x5978U, 0x5979U, 0x597AU, 0x5930U, 0x5931U, 0x5932U, 0x5933U, + 0x5934U, 0x5935U, 0x5936U, 0x5937U, 0x5938U, 0x5939U, 0x592BU, 0x592FU, + 0x5A41U, 0x5A42U, 0x5A43U, 0x5A44U, 0x5A45U, 0x5A46U, 0x5A47U, 0x5A48U, + 0x5A49U, 0x5A4AU, 0x5A4BU, 0x5A4CU, 0x5A4DU, 0x5A4EU, 0x5A4FU, 0x5A50U, + 0x5A51U, 0x5A52U, 0x5A53U, 0x5A54U, 0x5A55U, 0x5A56U, 0x5A57U, 0x5A58U, + 0x5A59U, 0x5A5AU, 0x5A61U, 0x5A62U, 0x5A63U, 0x5A64U, 0x5A65U, 0x5A66U, + 0x5A67U, 0x5A68U, 0x5A69U, 0x5A6AU, 0x5A6BU, 0x5A6CU, 0x5A6DU, 0x5A6EU, + 0x5A6FU, 0x5A70U, 0x5A71U, 0x5A72U, 0x5A73U, 0x5A74U, 0x5A75U, 0x5A76U, + 0x5A77U, 0x5A78U, 0x5A79U, 0x5A7AU, 0x5A30U, 0x5A31U, 0x5A32U, 0x5A33U, + 0x5A34U, 0x5A35U, 0x5A36U, 0x5A37U, 0x5A38U, 0x5A39U, 0x5A2BU, 0x5A2FU, + 0x6141U, 0x6142U, 0x6143U, 0x6144U, 0x6145U, 0x6146U, 0x6147U, 0x6148U, + 0x6149U, 0x614AU, 0x614BU, 0x614CU, 0x614DU, 0x614EU, 0x614FU, 0x6150U, + 0x6151U, 0x6152U, 0x6153U, 0x6154U, 0x6155U, 0x6156U, 0x6157U, 0x6158U, + 0x6159U, 0x615AU, 0x6161U, 0x6162U, 0x6163U, 0x6164U, 0x6165U, 0x6166U, + 0x6167U, 0x6168U, 0x6169U, 0x616AU, 0x616BU, 0x616CU, 0x616DU, 0x616EU, + 0x616FU, 0x6170U, 0x6171U, 0x6172U, 0x6173U, 0x6174U, 0x6175U, 0x6176U, + 0x6177U, 0x6178U, 0x6179U, 0x617AU, 0x6130U, 0x6131U, 0x6132U, 0x6133U, + 0x6134U, 0x6135U, 0x6136U, 0x6137U, 0x6138U, 0x6139U, 0x612BU, 0x612FU, + 0x6241U, 0x6242U, 0x6243U, 0x6244U, 0x6245U, 0x6246U, 0x6247U, 0x6248U, + 0x6249U, 0x624AU, 0x624BU, 0x624CU, 0x624DU, 0x624EU, 0x624FU, 0x6250U, + 0x6251U, 0x6252U, 0x6253U, 0x6254U, 0x6255U, 0x6256U, 0x6257U, 0x6258U, + 0x6259U, 0x625AU, 0x6261U, 0x6262U, 0x6263U, 0x6264U, 0x6265U, 0x6266U, + 0x6267U, 0x6268U, 0x6269U, 0x626AU, 0x626BU, 0x626CU, 0x626DU, 0x626EU, + 0x626FU, 0x6270U, 0x6271U, 0x6272U, 0x6273U, 0x6274U, 0x6275U, 0x6276U, + 0x6277U, 0x6278U, 0x6279U, 0x627AU, 0x6230U, 0x6231U, 0x6232U, 0x6233U, + 0x6234U, 0x6235U, 0x6236U, 0x6237U, 0x6238U, 0x6239U, 0x622BU, 0x622FU, + 0x6341U, 0x6342U, 0x6343U, 0x6344U, 0x6345U, 0x6346U, 0x6347U, 0x6348U, + 0x6349U, 0x634AU, 0x634BU, 0x634CU, 0x634DU, 0x634EU, 0x634FU, 0x6350U, + 0x6351U, 0x6352U, 0x6353U, 0x6354U, 0x6355U, 0x6356U, 0x6357U, 0x6358U, + 0x6359U, 0x635AU, 0x6361U, 0x6362U, 0x6363U, 0x6364U, 0x6365U, 0x6366U, + 0x6367U, 0x6368U, 0x6369U, 0x636AU, 0x636BU, 0x636CU, 0x636DU, 0x636EU, + 0x636FU, 0x6370U, 0x6371U, 0x6372U, 0x6373U, 0x6374U, 0x6375U, 0x6376U, + 0x6377U, 0x6378U, 0x6379U, 0x637AU, 0x6330U, 0x6331U, 0x6332U, 0x6333U, + 0x6334U, 0x6335U, 0x6336U, 0x6337U, 0x6338U, 0x6339U, 0x632BU, 0x632FU, + 0x6441U, 0x6442U, 0x6443U, 0x6444U, 0x6445U, 0x6446U, 0x6447U, 0x6448U, + 0x6449U, 0x644AU, 0x644BU, 0x644CU, 0x644DU, 0x644EU, 0x644FU, 0x6450U, + 0x6451U, 0x6452U, 0x6453U, 0x6454U, 0x6455U, 0x6456U, 0x6457U, 0x6458U, + 0x6459U, 0x645AU, 0x6461U, 0x6462U, 0x6463U, 0x6464U, 0x6465U, 0x6466U, + 0x6467U, 0x6468U, 0x6469U, 0x646AU, 0x646BU, 0x646CU, 0x646DU, 0x646EU, + 0x646FU, 0x6470U, 0x6471U, 0x6472U, 0x6473U, 0x6474U, 0x6475U, 0x6476U, + 0x6477U, 0x6478U, 0x6479U, 0x647AU, 0x6430U, 0x6431U, 0x6432U, 0x6433U, + 0x6434U, 0x6435U, 0x6436U, 0x6437U, 0x6438U, 0x6439U, 0x642BU, 0x642FU, + 0x6541U, 0x6542U, 0x6543U, 0x6544U, 0x6545U, 0x6546U, 0x6547U, 0x6548U, + 0x6549U, 0x654AU, 0x654BU, 0x654CU, 0x654DU, 0x654EU, 0x654FU, 0x6550U, + 0x6551U, 0x6552U, 0x6553U, 0x6554U, 0x6555U, 0x6556U, 0x6557U, 0x6558U, + 0x6559U, 0x655AU, 0x6561U, 0x6562U, 0x6563U, 0x6564U, 0x6565U, 0x6566U, + 0x6567U, 0x6568U, 0x6569U, 0x656AU, 0x656BU, 0x656CU, 0x656DU, 0x656EU, + 0x656FU, 0x6570U, 0x6571U, 0x6572U, 0x6573U, 0x6574U, 0x6575U, 0x6576U, + 0x6577U, 0x6578U, 0x6579U, 0x657AU, 0x6530U, 0x6531U, 0x6532U, 0x6533U, + 0x6534U, 0x6535U, 0x6536U, 0x6537U, 0x6538U, 0x6539U, 0x652BU, 0x652FU, + 0x6641U, 0x6642U, 0x6643U, 0x6644U, 0x6645U, 0x6646U, 0x6647U, 0x6648U, + 0x6649U, 0x664AU, 0x664BU, 0x664CU, 0x664DU, 0x664EU, 0x664FU, 0x6650U, + 0x6651U, 0x6652U, 0x6653U, 0x6654U, 0x6655U, 0x6656U, 0x6657U, 0x6658U, + 0x6659U, 0x665AU, 0x6661U, 0x6662U, 0x6663U, 0x6664U, 0x6665U, 0x6666U, + 0x6667U, 0x6668U, 0x6669U, 0x666AU, 0x666BU, 0x666CU, 0x666DU, 0x666EU, + 0x666FU, 0x6670U, 0x6671U, 0x6672U, 0x6673U, 0x6674U, 0x6675U, 0x6676U, + 0x6677U, 0x6678U, 0x6679U, 0x667AU, 0x6630U, 0x6631U, 0x6632U, 0x6633U, + 0x6634U, 0x6635U, 0x6636U, 0x6637U, 0x6638U, 0x6639U, 0x662BU, 0x662FU, + 0x6741U, 0x6742U, 0x6743U, 0x6744U, 0x6745U, 0x6746U, 0x6747U, 0x6748U, + 0x6749U, 0x674AU, 0x674BU, 0x674CU, 0x674DU, 0x674EU, 0x674FU, 0x6750U, + 0x6751U, 0x6752U, 0x6753U, 0x6754U, 0x6755U, 0x6756U, 0x6757U, 0x6758U, + 0x6759U, 0x675AU, 0x6761U, 0x6762U, 0x6763U, 0x6764U, 0x6765U, 0x6766U, + 0x6767U, 0x6768U, 0x6769U, 0x676AU, 0x676BU, 0x676CU, 0x676DU, 0x676EU, + 0x676FU, 0x6770U, 0x6771U, 0x6772U, 0x6773U, 0x6774U, 0x6775U, 0x6776U, + 0x6777U, 0x6778U, 0x6779U, 0x677AU, 0x6730U, 0x6731U, 0x6732U, 0x6733U, + 0x6734U, 0x6735U, 0x6736U, 0x6737U, 0x6738U, 0x6739U, 0x672BU, 0x672FU, + 0x6841U, 0x6842U, 0x6843U, 0x6844U, 0x6845U, 0x6846U, 0x6847U, 0x6848U, + 0x6849U, 0x684AU, 0x684BU, 0x684CU, 0x684DU, 0x684EU, 0x684FU, 0x6850U, + 0x6851U, 0x6852U, 0x6853U, 0x6854U, 0x6855U, 0x6856U, 0x6857U, 0x6858U, + 0x6859U, 0x685AU, 0x6861U, 0x6862U, 0x6863U, 0x6864U, 0x6865U, 0x6866U, + 0x6867U, 0x6868U, 0x6869U, 0x686AU, 0x686BU, 0x686CU, 0x686DU, 0x686EU, + 0x686FU, 0x6870U, 0x6871U, 0x6872U, 0x6873U, 0x6874U, 0x6875U, 0x6876U, + 0x6877U, 0x6878U, 0x6879U, 0x687AU, 0x6830U, 0x6831U, 0x6832U, 0x6833U, + 0x6834U, 0x6835U, 0x6836U, 0x6837U, 0x6838U, 0x6839U, 0x682BU, 0x682FU, + 0x6941U, 0x6942U, 0x6943U, 0x6944U, 0x6945U, 0x6946U, 0x6947U, 0x6948U, + 0x6949U, 0x694AU, 0x694BU, 0x694CU, 0x694DU, 0x694EU, 0x694FU, 0x6950U, + 0x6951U, 0x6952U, 0x6953U, 0x6954U, 0x6955U, 0x6956U, 0x6957U, 0x6958U, + 0x6959U, 0x695AU, 0x6961U, 0x6962U, 0x6963U, 0x6964U, 0x6965U, 0x6966U, + 0x6967U, 0x6968U, 0x6969U, 0x696AU, 0x696BU, 0x696CU, 0x696DU, 0x696EU, + 0x696FU, 0x6970U, 0x6971U, 0x6972U, 0x6973U, 0x6974U, 0x6975U, 0x6976U, + 0x6977U, 0x6978U, 0x6979U, 0x697AU, 0x6930U, 0x6931U, 0x6932U, 0x6933U, + 0x6934U, 0x6935U, 0x6936U, 0x6937U, 0x6938U, 0x6939U, 0x692BU, 0x692FU, + 0x6A41U, 0x6A42U, 0x6A43U, 0x6A44U, 0x6A45U, 0x6A46U, 0x6A47U, 0x6A48U, + 0x6A49U, 0x6A4AU, 0x6A4BU, 0x6A4CU, 0x6A4DU, 0x6A4EU, 0x6A4FU, 0x6A50U, + 0x6A51U, 0x6A52U, 0x6A53U, 0x6A54U, 0x6A55U, 0x6A56U, 0x6A57U, 0x6A58U, + 0x6A59U, 0x6A5AU, 0x6A61U, 0x6A62U, 0x6A63U, 0x6A64U, 0x6A65U, 0x6A66U, + 0x6A67U, 0x6A68U, 0x6A69U, 0x6A6AU, 0x6A6BU, 0x6A6CU, 0x6A6DU, 0x6A6EU, + 0x6A6FU, 0x6A70U, 0x6A71U, 0x6A72U, 0x6A73U, 0x6A74U, 0x6A75U, 0x6A76U, + 0x6A77U, 0x6A78U, 0x6A79U, 0x6A7AU, 0x6A30U, 0x6A31U, 0x6A32U, 0x6A33U, + 0x6A34U, 0x6A35U, 0x6A36U, 0x6A37U, 0x6A38U, 0x6A39U, 0x6A2BU, 0x6A2FU, + 0x6B41U, 0x6B42U, 0x6B43U, 0x6B44U, 0x6B45U, 0x6B46U, 0x6B47U, 0x6B48U, + 0x6B49U, 0x6B4AU, 0x6B4BU, 0x6B4CU, 0x6B4DU, 0x6B4EU, 0x6B4FU, 0x6B50U, + 0x6B51U, 0x6B52U, 0x6B53U, 0x6B54U, 0x6B55U, 0x6B56U, 0x6B57U, 0x6B58U, + 0x6B59U, 0x6B5AU, 0x6B61U, 0x6B62U, 0x6B63U, 0x6B64U, 0x6B65U, 0x6B66U, + 0x6B67U, 0x6B68U, 0x6B69U, 0x6B6AU, 0x6B6BU, 0x6B6CU, 0x6B6DU, 0x6B6EU, + 0x6B6FU, 0x6B70U, 0x6B71U, 0x6B72U, 0x6B73U, 0x6B74U, 0x6B75U, 0x6B76U, + 0x6B77U, 0x6B78U, 0x6B79U, 0x6B7AU, 0x6B30U, 0x6B31U, 0x6B32U, 0x6B33U, + 0x6B34U, 0x6B35U, 0x6B36U, 0x6B37U, 0x6B38U, 0x6B39U, 0x6B2BU, 0x6B2FU, + 0x6C41U, 0x6C42U, 0x6C43U, 0x6C44U, 0x6C45U, 0x6C46U, 0x6C47U, 0x6C48U, + 0x6C49U, 0x6C4AU, 0x6C4BU, 0x6C4CU, 0x6C4DU, 0x6C4EU, 0x6C4FU, 0x6C50U, + 0x6C51U, 0x6C52U, 0x6C53U, 0x6C54U, 0x6C55U, 0x6C56U, 0x6C57U, 0x6C58U, + 0x6C59U, 0x6C5AU, 0x6C61U, 0x6C62U, 0x6C63U, 0x6C64U, 0x6C65U, 0x6C66U, + 0x6C67U, 0x6C68U, 0x6C69U, 0x6C6AU, 0x6C6BU, 0x6C6CU, 0x6C6DU, 0x6C6EU, + 0x6C6FU, 0x6C70U, 0x6C71U, 0x6C72U, 0x6C73U, 0x6C74U, 0x6C75U, 0x6C76U, + 0x6C77U, 0x6C78U, 0x6C79U, 0x6C7AU, 0x6C30U, 0x6C31U, 0x6C32U, 0x6C33U, + 0x6C34U, 0x6C35U, 0x6C36U, 0x6C37U, 0x6C38U, 0x6C39U, 0x6C2BU, 0x6C2FU, + 0x6D41U, 0x6D42U, 0x6D43U, 0x6D44U, 0x6D45U, 0x6D46U, 0x6D47U, 0x6D48U, + 0x6D49U, 0x6D4AU, 0x6D4BU, 0x6D4CU, 0x6D4DU, 0x6D4EU, 0x6D4FU, 0x6D50U, + 0x6D51U, 0x6D52U, 0x6D53U, 0x6D54U, 0x6D55U, 0x6D56U, 0x6D57U, 0x6D58U, + 0x6D59U, 0x6D5AU, 0x6D61U, 0x6D62U, 0x6D63U, 0x6D64U, 0x6D65U, 0x6D66U, + 0x6D67U, 0x6D68U, 0x6D69U, 0x6D6AU, 0x6D6BU, 0x6D6CU, 0x6D6DU, 0x6D6EU, + 0x6D6FU, 0x6D70U, 0x6D71U, 0x6D72U, 0x6D73U, 0x6D74U, 0x6D75U, 0x6D76U, + 0x6D77U, 0x6D78U, 0x6D79U, 0x6D7AU, 0x6D30U, 0x6D31U, 0x6D32U, 0x6D33U, + 0x6D34U, 0x6D35U, 0x6D36U, 0x6D37U, 0x6D38U, 0x6D39U, 0x6D2BU, 0x6D2FU, + 0x6E41U, 0x6E42U, 0x6E43U, 0x6E44U, 0x6E45U, 0x6E46U, 0x6E47U, 0x6E48U, + 0x6E49U, 0x6E4AU, 0x6E4BU, 0x6E4CU, 0x6E4DU, 0x6E4EU, 0x6E4FU, 0x6E50U, + 0x6E51U, 0x6E52U, 0x6E53U, 0x6E54U, 0x6E55U, 0x6E56U, 0x6E57U, 0x6E58U, + 0x6E59U, 0x6E5AU, 0x6E61U, 0x6E62U, 0x6E63U, 0x6E64U, 0x6E65U, 0x6E66U, + 0x6E67U, 0x6E68U, 0x6E69U, 0x6E6AU, 0x6E6BU, 0x6E6CU, 0x6E6DU, 0x6E6EU, + 0x6E6FU, 0x6E70U, 0x6E71U, 0x6E72U, 0x6E73U, 0x6E74U, 0x6E75U, 0x6E76U, + 0x6E77U, 0x6E78U, 0x6E79U, 0x6E7AU, 0x6E30U, 0x6E31U, 0x6E32U, 0x6E33U, + 0x6E34U, 0x6E35U, 0x6E36U, 0x6E37U, 0x6E38U, 0x6E39U, 0x6E2BU, 0x6E2FU, + 0x6F41U, 0x6F42U, 0x6F43U, 0x6F44U, 0x6F45U, 0x6F46U, 0x6F47U, 0x6F48U, + 0x6F49U, 0x6F4AU, 0x6F4BU, 0x6F4CU, 0x6F4DU, 0x6F4EU, 0x6F4FU, 0x6F50U, + 0x6F51U, 0x6F52U, 0x6F53U, 0x6F54U, 0x6F55U, 0x6F56U, 0x6F57U, 0x6F58U, + 0x6F59U, 0x6F5AU, 0x6F61U, 0x6F62U, 0x6F63U, 0x6F64U, 0x6F65U, 0x6F66U, + 0x6F67U, 0x6F68U, 0x6F69U, 0x6F6AU, 0x6F6BU, 0x6F6CU, 0x6F6DU, 0x6F6EU, + 0x6F6FU, 0x6F70U, 0x6F71U, 0x6F72U, 0x6F73U, 0x6F74U, 0x6F75U, 0x6F76U, + 0x6F77U, 0x6F78U, 0x6F79U, 0x6F7AU, 0x6F30U, 0x6F31U, 0x6F32U, 0x6F33U, + 0x6F34U, 0x6F35U, 0x6F36U, 0x6F37U, 0x6F38U, 0x6F39U, 0x6F2BU, 0x6F2FU, + 0x7041U, 0x7042U, 0x7043U, 0x7044U, 0x7045U, 0x7046U, 0x7047U, 0x7048U, + 0x7049U, 0x704AU, 0x704BU, 0x704CU, 0x704DU, 0x704EU, 0x704FU, 0x7050U, + 0x7051U, 0x7052U, 0x7053U, 0x7054U, 0x7055U, 0x7056U, 0x7057U, 0x7058U, + 0x7059U, 0x705AU, 0x7061U, 0x7062U, 0x7063U, 0x7064U, 0x7065U, 0x7066U, + 0x7067U, 0x7068U, 0x7069U, 0x706AU, 0x706BU, 0x706CU, 0x706DU, 0x706EU, + 0x706FU, 0x7070U, 0x7071U, 0x7072U, 0x7073U, 0x7074U, 0x7075U, 0x7076U, + 0x7077U, 0x7078U, 0x7079U, 0x707AU, 0x7030U, 0x7031U, 0x7032U, 0x7033U, + 0x7034U, 0x7035U, 0x7036U, 0x7037U, 0x7038U, 0x7039U, 0x702BU, 0x702FU, + 0x7141U, 0x7142U, 0x7143U, 0x7144U, 0x7145U, 0x7146U, 0x7147U, 0x7148U, + 0x7149U, 0x714AU, 0x714BU, 0x714CU, 0x714DU, 0x714EU, 0x714FU, 0x7150U, + 0x7151U, 0x7152U, 0x7153U, 0x7154U, 0x7155U, 0x7156U, 0x7157U, 0x7158U, + 0x7159U, 0x715AU, 0x7161U, 0x7162U, 0x7163U, 0x7164U, 0x7165U, 0x7166U, + 0x7167U, 0x7168U, 0x7169U, 0x716AU, 0x716BU, 0x716CU, 0x716DU, 0x716EU, + 0x716FU, 0x7170U, 0x7171U, 0x7172U, 0x7173U, 0x7174U, 0x7175U, 0x7176U, + 0x7177U, 0x7178U, 0x7179U, 0x717AU, 0x7130U, 0x7131U, 0x7132U, 0x7133U, + 0x7134U, 0x7135U, 0x7136U, 0x7137U, 0x7138U, 0x7139U, 0x712BU, 0x712FU, + 0x7241U, 0x7242U, 0x7243U, 0x7244U, 0x7245U, 0x7246U, 0x7247U, 0x7248U, + 0x7249U, 0x724AU, 0x724BU, 0x724CU, 0x724DU, 0x724EU, 0x724FU, 0x7250U, + 0x7251U, 0x7252U, 0x7253U, 0x7254U, 0x7255U, 0x7256U, 0x7257U, 0x7258U, + 0x7259U, 0x725AU, 0x7261U, 0x7262U, 0x7263U, 0x7264U, 0x7265U, 0x7266U, + 0x7267U, 0x7268U, 0x7269U, 0x726AU, 0x726BU, 0x726CU, 0x726DU, 0x726EU, + 0x726FU, 0x7270U, 0x7271U, 0x7272U, 0x7273U, 0x7274U, 0x7275U, 0x7276U, + 0x7277U, 0x7278U, 0x7279U, 0x727AU, 0x7230U, 0x7231U, 0x7232U, 0x7233U, + 0x7234U, 0x7235U, 0x7236U, 0x7237U, 0x7238U, 0x7239U, 0x722BU, 0x722FU, + 0x7341U, 0x7342U, 0x7343U, 0x7344U, 0x7345U, 0x7346U, 0x7347U, 0x7348U, + 0x7349U, 0x734AU, 0x734BU, 0x734CU, 0x734DU, 0x734EU, 0x734FU, 0x7350U, + 0x7351U, 0x7352U, 0x7353U, 0x7354U, 0x7355U, 0x7356U, 0x7357U, 0x7358U, + 0x7359U, 0x735AU, 0x7361U, 0x7362U, 0x7363U, 0x7364U, 0x7365U, 0x7366U, + 0x7367U, 0x7368U, 0x7369U, 0x736AU, 0x736BU, 0x736CU, 0x736DU, 0x736EU, + 0x736FU, 0x7370U, 0x7371U, 0x7372U, 0x7373U, 0x7374U, 0x7375U, 0x7376U, + 0x7377U, 0x7378U, 0x7379U, 0x737AU, 0x7330U, 0x7331U, 0x7332U, 0x7333U, + 0x7334U, 0x7335U, 0x7336U, 0x7337U, 0x7338U, 0x7339U, 0x732BU, 0x732FU, + 0x7441U, 0x7442U, 0x7443U, 0x7444U, 0x7445U, 0x7446U, 0x7447U, 0x7448U, + 0x7449U, 0x744AU, 0x744BU, 0x744CU, 0x744DU, 0x744EU, 0x744FU, 0x7450U, + 0x7451U, 0x7452U, 0x7453U, 0x7454U, 0x7455U, 0x7456U, 0x7457U, 0x7458U, + 0x7459U, 0x745AU, 0x7461U, 0x7462U, 0x7463U, 0x7464U, 0x7465U, 0x7466U, + 0x7467U, 0x7468U, 0x7469U, 0x746AU, 0x746BU, 0x746CU, 0x746DU, 0x746EU, + 0x746FU, 0x7470U, 0x7471U, 0x7472U, 0x7473U, 0x7474U, 0x7475U, 0x7476U, + 0x7477U, 0x7478U, 0x7479U, 0x747AU, 0x7430U, 0x7431U, 0x7432U, 0x7433U, + 0x7434U, 0x7435U, 0x7436U, 0x7437U, 0x7438U, 0x7439U, 0x742BU, 0x742FU, + 0x7541U, 0x7542U, 0x7543U, 0x7544U, 0x7545U, 0x7546U, 0x7547U, 0x7548U, + 0x7549U, 0x754AU, 0x754BU, 0x754CU, 0x754DU, 0x754EU, 0x754FU, 0x7550U, + 0x7551U, 0x7552U, 0x7553U, 0x7554U, 0x7555U, 0x7556U, 0x7557U, 0x7558U, + 0x7559U, 0x755AU, 0x7561U, 0x7562U, 0x7563U, 0x7564U, 0x7565U, 0x7566U, + 0x7567U, 0x7568U, 0x7569U, 0x756AU, 0x756BU, 0x756CU, 0x756DU, 0x756EU, + 0x756FU, 0x7570U, 0x7571U, 0x7572U, 0x7573U, 0x7574U, 0x7575U, 0x7576U, + 0x7577U, 0x7578U, 0x7579U, 0x757AU, 0x7530U, 0x7531U, 0x7532U, 0x7533U, + 0x7534U, 0x7535U, 0x7536U, 0x7537U, 0x7538U, 0x7539U, 0x752BU, 0x752FU, + 0x7641U, 0x7642U, 0x7643U, 0x7644U, 0x7645U, 0x7646U, 0x7647U, 0x7648U, + 0x7649U, 0x764AU, 0x764BU, 0x764CU, 0x764DU, 0x764EU, 0x764FU, 0x7650U, + 0x7651U, 0x7652U, 0x7653U, 0x7654U, 0x7655U, 0x7656U, 0x7657U, 0x7658U, + 0x7659U, 0x765AU, 0x7661U, 0x7662U, 0x7663U, 0x7664U, 0x7665U, 0x7666U, + 0x7667U, 0x7668U, 0x7669U, 0x766AU, 0x766BU, 0x766CU, 0x766DU, 0x766EU, + 0x766FU, 0x7670U, 0x7671U, 0x7672U, 0x7673U, 0x7674U, 0x7675U, 0x7676U, + 0x7677U, 0x7678U, 0x7679U, 0x767AU, 0x7630U, 0x7631U, 0x7632U, 0x7633U, + 0x7634U, 0x7635U, 0x7636U, 0x7637U, 0x7638U, 0x7639U, 0x762BU, 0x762FU, + 0x7741U, 0x7742U, 0x7743U, 0x7744U, 0x7745U, 0x7746U, 0x7747U, 0x7748U, + 0x7749U, 0x774AU, 0x774BU, 0x774CU, 0x774DU, 0x774EU, 0x774FU, 0x7750U, + 0x7751U, 0x7752U, 0x7753U, 0x7754U, 0x7755U, 0x7756U, 0x7757U, 0x7758U, + 0x7759U, 0x775AU, 0x7761U, 0x7762U, 0x7763U, 0x7764U, 0x7765U, 0x7766U, + 0x7767U, 0x7768U, 0x7769U, 0x776AU, 0x776BU, 0x776CU, 0x776DU, 0x776EU, + 0x776FU, 0x7770U, 0x7771U, 0x7772U, 0x7773U, 0x7774U, 0x7775U, 0x7776U, + 0x7777U, 0x7778U, 0x7779U, 0x777AU, 0x7730U, 0x7731U, 0x7732U, 0x7733U, + 0x7734U, 0x7735U, 0x7736U, 0x7737U, 0x7738U, 0x7739U, 0x772BU, 0x772FU, + 0x7841U, 0x7842U, 0x7843U, 0x7844U, 0x7845U, 0x7846U, 0x7847U, 0x7848U, + 0x7849U, 0x784AU, 0x784BU, 0x784CU, 0x784DU, 0x784EU, 0x784FU, 0x7850U, + 0x7851U, 0x7852U, 0x7853U, 0x7854U, 0x7855U, 0x7856U, 0x7857U, 0x7858U, + 0x7859U, 0x785AU, 0x7861U, 0x7862U, 0x7863U, 0x7864U, 0x7865U, 0x7866U, + 0x7867U, 0x7868U, 0x7869U, 0x786AU, 0x786BU, 0x786CU, 0x786DU, 0x786EU, + 0x786FU, 0x7870U, 0x7871U, 0x7872U, 0x7873U, 0x7874U, 0x7875U, 0x7876U, + 0x7877U, 0x7878U, 0x7879U, 0x787AU, 0x7830U, 0x7831U, 0x7832U, 0x7833U, + 0x7834U, 0x7835U, 0x7836U, 0x7837U, 0x7838U, 0x7839U, 0x782BU, 0x782FU, + 0x7941U, 0x7942U, 0x7943U, 0x7944U, 0x7945U, 0x7946U, 0x7947U, 0x7948U, + 0x7949U, 0x794AU, 0x794BU, 0x794CU, 0x794DU, 0x794EU, 0x794FU, 0x7950U, + 0x7951U, 0x7952U, 0x7953U, 0x7954U, 0x7955U, 0x7956U, 0x7957U, 0x7958U, + 0x7959U, 0x795AU, 0x7961U, 0x7962U, 0x7963U, 0x7964U, 0x7965U, 0x7966U, + 0x7967U, 0x7968U, 0x7969U, 0x796AU, 0x796BU, 0x796CU, 0x796DU, 0x796EU, + 0x796FU, 0x7970U, 0x7971U, 0x7972U, 0x7973U, 0x7974U, 0x7975U, 0x7976U, + 0x7977U, 0x7978U, 0x7979U, 0x797AU, 0x7930U, 0x7931U, 0x7932U, 0x7933U, + 0x7934U, 0x7935U, 0x7936U, 0x7937U, 0x7938U, 0x7939U, 0x792BU, 0x792FU, + 0x7A41U, 0x7A42U, 0x7A43U, 0x7A44U, 0x7A45U, 0x7A46U, 0x7A47U, 0x7A48U, + 0x7A49U, 0x7A4AU, 0x7A4BU, 0x7A4CU, 0x7A4DU, 0x7A4EU, 0x7A4FU, 0x7A50U, + 0x7A51U, 0x7A52U, 0x7A53U, 0x7A54U, 0x7A55U, 0x7A56U, 0x7A57U, 0x7A58U, + 0x7A59U, 0x7A5AU, 0x7A61U, 0x7A62U, 0x7A63U, 0x7A64U, 0x7A65U, 0x7A66U, + 0x7A67U, 0x7A68U, 0x7A69U, 0x7A6AU, 0x7A6BU, 0x7A6CU, 0x7A6DU, 0x7A6EU, + 0x7A6FU, 0x7A70U, 0x7A71U, 0x7A72U, 0x7A73U, 0x7A74U, 0x7A75U, 0x7A76U, + 0x7A77U, 0x7A78U, 0x7A79U, 0x7A7AU, 0x7A30U, 0x7A31U, 0x7A32U, 0x7A33U, + 0x7A34U, 0x7A35U, 0x7A36U, 0x7A37U, 0x7A38U, 0x7A39U, 0x7A2BU, 0x7A2FU, + 0x3041U, 0x3042U, 0x3043U, 0x3044U, 0x3045U, 0x3046U, 0x3047U, 0x3048U, + 0x3049U, 0x304AU, 0x304BU, 0x304CU, 0x304DU, 0x304EU, 0x304FU, 0x3050U, + 0x3051U, 0x3052U, 0x3053U, 0x3054U, 0x3055U, 0x3056U, 0x3057U, 0x3058U, + 0x3059U, 0x305AU, 0x3061U, 0x3062U, 0x3063U, 0x3064U, 0x3065U, 0x3066U, + 0x3067U, 0x3068U, 0x3069U, 0x306AU, 0x306BU, 0x306CU, 0x306DU, 0x306EU, + 0x306FU, 0x3070U, 0x3071U, 0x3072U, 0x3073U, 0x3074U, 0x3075U, 0x3076U, + 0x3077U, 0x3078U, 0x3079U, 0x307AU, 0x3030U, 0x3031U, 0x3032U, 0x3033U, + 0x3034U, 0x3035U, 0x3036U, 0x3037U, 0x3038U, 0x3039U, 0x302BU, 0x302FU, + 0x3141U, 0x3142U, 0x3143U, 0x3144U, 0x3145U, 0x3146U, 0x3147U, 0x3148U, + 0x3149U, 0x314AU, 0x314BU, 0x314CU, 0x314DU, 0x314EU, 0x314FU, 0x3150U, + 0x3151U, 0x3152U, 0x3153U, 0x3154U, 0x3155U, 0x3156U, 0x3157U, 0x3158U, + 0x3159U, 0x315AU, 0x3161U, 0x3162U, 0x3163U, 0x3164U, 0x3165U, 0x3166U, + 0x3167U, 0x3168U, 0x3169U, 0x316AU, 0x316BU, 0x316CU, 0x316DU, 0x316EU, + 0x316FU, 0x3170U, 0x3171U, 0x3172U, 0x3173U, 0x3174U, 0x3175U, 0x3176U, + 0x3177U, 0x3178U, 0x3179U, 0x317AU, 0x3130U, 0x3131U, 0x3132U, 0x3133U, + 0x3134U, 0x3135U, 0x3136U, 0x3137U, 0x3138U, 0x3139U, 0x312BU, 0x312FU, + 0x3241U, 0x3242U, 0x3243U, 0x3244U, 0x3245U, 0x3246U, 0x3247U, 0x3248U, + 0x3249U, 0x324AU, 0x324BU, 0x324CU, 0x324DU, 0x324EU, 0x324FU, 0x3250U, + 0x3251U, 0x3252U, 0x3253U, 0x3254U, 0x3255U, 0x3256U, 0x3257U, 0x3258U, + 0x3259U, 0x325AU, 0x3261U, 0x3262U, 0x3263U, 0x3264U, 0x3265U, 0x3266U, + 0x3267U, 0x3268U, 0x3269U, 0x326AU, 0x326BU, 0x326CU, 0x326DU, 0x326EU, + 0x326FU, 0x3270U, 0x3271U, 0x3272U, 0x3273U, 0x3274U, 0x3275U, 0x3276U, + 0x3277U, 0x3278U, 0x3279U, 0x327AU, 0x3230U, 0x3231U, 0x3232U, 0x3233U, + 0x3234U, 0x3235U, 0x3236U, 0x3237U, 0x3238U, 0x3239U, 0x322BU, 0x322FU, + 0x3341U, 0x3342U, 0x3343U, 0x3344U, 0x3345U, 0x3346U, 0x3347U, 0x3348U, + 0x3349U, 0x334AU, 0x334BU, 0x334CU, 0x334DU, 0x334EU, 0x334FU, 0x3350U, + 0x3351U, 0x3352U, 0x3353U, 0x3354U, 0x3355U, 0x3356U, 0x3357U, 0x3358U, + 0x3359U, 0x335AU, 0x3361U, 0x3362U, 0x3363U, 0x3364U, 0x3365U, 0x3366U, + 0x3367U, 0x3368U, 0x3369U, 0x336AU, 0x336BU, 0x336CU, 0x336DU, 0x336EU, + 0x336FU, 0x3370U, 0x3371U, 0x3372U, 0x3373U, 0x3374U, 0x3375U, 0x3376U, + 0x3377U, 0x3378U, 0x3379U, 0x337AU, 0x3330U, 0x3331U, 0x3332U, 0x3333U, + 0x3334U, 0x3335U, 0x3336U, 0x3337U, 0x3338U, 0x3339U, 0x332BU, 0x332FU, + 0x3441U, 0x3442U, 0x3443U, 0x3444U, 0x3445U, 0x3446U, 0x3447U, 0x3448U, + 0x3449U, 0x344AU, 0x344BU, 0x344CU, 0x344DU, 0x344EU, 0x344FU, 0x3450U, + 0x3451U, 0x3452U, 0x3453U, 0x3454U, 0x3455U, 0x3456U, 0x3457U, 0x3458U, + 0x3459U, 0x345AU, 0x3461U, 0x3462U, 0x3463U, 0x3464U, 0x3465U, 0x3466U, + 0x3467U, 0x3468U, 0x3469U, 0x346AU, 0x346BU, 0x346CU, 0x346DU, 0x346EU, + 0x346FU, 0x3470U, 0x3471U, 0x3472U, 0x3473U, 0x3474U, 0x3475U, 0x3476U, + 0x3477U, 0x3478U, 0x3479U, 0x347AU, 0x3430U, 0x3431U, 0x3432U, 0x3433U, + 0x3434U, 0x3435U, 0x3436U, 0x3437U, 0x3438U, 0x3439U, 0x342BU, 0x342FU, + 0x3541U, 0x3542U, 0x3543U, 0x3544U, 0x3545U, 0x3546U, 0x3547U, 0x3548U, + 0x3549U, 0x354AU, 0x354BU, 0x354CU, 0x354DU, 0x354EU, 0x354FU, 0x3550U, + 0x3551U, 0x3552U, 0x3553U, 0x3554U, 0x3555U, 0x3556U, 0x3557U, 0x3558U, + 0x3559U, 0x355AU, 0x3561U, 0x3562U, 0x3563U, 0x3564U, 0x3565U, 0x3566U, + 0x3567U, 0x3568U, 0x3569U, 0x356AU, 0x356BU, 0x356CU, 0x356DU, 0x356EU, + 0x356FU, 0x3570U, 0x3571U, 0x3572U, 0x3573U, 0x3574U, 0x3575U, 0x3576U, + 0x3577U, 0x3578U, 0x3579U, 0x357AU, 0x3530U, 0x3531U, 0x3532U, 0x3533U, + 0x3534U, 0x3535U, 0x3536U, 0x3537U, 0x3538U, 0x3539U, 0x352BU, 0x352FU, + 0x3641U, 0x3642U, 0x3643U, 0x3644U, 0x3645U, 0x3646U, 0x3647U, 0x3648U, + 0x3649U, 0x364AU, 0x364BU, 0x364CU, 0x364DU, 0x364EU, 0x364FU, 0x3650U, + 0x3651U, 0x3652U, 0x3653U, 0x3654U, 0x3655U, 0x3656U, 0x3657U, 0x3658U, + 0x3659U, 0x365AU, 0x3661U, 0x3662U, 0x3663U, 0x3664U, 0x3665U, 0x3666U, + 0x3667U, 0x3668U, 0x3669U, 0x366AU, 0x366BU, 0x366CU, 0x366DU, 0x366EU, + 0x366FU, 0x3670U, 0x3671U, 0x3672U, 0x3673U, 0x3674U, 0x3675U, 0x3676U, + 0x3677U, 0x3678U, 0x3679U, 0x367AU, 0x3630U, 0x3631U, 0x3632U, 0x3633U, + 0x3634U, 0x3635U, 0x3636U, 0x3637U, 0x3638U, 0x3639U, 0x362BU, 0x362FU, + 0x3741U, 0x3742U, 0x3743U, 0x3744U, 0x3745U, 0x3746U, 0x3747U, 0x3748U, + 0x3749U, 0x374AU, 0x374BU, 0x374CU, 0x374DU, 0x374EU, 0x374FU, 0x3750U, + 0x3751U, 0x3752U, 0x3753U, 0x3754U, 0x3755U, 0x3756U, 0x3757U, 0x3758U, + 0x3759U, 0x375AU, 0x3761U, 0x3762U, 0x3763U, 0x3764U, 0x3765U, 0x3766U, + 0x3767U, 0x3768U, 0x3769U, 0x376AU, 0x376BU, 0x376CU, 0x376DU, 0x376EU, + 0x376FU, 0x3770U, 0x3771U, 0x3772U, 0x3773U, 0x3774U, 0x3775U, 0x3776U, + 0x3777U, 0x3778U, 0x3779U, 0x377AU, 0x3730U, 0x3731U, 0x3732U, 0x3733U, + 0x3734U, 0x3735U, 0x3736U, 0x3737U, 0x3738U, 0x3739U, 0x372BU, 0x372FU, + 0x3841U, 0x3842U, 0x3843U, 0x3844U, 0x3845U, 0x3846U, 0x3847U, 0x3848U, + 0x3849U, 0x384AU, 0x384BU, 0x384CU, 0x384DU, 0x384EU, 0x384FU, 0x3850U, + 0x3851U, 0x3852U, 0x3853U, 0x3854U, 0x3855U, 0x3856U, 0x3857U, 0x3858U, + 0x3859U, 0x385AU, 0x3861U, 0x3862U, 0x3863U, 0x3864U, 0x3865U, 0x3866U, + 0x3867U, 0x3868U, 0x3869U, 0x386AU, 0x386BU, 0x386CU, 0x386DU, 0x386EU, + 0x386FU, 0x3870U, 0x3871U, 0x3872U, 0x3873U, 0x3874U, 0x3875U, 0x3876U, + 0x3877U, 0x3878U, 0x3879U, 0x387AU, 0x3830U, 0x3831U, 0x3832U, 0x3833U, + 0x3834U, 0x3835U, 0x3836U, 0x3837U, 0x3838U, 0x3839U, 0x382BU, 0x382FU, + 0x3941U, 0x3942U, 0x3943U, 0x3944U, 0x3945U, 0x3946U, 0x3947U, 0x3948U, + 0x3949U, 0x394AU, 0x394BU, 0x394CU, 0x394DU, 0x394EU, 0x394FU, 0x3950U, + 0x3951U, 0x3952U, 0x3953U, 0x3954U, 0x3955U, 0x3956U, 0x3957U, 0x3958U, + 0x3959U, 0x395AU, 0x3961U, 0x3962U, 0x3963U, 0x3964U, 0x3965U, 0x3966U, + 0x3967U, 0x3968U, 0x3969U, 0x396AU, 0x396BU, 0x396CU, 0x396DU, 0x396EU, + 0x396FU, 0x3970U, 0x3971U, 0x3972U, 0x3973U, 0x3974U, 0x3975U, 0x3976U, + 0x3977U, 0x3978U, 0x3979U, 0x397AU, 0x3930U, 0x3931U, 0x3932U, 0x3933U, + 0x3934U, 0x3935U, 0x3936U, 0x3937U, 0x3938U, 0x3939U, 0x392BU, 0x392FU, + 0x2B41U, 0x2B42U, 0x2B43U, 0x2B44U, 0x2B45U, 0x2B46U, 0x2B47U, 0x2B48U, + 0x2B49U, 0x2B4AU, 0x2B4BU, 0x2B4CU, 0x2B4DU, 0x2B4EU, 0x2B4FU, 0x2B50U, + 0x2B51U, 0x2B52U, 0x2B53U, 0x2B54U, 0x2B55U, 0x2B56U, 0x2B57U, 0x2B58U, + 0x2B59U, 0x2B5AU, 0x2B61U, 0x2B62U, 0x2B63U, 0x2B64U, 0x2B65U, 0x2B66U, + 0x2B67U, 0x2B68U, 0x2B69U, 0x2B6AU, 0x2B6BU, 0x2B6CU, 0x2B6DU, 0x2B6EU, + 0x2B6FU, 0x2B70U, 0x2B71U, 0x2B72U, 0x2B73U, 0x2B74U, 0x2B75U, 0x2B76U, + 0x2B77U, 0x2B78U, 0x2B79U, 0x2B7AU, 0x2B30U, 0x2B31U, 0x2B32U, 0x2B33U, + 0x2B34U, 0x2B35U, 0x2B36U, 0x2B37U, 0x2B38U, 0x2B39U, 0x2B2BU, 0x2B2FU, + 0x2F41U, 0x2F42U, 0x2F43U, 0x2F44U, 0x2F45U, 0x2F46U, 0x2F47U, 0x2F48U, + 0x2F49U, 0x2F4AU, 0x2F4BU, 0x2F4CU, 0x2F4DU, 0x2F4EU, 0x2F4FU, 0x2F50U, + 0x2F51U, 0x2F52U, 0x2F53U, 0x2F54U, 0x2F55U, 0x2F56U, 0x2F57U, 0x2F58U, + 0x2F59U, 0x2F5AU, 0x2F61U, 0x2F62U, 0x2F63U, 0x2F64U, 0x2F65U, 0x2F66U, + 0x2F67U, 0x2F68U, 0x2F69U, 0x2F6AU, 0x2F6BU, 0x2F6CU, 0x2F6DU, 0x2F6EU, + 0x2F6FU, 0x2F70U, 0x2F71U, 0x2F72U, 0x2F73U, 0x2F74U, 0x2F75U, 0x2F76U, + 0x2F77U, 0x2F78U, 0x2F79U, 0x2F7AU, 0x2F30U, 0x2F31U, 0x2F32U, 0x2F33U, + 0x2F34U, 0x2F35U, 0x2F36U, 0x2F37U, 0x2F38U, 0x2F39U, 0x2F2BU, 0x2F2FU, +#endif +}; diff --git a/mypyc/lib-rt/base64/tables/tables.c b/mypyc/lib-rt/base64/tables/tables.c new file mode 100644 index 0000000000000..45778b6befdd6 --- /dev/null +++ b/mypyc/lib-rt/base64/tables/tables.c @@ -0,0 +1,40 @@ +#include "tables.h" + +const uint8_t +base64_table_enc_6bit[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/"; + +// In the lookup table below, note that the value for '=' (character 61) is +// 254, not 255. This character is used for in-band signaling of the end of +// the datastream, and we will use that later. The characters A-Z, a-z, 0-9 +// and + / are mapped to their "decoded" values. The other bytes all map to +// the value 255, which flags them as "invalid input". + +const uint8_t +base64_table_dec_8bit[] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0..15 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 16..31 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, // 32..47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, // 48..63 + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64..79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // 80..95 + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96..111 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, // 112..127 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 128..143 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +}; + +#if BASE64_WORDSIZE >= 32 +# include "table_dec_32bit.h" +# include "table_enc_12bit.h" +#endif diff --git a/mypyc/lib-rt/base64/tables/tables.h b/mypyc/lib-rt/base64/tables/tables.h new file mode 100644 index 0000000000000..cb74268a4bf12 --- /dev/null +++ b/mypyc/lib-rt/base64/tables/tables.h @@ -0,0 +1,23 @@ +#ifndef BASE64_TABLES_H +#define BASE64_TABLES_H + +#include + +#include "../env.h" + +// These tables are used by all codecs for fallback plain encoding/decoding: +extern const uint8_t base64_table_enc_6bit[]; +extern const uint8_t base64_table_dec_8bit[]; + +// These tables are used for the 32-bit and 64-bit generic decoders: +#if BASE64_WORDSIZE >= 32 +extern const uint32_t base64_table_dec_32bit_d0[]; +extern const uint32_t base64_table_dec_32bit_d1[]; +extern const uint32_t base64_table_dec_32bit_d2[]; +extern const uint32_t base64_table_dec_32bit_d3[]; + +// This table is used by the 32 and 64-bit generic encoders: +extern const uint16_t base64_table_enc_12bit[]; +#endif + +#endif // BASE64_TABLES_H diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 4da62be115718..6ff34b021a9a3 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -99,10 +99,14 @@ 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); + PyObject *name = _PyUnicode_FromId(&PyId_join); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodOneArg(sep, name, iter); } } @@ -141,3 +145,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 b33233521afda..b102aba57307f 100644 --- a/mypyc/lib-rt/dict_ops.c +++ b/mypyc/lib-rt/dict_ops.c @@ -208,7 +208,11 @@ PyObject *CPyDict_KeysView(PyObject *dict) { return _CPyDictView_New(dict, &PyDictKeys_Type); } _Py_IDENTIFIER(keys); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_keys); + PyObject *name = _PyUnicode_FromId(&PyId_keys); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_ValuesView(PyObject *dict) { @@ -216,7 +220,11 @@ PyObject *CPyDict_ValuesView(PyObject *dict) { return _CPyDictView_New(dict, &PyDictValues_Type); } _Py_IDENTIFIER(values); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_values); + PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_ItemsView(PyObject *dict) { @@ -224,7 +232,11 @@ PyObject *CPyDict_ItemsView(PyObject *dict) { return _CPyDictView_New(dict, &PyDictItems_Type); } _Py_IDENTIFIER(items); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_items); + PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_Keys(PyObject *dict) { @@ -234,7 +246,11 @@ PyObject *CPyDict_Keys(PyObject *dict) { // Inline generic fallback logic to also return a list. PyObject *list = PyList_New(0); _Py_IDENTIFIER(keys); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_keys); + PyObject *name = _PyUnicode_FromId(&PyId_keys); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -253,7 +269,11 @@ PyObject *CPyDict_Values(PyObject *dict) { // Inline generic fallback logic to also return a list. PyObject *list = PyList_New(0); _Py_IDENTIFIER(values); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_values); + PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -272,7 +292,11 @@ PyObject *CPyDict_Items(PyObject *dict) { // Inline generic fallback logic to also return a list. PyObject *list = PyList_New(0); _Py_IDENTIFIER(items); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_items); + PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -289,7 +313,11 @@ char CPyDict_Clear(PyObject *dict) { PyDict_Clear(dict); } else { _Py_IDENTIFIER(clear); - PyObject *res = _PyObject_CallMethodIdNoArgs(dict, &PyId_clear); + PyObject *name = _PyUnicode_FromId(&PyId_clear); /* borrowed */ + if (name == NULL) { + return 0; + } + PyObject *res = PyObject_CallMethodNoArgs(dict, name); if (res == NULL) { return 0; } @@ -302,7 +330,11 @@ PyObject *CPyDict_Copy(PyObject *dict) { return PyDict_Copy(dict); } _Py_IDENTIFIER(copy); - return _PyObject_CallMethodIdNoArgs(dict, &PyId_copy); + PyObject *name = _PyUnicode_FromId(&PyId_copy); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(dict, name); } PyObject *CPyDict_GetKeysIter(PyObject *dict) { @@ -321,7 +353,11 @@ PyObject *CPyDict_GetItemsIter(PyObject *dict) { return dict; } _Py_IDENTIFIER(items); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_items); + PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } @@ -337,7 +373,11 @@ PyObject *CPyDict_GetValuesIter(PyObject *dict) { return dict; } _Py_IDENTIFIER(values); - PyObject *view = _PyObject_CallMethodIdNoArgs(dict, &PyId_values); + PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */ + if (name == NULL) { + return NULL; + } + PyObject *view = PyObject_CallMethodNoArgs(dict, name); if (view == NULL) { return NULL; } diff --git a/mypyc/lib-rt/exc_ops.c b/mypyc/lib-rt/exc_ops.c index d8307ecf21f85..85498420d2af4 100644 --- a/mypyc/lib-rt/exc_ops.c +++ b/mypyc/lib-rt/exc_ops.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + // Exception related primitive operations // // These are registered in mypyc.primitives.exc_ops. @@ -24,7 +26,7 @@ void CPy_Reraise(void) { } void CPyErr_SetObjectAndTraceback(PyObject *type, PyObject *value, PyObject *traceback) { - if (!PyType_Check(type) && value == Py_None) { + if (!PyType_Check(type) && Py_IsNone(value)) { // The first argument must be an exception instance value = type; type = (PyObject *)Py_TYPE(value); diff --git a/mypyc/lib-rt/float_ops.c b/mypyc/lib-rt/float_ops.c index d8c6f25955fa7..3190657425592 100644 --- a/mypyc/lib-rt/float_ops.c +++ b/mypyc/lib-rt/float_ops.c @@ -16,6 +16,33 @@ static double CPy_MathRangeError(void) { return CPY_FLOAT_ERROR; } +static double CPy_MathExpectedNonNegativeInputError(double x) { + char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL); + if (buf) { + PyErr_Format(PyExc_ValueError, "expected a nonnegative input, got %s", buf); + PyMem_Free(buf); + } + return CPY_FLOAT_ERROR; +} + +static double CPy_MathExpectedPositiveInputError(double x) { + char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL); + if (buf) { + PyErr_Format(PyExc_ValueError, "expected a positive input, got %s", buf); + PyMem_Free(buf); + } + return CPY_FLOAT_ERROR; +} + +static double CPy_MathExpectedFiniteInput(double x) { + char *buf = PyOS_double_to_string(x, 'r', 0, Py_DTSF_ADD_DOT_0, NULL); + if (buf) { + PyErr_Format(PyExc_ValueError, "expected a finite input, got %s", buf); + PyMem_Free(buf); + } + return CPY_FLOAT_ERROR; +} + double CPyFloat_FromTagged(CPyTagged x) { if (CPyTagged_CheckShort(x)) { return CPyTagged_ShortAsSsize_t(x); @@ -30,7 +57,11 @@ double CPyFloat_FromTagged(CPyTagged x) { double CPyFloat_Sin(double x) { double v = sin(x); if (unlikely(isnan(v)) && !isnan(x)) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedFiniteInput(x); +#else return CPy_DomainError(); +#endif } return v; } @@ -38,21 +69,33 @@ double CPyFloat_Sin(double x) { double CPyFloat_Cos(double x) { double v = cos(x); if (unlikely(isnan(v)) && !isnan(x)) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedFiniteInput(x); +#else return CPy_DomainError(); +#endif } return v; } double CPyFloat_Tan(double x) { if (unlikely(isinf(x))) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedFiniteInput(x); +#else return CPy_DomainError(); +#endif } return tan(x); } double CPyFloat_Sqrt(double x) { if (x < 0.0) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedNonNegativeInputError(x); +#else return CPy_DomainError(); +#endif } return sqrt(x); } @@ -67,7 +110,11 @@ double CPyFloat_Exp(double x) { double CPyFloat_Log(double x) { if (x <= 0.0) { +#if CPY_3_14_FEATURES + return CPy_MathExpectedPositiveInputError(x); +#else return CPy_DomainError(); +#endif } return log(x); } diff --git a/mypyc/lib-rt/generic_ops.c b/mypyc/lib-rt/generic_ops.c index 260cfec5b360d..1e1e184bf290b 100644 --- a/mypyc/lib-rt/generic_ops.c +++ b/mypyc/lib-rt/generic_ops.c @@ -62,3 +62,23 @@ PyObject *CPyObject_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) { Py_DECREF(slice); return result; } + +typedef PyObject *(*SetupFunction)(PyObject *); + +PyObject *CPy_SetupObject(PyObject *type) { + PyTypeObject *tp = (PyTypeObject *)type; + PyMethodDef *def = NULL; + for(; tp; tp = tp->tp_base) { + def = tp->tp_methods; + if (!def || !def->ml_name) { + continue; + } + + if (!strcmp(def->ml_name, "__internal_mypyc_setup")) { + return ((SetupFunction)(void(*)(void))def->ml_meth)(type); + } + } + + PyErr_SetString(PyExc_RuntimeError, "Internal mypyc error: Unable to find object setup function"); + return NULL; +} diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 1bc2f5b02ba8c..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; diff --git a/mypyc/lib-rt/getargsfast.c b/mypyc/lib-rt/getargsfast.c index 62d0dfed0a6d7..e5667e22efe3b 100644 --- a/mypyc/lib-rt/getargsfast.c +++ b/mypyc/lib-rt/getargsfast.c @@ -271,16 +271,9 @@ 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 CPY_3_13_FEATURES - if (_PyUnicode_Equal(kwname, key)) { + if (PyUnicode_Equal(kwname, key)) { return kwstack[i]; } -#else - if (_PyUnicode_EQ(kwname, key)) { - return kwstack[i]; - } -#endif - } return NULL; } diff --git a/mypyc/lib-rt/init.c b/mypyc/lib-rt/init.c index 01b133233489e..9215c2d590194 100644 --- a/mypyc/lib-rt/init.c +++ b/mypyc/lib-rt/init.c @@ -4,10 +4,21 @@ struct ExcDummyStruct _CPy_ExcDummyStruct = { PyObject_HEAD_INIT(NULL) }; PyObject *_CPy_ExcDummy = (PyObject *)&_CPy_ExcDummyStruct; +// System-wide empty tuple constant +PyObject * __mypyc_empty_tuple__ = NULL; + // Because its dynamic linker is more restricted than linux/OS X, // Windows doesn't allow initializing globals with values from // other dynamic libraries. This means we need to initialize // things at load time. void CPy_Init(void) { _CPy_ExcDummyStruct.ob_base.ob_type = &PyBaseObject_Type; + + // Initialize system-wide empty tuple constant + if (__mypyc_empty_tuple__ == NULL) { + __mypyc_empty_tuple__ = PyTuple_New(0); + if (!__mypyc_empty_tuple__) { + CPyError_OutOfMemory(); + } + } } diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c index 9b5d4ef65fb19..333783ae619d4 100644 --- a/mypyc/lib-rt/int_ops.c +++ b/mypyc/lib-rt/int_ops.c @@ -5,6 +5,10 @@ #include #include "CPy.h" +#ifdef _MSC_VER +#include +#endif + #ifndef _WIN32 // On 64-bit Linux and macOS, ssize_t and long are both 64 bits, and // PyLong_FromLong is faster than PyLong_FromSsize_t, so use the faster one @@ -15,6 +19,17 @@ #define CPyLong_FromSsize_t PyLong_FromSsize_t #endif +#if defined(__GNUC__) || defined(__clang__) +# if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8) +# define CPY_CLZ(x) __builtin_clzll((unsigned long long)(x)) +# define CPY_BITS 64 +# else +# define CPY_CLZ(x) __builtin_clz((unsigned int)(x)) +# define CPY_BITS 32 +# endif +#endif + + CPyTagged CPyTagged_FromSsize_t(Py_ssize_t value) { // We use a Python object if the value shifted left by 1 is too // large for Py_ssize_t @@ -124,7 +139,7 @@ CPyTagged CPyTagged_Add_(CPyTagged left, CPyTagged right) { return CPyTagged_StealFromObject(result); } -// Tagged int subraction slow path, where the result may be a long integer +// Tagged int subtraction slow path, where the result may be a long integer CPyTagged CPyTagged_Subtract_(CPyTagged left, CPyTagged right) { PyObject *left_obj = CPyTagged_AsObject(left); PyObject *right_obj = CPyTagged_AsObject(right); @@ -232,13 +247,6 @@ PyObject *CPyBool_Str(bool b) { return PyObject_Str(b ? Py_True : Py_False); } -static void CPyLong_NormalizeUnsigned(PyLongObject *v) { - Py_ssize_t i = CPY_LONG_SIZE_UNSIGNED(v); - while (i > 0 && CPY_LONG_DIGIT(v, i - 1) == 0) - i--; - CPyLong_SetUnsignedSize(v, i); -} - // Bitwise op '&', '|' or '^' using the generic (slow) API static CPyTagged GenericBitwiseOp(CPyTagged a, CPyTagged b, char op) { PyObject *aobj = CPyTagged_AsObject(a); @@ -302,7 +310,6 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op) { digit *adigits = GetIntDigits(a, &asize, abuf); digit *bdigits = GetIntDigits(b, &bsize, bbuf); - PyLongObject *r; if (unlikely(asize < 0 || bsize < 0)) { // Negative operand. This is slower, but bitwise ops on them are pretty rare. return GenericBitwiseOp(a, b, op); @@ -317,31 +324,31 @@ CPyTagged CPyTagged_BitwiseLongOp_(CPyTagged a, CPyTagged b, char op) { asize = bsize; bsize = tmp_size; } - r = _PyLong_New(op == '&' ? asize : bsize); - if (unlikely(r == NULL)) { + void *digits = NULL; + PyLongWriter *writer = PyLongWriter_Create(0, op == '&' ? asize : bsize, &digits); + if (unlikely(writer == NULL)) { CPyError_OutOfMemory(); } Py_ssize_t i; if (op == '&') { for (i = 0; i < asize; i++) { - CPY_LONG_DIGIT(r, i) = adigits[i] & bdigits[i]; + ((digit *)digits)[i] = adigits[i] & bdigits[i]; } } else { if (op == '|') { for (i = 0; i < asize; i++) { - CPY_LONG_DIGIT(r, i) = adigits[i] | bdigits[i]; + ((digit *)digits)[i] = adigits[i] | bdigits[i]; } } else { for (i = 0; i < asize; i++) { - CPY_LONG_DIGIT(r, i) = adigits[i] ^ bdigits[i]; + ((digit *)digits)[i] = adigits[i] ^ bdigits[i]; } } for (; i < bsize; i++) { - CPY_LONG_DIGIT(r, i) = bdigits[i]; + ((digit *)digits)[i] = bdigits[i]; } } - CPyLong_NormalizeUnsigned(r); - return CPyTagged_StealFromObject((PyObject *)r); + return CPyTagged_StealFromObject(PyLongWriter_Finish(writer)); } // Bitwise '~' slow path @@ -589,3 +596,52 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { } return 1.0; } + +// int.bit_length() +CPyTagged CPyTagged_BitLength(CPyTagged self) { + // Handle zero + if (self == 0) { + return 0; + } + + // Fast path for small (tagged) ints + if (CPyTagged_CheckShort(self)) { + Py_ssize_t val = CPyTagged_ShortAsSsize_t(self); + Py_ssize_t absval = val < 0 ? -val : val; + int bits = 0; + if (absval) { +#if defined(_MSC_VER) + #if defined(_WIN64) + unsigned long idx; + if (_BitScanReverse64(&idx, (unsigned __int64)absval)) { + bits = (int)(idx + 1); + } + #else + unsigned long idx; + if (_BitScanReverse(&idx, (unsigned long)absval)) { + bits = (int)(idx + 1); + } + #endif +#elif defined(__GNUC__) || defined(__clang__) + bits = (int)(CPY_BITS - CPY_CLZ(absval)); +#else + // Fallback to loop if no builtin + while (absval) { + absval >>= 1; + bits++; + } +#endif + } + return bits << 1; + } + + // Slow path for big ints + PyObject *pyint = CPyTagged_AsObject(self); + int bits = _PyLong_NumBits(pyint); + Py_DECREF(pyint); + if (bits < 0) { + // _PyLong_NumBits sets an error on failure + return CPY_INT_TAG; + } + return bits << 1; +} diff --git a/mypyc/lib-rt/librt_base64.c b/mypyc/lib-rt/librt_base64.c new file mode 100644 index 0000000000000..1720359ef9a69 --- /dev/null +++ b/mypyc/lib-rt/librt_base64.c @@ -0,0 +1,311 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include "librt_base64.h" +#include "libbase64.h" +#include "pythoncapi_compat.h" + +#ifdef MYPYC_EXPERIMENTAL + +static PyObject * +b64decode_handle_invalid_input( + PyObject *out_bytes, char *outbuf, size_t max_out, const char *src, size_t srclen); + +#define BASE64_MAXBIN ((PY_SSIZE_T_MAX - 3) / 2) + +#define STACK_BUFFER_SIZE 1024 + +static PyObject * +b64encode_internal(PyObject *obj) { + unsigned char *ascii_data; + char *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + Py_ssize_t bin_len, out_len; + PyBytesWriter *writer; + int newline = 0; // TODO + + if (!PyBytes_Check(obj)) { + PyErr_SetString(PyExc_TypeError, "base64() expects a bytes object"); + return NULL; + } + + bin_data = PyBytes_AS_STRING(obj); + bin_len = PyBytes_GET_SIZE(obj); + assert(bin_len >= 0); + + if (bin_len > BASE64_MAXBIN) { + PyErr_SetString(PyExc_ValueError, "Too much data for base64 line"); + return NULL; + } + + Py_ssize_t buflen = 4 * bin_len / 3 + 4; + char *buf; + char stack_buf[STACK_BUFFER_SIZE]; + if (buflen <= STACK_BUFFER_SIZE) { + buf = stack_buf; + } else { + buf = PyMem_Malloc(buflen); + if (buf == NULL) { + return PyErr_NoMemory(); + } + } + size_t actual_len; + base64_encode(bin_data, bin_len, buf, &actual_len, 0); + PyObject *res = PyBytes_FromStringAndSize(buf, actual_len); + if (buflen > STACK_BUFFER_SIZE) + PyMem_Free(buf); + return res; +} + +static PyObject* +b64encode(PyObject *self, PyObject *const *args, size_t nargs) { + if (nargs != 1) { + PyErr_SetString(PyExc_TypeError, "b64encode() takes exactly one argument"); + return 0; + } + return b64encode_internal(args[0]); +} + +static inline int +is_valid_base64_char(char c, bool allow_padding) { + return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || (c == '+') || (c == '/') || (allow_padding && c == '=')); +} + +static PyObject * +b64decode_internal(PyObject *arg) { + const char *src; + Py_ssize_t srclen_ssz; + + // Get input pointer and length + if (PyBytes_Check(arg)) { + src = PyBytes_AS_STRING(arg); + srclen_ssz = PyBytes_GET_SIZE(arg); + } else if (PyUnicode_Check(arg)) { + if (!PyUnicode_IS_ASCII(arg)) { + PyErr_SetString(PyExc_ValueError, + "string argument should contain only ASCII characters"); + return NULL; + } + src = (const char *)PyUnicode_1BYTE_DATA(arg); + srclen_ssz = PyUnicode_GET_LENGTH(arg); + } else { + PyErr_SetString(PyExc_TypeError, + "argument should be a bytes-like object or ASCII string"); + return NULL; + } + + // Fast-path: empty input + if (srclen_ssz == 0) { + return PyBytes_FromStringAndSize(NULL, 0); + } + + // Quickly ignore invalid characters at the end. Other invalid characters + // are also accepted, but they need a slow path. + while (srclen_ssz > 0 && !is_valid_base64_char(src[srclen_ssz - 1], true)) { + srclen_ssz--; + } + + // Compute an output capacity that's at least 3/4 of input, without overflow: + // ceil(3/4 * N) == N - floor(N/4) + size_t srclen = (size_t)srclen_ssz; + size_t max_out = srclen - (srclen / 4); + if (max_out == 0) { + max_out = 1; // defensive (srclen > 0 implies >= 1 anyway) + } + if (max_out > (size_t)PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "input too large"); + return NULL; + } + + // Allocate output bytes (uninitialized) of the max capacity + PyObject *out_bytes = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)max_out); + if (out_bytes == NULL) { + return NULL; // Propagate memory error + } + + char *outbuf = PyBytes_AS_STRING(out_bytes); + size_t outlen = max_out; + + int ret = base64_decode(src, srclen, outbuf, &outlen, 0); + + if (ret != 1) { + if (ret == 0) { + // Slow path: handle non-base64 input + return b64decode_handle_invalid_input(out_bytes, outbuf, max_out, src, srclen); + } + Py_DECREF(out_bytes); + if (ret == -1) { + PyErr_SetString(PyExc_NotImplementedError, "base64 codec not available in this build"); + } else { + PyErr_SetString(PyExc_RuntimeError, "base64_decode failed"); + } + return NULL; + } + + // Sanity-check contract (decoder must not overflow our buffer) + if (outlen > max_out) { + Py_DECREF(out_bytes); + PyErr_SetString(PyExc_RuntimeError, "decoder wrote past output buffer"); + return NULL; + } + + // Shrink in place to the actual decoded length + if (_PyBytes_Resize(&out_bytes, (Py_ssize_t)outlen) < 0) { + // _PyBytes_Resize sets an exception and may free the old object + return NULL; + } + return out_bytes; +} + +// Process non-base64 input by ignoring non-base64 characters, for compatibility +// with stdlib b64decode. +static PyObject * +b64decode_handle_invalid_input( + PyObject *out_bytes, char *outbuf, size_t max_out, const char *src, size_t srclen) +{ + // Copy input to a temporary buffer, with non-base64 characters and extra suffix + // characters removed + size_t newbuf_len = 0; + char *newbuf = PyMem_Malloc(srclen); + if (newbuf == NULL) { + Py_DECREF(out_bytes); + return PyErr_NoMemory(); + } + + // Copy base64 characters and some padding to the new buffer + for (size_t i = 0; i < srclen; i++) { + char c = src[i]; + if (is_valid_base64_char(c, false)) { + newbuf[newbuf_len++] = c; + } else if (c == '=') { + // Copy a necessary amount of padding + int remainder = newbuf_len % 4; + if (remainder == 0) { + // No padding needed + break; + } + int numpad = 4 - remainder; + // Check that there is at least the required amount padding (CPython ignores + // extra padding) + while (numpad > 0) { + if (i == srclen || src[i] != '=') { + break; + } + newbuf[newbuf_len++] = '='; + i++; + numpad--; + // Skip non-base64 alphabet characters within padding + while (i < srclen && !is_valid_base64_char(src[i], true)) { + i++; + } + } + break; + } + } + + // Stdlib always performs a non-strict padding check + if (newbuf_len % 4 != 0) { + Py_DECREF(out_bytes); + PyMem_Free(newbuf); + PyErr_SetString(PyExc_ValueError, "Incorrect padding"); + return NULL; + } + + size_t outlen = max_out; + int ret = base64_decode(newbuf, newbuf_len, outbuf, &outlen, 0); + PyMem_Free(newbuf); + + if (ret != 1) { + Py_DECREF(out_bytes); + if (ret == 0) { + PyErr_SetString(PyExc_ValueError, "Only base64 data is allowed"); + } + if (ret == -1) { + PyErr_SetString(PyExc_NotImplementedError, "base64 codec not available in this build"); + } else { + PyErr_SetString(PyExc_RuntimeError, "base64_decode failed"); + } + return NULL; + } + + // Shrink in place to the actual decoded length + if (_PyBytes_Resize(&out_bytes, (Py_ssize_t)outlen) < 0) { + // _PyBytes_Resize sets an exception and may free the old object + return NULL; + } + return out_bytes; +} + + +static PyObject* +b64decode(PyObject *self, PyObject *const *args, size_t nargs) { + if (nargs != 1) { + PyErr_SetString(PyExc_TypeError, "b64decode() takes exactly one argument"); + return 0; + } + return b64decode_internal(args[0]); +} + +#endif + +static PyMethodDef librt_base64_module_methods[] = { +#ifdef MYPYC_EXPERIMENTAL + {"b64encode", (PyCFunction)b64encode, METH_FASTCALL, PyDoc_STR("Encode bytes object using Base64.")}, + {"b64decode", (PyCFunction)b64decode, METH_FASTCALL, PyDoc_STR("Decode a Base64 encoded bytes object or ASCII string.")}, +#endif + {NULL, NULL, 0, NULL} +}; + +static int +base64_abi_version(void) { + return 0; +} + +static int +base64_api_version(void) { + return 0; +} + +static int +librt_base64_module_exec(PyObject *m) +{ +#ifdef MYPYC_EXPERIMENTAL + // Export mypy internal C API, be careful with the order! + static void *base64_api[LIBRT_BASE64_API_LEN] = { + (void *)base64_abi_version, + (void *)base64_api_version, + (void *)b64encode_internal, + }; + PyObject *c_api_object = PyCapsule_New((void *)base64_api, "librt.base64._C_API", NULL); + if (PyModule_Add(m, "_C_API", c_api_object) < 0) { + return -1; + } +#endif + return 0; +} + +static PyModuleDef_Slot librt_base64_module_slots[] = { + {Py_mod_exec, librt_base64_module_exec}, +#ifdef Py_MOD_GIL_NOT_USED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif + {0, NULL} +}; + +static PyModuleDef librt_base64_module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "base64", + .m_doc = "Fast base64 encoding and decoding optimized for mypyc", + .m_size = 0, + .m_methods = librt_base64_module_methods, + .m_slots = librt_base64_module_slots, +}; + +PyMODINIT_FUNC +PyInit_base64(void) +{ + return PyModuleDef_Init(&librt_base64_module); +} diff --git a/mypyc/lib-rt/librt_base64.h b/mypyc/lib-rt/librt_base64.h new file mode 100644 index 0000000000000..cc97e54155fdb --- /dev/null +++ b/mypyc/lib-rt/librt_base64.h @@ -0,0 +1,60 @@ +#ifndef LIBRT_BASE64_H +#define LIBRT_BASE64_H + +#ifndef MYPYC_EXPERIMENTAL + +static int +import_librt_base64(void) +{ + // All librt.base64 features are experimental for now, so don't set up the API here + return 0; +} + +#else // MYPYC_EXPERIMENTAL + +#define LIBRT_BASE64_ABI_VERSION 0 +#define LIBRT_BASE64_API_VERSION 0 +#define LIBRT_BASE64_API_LEN 3 + +static void *LibRTBase64_API[LIBRT_BASE64_API_LEN]; + +#define LibRTBase64_ABIVersion (*(int (*)(void)) LibRTBase64_API[0]) +#define LibRTBase64_APIVersion (*(int (*)(void)) LibRTBase64_API[1]) +#define LibRTBase64_b64encode_internal (*(PyObject* (*)(PyObject *source)) LibRTBase64_API[2]) + +static int +import_librt_base64(void) +{ + PyObject *mod = PyImport_ImportModule("librt.base64"); + if (mod == NULL) + return -1; + Py_DECREF(mod); // we import just for the side effect of making the below work. + void *capsule = PyCapsule_Import("librt.base64._C_API", 0); + if (capsule == NULL) + return -1; + memcpy(LibRTBase64_API, capsule, sizeof(LibRTBase64_API)); + if (LibRTBase64_ABIVersion() != LIBRT_BASE64_ABI_VERSION) { + char err[128]; + snprintf(err, sizeof(err), "ABI version conflict for librt.base64, expected %d, found %d", + LIBRT_BASE64_ABI_VERSION, + LibRTBase64_ABIVersion() + ); + PyErr_SetString(PyExc_ValueError, err); + return -1; + } + if (LibRTBase64_APIVersion() < LIBRT_BASE64_API_VERSION) { + char err[128]; + snprintf(err, sizeof(err), + "API version conflict for librt.base64, expected %d or newer, found %d (hint: upgrade librt)", + LIBRT_BASE64_API_VERSION, + LibRTBase64_APIVersion() + ); + PyErr_SetString(PyExc_ValueError, err); + return -1; + } + return 0; +} + +#endif // MYPYC_EXPERIMENTAL + +#endif // LIBRT_BASE64_H diff --git a/mypyc/lib-rt/librt_internal.c b/mypyc/lib-rt/librt_internal.c new file mode 100644 index 0000000000000..fe18c541c11fc --- /dev/null +++ b/mypyc/lib-rt/librt_internal.c @@ -0,0 +1,1037 @@ +#include "pythoncapi_compat.h" + +#define PY_SSIZE_T_CLEAN +#include +#include +#include "CPy.h" +#define LIBRT_INTERNAL_MODULE +#include "librt_internal.h" + +#define START_SIZE 512 + +// See comment in read_int_internal() on motivation for these values. +#define MIN_ONE_BYTE_INT -10 +#define MAX_ONE_BYTE_INT 117 // 2 ** 7 - 1 - 10 +#define MIN_TWO_BYTES_INT -100 +#define MAX_TWO_BYTES_INT 16283 // 2 ** (8 + 6) - 1 - 100 +#define MIN_FOUR_BYTES_INT -10000 +#define MAX_FOUR_BYTES_INT 536860911 // 2 ** (3 * 8 + 5) - 1 - 10000 + +#define TWO_BYTES_INT_BIT 1 +#define FOUR_BYTES_INT_BIT 2 +#define LONG_INT_BIT 4 + +#define FOUR_BYTES_INT_TRAILER 3 +// We add one reserved bit here so that we can potentially support +// 8 bytes format in the future. +#define LONG_INT_TRAILER 15 + +#define CPY_BOOL_ERROR 2 +#define CPY_NONE_ERROR 2 +#define CPY_NONE 1 + +#define _CHECK_READ_BUFFER(data, err) if (unlikely(_check_read_buffer(data) == CPY_NONE_ERROR)) \ + return err; +#define _CHECK_WRITE_BUFFER(data, err) if (unlikely(_check_write_buffer(data) == CPY_NONE_ERROR)) \ + return err; +#define _CHECK_WRITE(data, need) if (unlikely(_check_size((WriteBufferObject *)data, need) == CPY_NONE_ERROR)) \ + return CPY_NONE_ERROR; +#define _CHECK_READ(data, size, err) if (unlikely(_check_read((ReadBufferObject *)data, size) == CPY_NONE_ERROR)) \ + return err; + +#define _READ(result, data, type) \ + do { \ + *(result) = *(type *)(((ReadBufferObject *)data)->ptr); \ + ((ReadBufferObject *)data)->ptr += sizeof(type); \ + } while (0) + +#define _WRITE(data, type, v) \ + do { \ + *(type *)(((WriteBufferObject *)data)->ptr) = v; \ + ((WriteBufferObject *)data)->ptr += sizeof(type); \ + } while (0) + +// +// ReadBuffer +// + +#if PY_BIG_ENDIAN +uint16_t reverse_16(uint16_t number) { + return (number << 8) | (number >> 8); +} + +uint32_t reverse_32(uint32_t number) { + return ((number & 0xFF) << 24) | ((number & 0xFF00) << 8) | ((number & 0xFF0000) >> 8) | (number >> 24); +} +#endif + +typedef struct { + PyObject_HEAD + char *ptr; // Current read location in the buffer + char *end; // End of the buffer + PyObject *source; // The object that contains the buffer +} ReadBufferObject; + +static PyTypeObject ReadBufferType; + +static PyObject* +ReadBuffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + if (type != &ReadBufferType) { + PyErr_SetString(PyExc_TypeError, "ReadBuffer should not be subclassed"); + return NULL; + } + + ReadBufferObject *self = (ReadBufferObject *)type->tp_alloc(type, 0); + if (self != NULL) { + self->source = NULL; + self->ptr = NULL; + self->end = NULL; + } + return (PyObject *) self; +} + +static int +ReadBuffer_init_internal(ReadBufferObject *self, PyObject *source) { + if (!PyBytes_CheckExact(source)) { + PyErr_SetString(PyExc_TypeError, "source must be a bytes object"); + return -1; + } + self->source = Py_NewRef(source); + self->ptr = PyBytes_AS_STRING(source); + self->end = self->ptr + PyBytes_GET_SIZE(source); + return 0; +} + +static PyObject* +ReadBuffer_internal(PyObject *source) { + ReadBufferObject *self = (ReadBufferObject *)ReadBufferType.tp_alloc(&ReadBufferType, 0); + if (self == NULL) + return NULL; + self->ptr = NULL; + self->end = NULL; + self->source = NULL; + if (ReadBuffer_init_internal(self, source) == -1) { + Py_DECREF(self); + return NULL; + } + return (PyObject *)self; +} + +static int +ReadBuffer_init(ReadBufferObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"source", NULL}; + PyObject *source = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &source)) + return -1; + + return ReadBuffer_init_internal(self, source); +} + +static void +ReadBuffer_dealloc(ReadBufferObject *self) +{ + Py_CLEAR(self->source); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyMethodDef ReadBuffer_methods[] = { + {NULL} /* Sentinel */ +}; + +static PyTypeObject ReadBufferType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "ReadBuffer", + .tp_doc = PyDoc_STR("Mypy cache buffer objects"), + .tp_basicsize = sizeof(ReadBufferObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = ReadBuffer_new, + .tp_init = (initproc) ReadBuffer_init, + .tp_dealloc = (destructor) ReadBuffer_dealloc, + .tp_methods = ReadBuffer_methods, +}; + +// +// WriteBuffer +// + +typedef struct { + PyObject_HEAD + char *buf; // Beginning of the buffer + char *ptr; // Current write location in the buffer + char *end; // End of the buffer +} WriteBufferObject; + +static PyTypeObject WriteBufferType; + +static PyObject* +WriteBuffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + if (type != &WriteBufferType) { + PyErr_SetString(PyExc_TypeError, "WriteBuffer cannot be subclassed"); + return NULL; + } + + WriteBufferObject *self = (WriteBufferObject *)type->tp_alloc(type, 0); + if (self != NULL) { + self->buf = NULL; + self->ptr = NULL; + self->end = NULL; + } + return (PyObject *)self; +} + +static int +WriteBuffer_init_internal(WriteBufferObject *self) { + Py_ssize_t size = START_SIZE; + self->buf = PyMem_Malloc(size + 1); + if (self->buf == NULL) { + PyErr_NoMemory(); + return -1; + } + self->ptr = self->buf; + self->end = self->buf + size; + return 0; +} + +static PyObject* +WriteBuffer_internal(void) { + WriteBufferObject *self = (WriteBufferObject *)WriteBufferType.tp_alloc(&WriteBufferType, 0); + if (self == NULL) + return NULL; + self->buf = NULL; + self->ptr = NULL; + self->end = NULL; + if (WriteBuffer_init_internal(self) == -1) { + Py_DECREF(self); + return NULL; + } + return (PyObject *)self; +} + +static int +WriteBuffer_init(WriteBufferObject *self, PyObject *args, PyObject *kwds) +{ + if (!PyArg_ParseTuple(args, "")) { + return -1; + } + + if (kwds != NULL && PyDict_Size(kwds) > 0) { + PyErr_SetString(PyExc_TypeError, + "WriteBuffer() takes no keyword arguments"); + return -1; + } + + return WriteBuffer_init_internal(self); +} + +static void +WriteBuffer_dealloc(WriteBufferObject *self) +{ + PyMem_Free(self->buf); + self->buf = NULL; + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject* +WriteBuffer_getvalue_internal(PyObject *self) +{ + WriteBufferObject *obj = (WriteBufferObject *)self; + return PyBytes_FromStringAndSize(obj->buf, obj->ptr - obj->buf); +} + +static PyObject* +WriteBuffer_getvalue(WriteBufferObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyBytes_FromStringAndSize(self->buf, self->ptr - self->buf); +} + +static PyMethodDef WriteBuffer_methods[] = { + {"getvalue", (PyCFunction) WriteBuffer_getvalue, METH_NOARGS, + "Return the buffer content as bytes object" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject WriteBufferType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "WriteBuffer", + .tp_doc = PyDoc_STR("Mypy cache buffer objects"), + .tp_basicsize = sizeof(WriteBufferObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = WriteBuffer_new, + .tp_init = (initproc) WriteBuffer_init, + .tp_dealloc = (destructor) WriteBuffer_dealloc, + .tp_methods = WriteBuffer_methods, +}; + +// ---------- + +static inline char +_check_read_buffer(PyObject *data) { + if (unlikely(Py_TYPE(data) != &ReadBufferType)) { + PyErr_Format( + PyExc_TypeError, "data must be a ReadBuffer object, got %s", Py_TYPE(data)->tp_name + ); + return CPY_NONE_ERROR; + } + return CPY_NONE; +} + +static inline char +_check_write_buffer(PyObject *data) { + if (unlikely(Py_TYPE(data) != &WriteBufferType)) { + PyErr_Format( + PyExc_TypeError, "data must be a WriteBuffer object, got %s", Py_TYPE(data)->tp_name + ); + return CPY_NONE_ERROR; + } + return CPY_NONE; +} + +static inline char +_check_size(WriteBufferObject *data, Py_ssize_t need) { + if (data->end - data->ptr >= need) + return CPY_NONE; + Py_ssize_t index = data->ptr - data->buf; + Py_ssize_t target = index + need; + Py_ssize_t size = data->end - data->buf; + do { + size *= 2; + } while (target >= size); + data->buf = PyMem_Realloc(data->buf, size); + if (unlikely(data->buf == NULL)) { + PyErr_NoMemory(); + return CPY_NONE_ERROR; + } + data->ptr = data->buf + index; + data->end = data->buf + size; + return CPY_NONE; +} + +static inline char +_check_read(ReadBufferObject *data, Py_ssize_t need) { + if (unlikely((data->end - data->ptr) < need)) { + PyErr_SetString(PyExc_ValueError, "reading past the buffer end"); + return CPY_NONE_ERROR; + } + return CPY_NONE; +} + +/* +bool format: single byte + \x00 - False + \x01 - True +*/ + +static char +read_bool_internal(PyObject *data) { + _CHECK_READ(data, 1, CPY_BOOL_ERROR) + char res; + _READ(&res, data, char); + if (unlikely((res != 0) & (res != 1))) { + PyErr_SetString(PyExc_ValueError, "invalid bool value"); + return CPY_BOOL_ERROR; + } + return res; +} + +static PyObject* +read_bool(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", 0}; + static CPyArg_Parser parser = {"O:read_bool", kwlist, 0}; + PyObject *data; + if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) { + return NULL; + } + _CHECK_READ_BUFFER(data, NULL) + char res = read_bool_internal(data); + if (unlikely(res == CPY_BOOL_ERROR)) + return NULL; + PyObject *retval = res ? Py_True : Py_False; + Py_INCREF(retval); + return retval; +} + +static char +write_bool_internal(PyObject *data, char value) { + _CHECK_WRITE(data, 1) + _WRITE(data, char, value); + return CPY_NONE; +} + +static PyObject* +write_bool(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", "value", 0}; + static CPyArg_Parser parser = {"OO:write_bool", kwlist, 0}; + PyObject *data; + PyObject *value; + if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) { + return NULL; + } + _CHECK_WRITE_BUFFER(data, NULL) + if (unlikely(!PyBool_Check(value))) { + PyErr_SetString(PyExc_TypeError, "value must be a bool"); + return NULL; + } + if (unlikely(write_bool_internal(data, Py_IsTrue(value)) == CPY_NONE_ERROR)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +str format: size as int (see below) followed by UTF-8 bytes +*/ + +static inline CPyTagged +_read_short_int(PyObject *data, uint8_t first) { + uint8_t second; + uint16_t two_more; + if ((first & TWO_BYTES_INT_BIT) == 0) { + // Note we use tagged ints since this function can return an error. + return ((Py_ssize_t)(first >> 1) + MIN_ONE_BYTE_INT) << 1; + } + if ((first & FOUR_BYTES_INT_BIT) == 0) { + _CHECK_READ(data, 1, CPY_INT_TAG) + _READ(&second, data, uint8_t); + return ((((Py_ssize_t)second) << 6) + (Py_ssize_t)(first >> 2) + MIN_TWO_BYTES_INT) << 1; + } + // The caller is responsible to verify this is called only for short ints. + _CHECK_READ(data, 3, CPY_INT_TAG) + // TODO: check if compilers emit optimal code for these two reads, and tweak if needed. + _READ(&second, data, uint8_t); + _READ(&two_more, data, uint16_t); +#if PY_BIG_ENDIAN + two_more = reverse_16(two_more); +#endif + Py_ssize_t higher = (((Py_ssize_t)two_more) << 13) + (((Py_ssize_t)second) << 5); + return (higher + (Py_ssize_t)(first >> 3) + MIN_FOUR_BYTES_INT) << 1; +} + +static PyObject* +read_str_internal(PyObject *data) { + // Read string length. + _CHECK_READ(data, 1, NULL) + uint8_t first; + _READ(&first, data, uint8_t); + if (unlikely(first == LONG_INT_TRAILER)) { + // Fail fast for invalid/tampered data. + PyErr_SetString(PyExc_ValueError, "invalid str size"); + return NULL; + } + CPyTagged tagged_size = _read_short_int(data, first); + if (tagged_size == CPY_INT_TAG) + return NULL; + if ((Py_ssize_t)tagged_size < 0) { + // Fail fast for invalid/tampered data. + PyErr_SetString(PyExc_ValueError, "invalid str size"); + return NULL; + } + Py_ssize_t size = tagged_size >> 1; + // Read string content. + char *ptr = ((ReadBufferObject *)data)->ptr; + _CHECK_READ(data, size, NULL) + PyObject *res = PyUnicode_FromStringAndSize(ptr, (Py_ssize_t)size); + if (unlikely(res == NULL)) + return NULL; + ((ReadBufferObject *)data)->ptr += size; + return res; +} + +static PyObject* +read_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", 0}; + static CPyArg_Parser parser = {"O:read_str", kwlist, 0}; + PyObject *data; + if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) { + return NULL; + } + _CHECK_READ_BUFFER(data, NULL) + return read_str_internal(data); +} + +// The caller *must* check that real_value is within allowed range (29 bits). +static inline char +_write_short_int(PyObject *data, Py_ssize_t real_value) { + if (real_value >= MIN_ONE_BYTE_INT && real_value <= MAX_ONE_BYTE_INT) { + _CHECK_WRITE(data, 1) + _WRITE(data, uint8_t, (uint8_t)(real_value - MIN_ONE_BYTE_INT) << 1); + } else if (real_value >= MIN_TWO_BYTES_INT && real_value <= MAX_TWO_BYTES_INT) { + _CHECK_WRITE(data, 2) +#if PY_BIG_ENDIAN + uint16_t to_write = ((uint16_t)(real_value - MIN_TWO_BYTES_INT) << 2) | TWO_BYTES_INT_BIT; + _WRITE(data, uint16_t, reverse_16(to_write)); +#else + _WRITE(data, uint16_t, ((uint16_t)(real_value - MIN_TWO_BYTES_INT) << 2) | TWO_BYTES_INT_BIT); +#endif + } else { + _CHECK_WRITE(data, 4) +#if PY_BIG_ENDIAN + uint32_t to_write = ((uint32_t)(real_value - MIN_FOUR_BYTES_INT) << 3) | FOUR_BYTES_INT_TRAILER; + _WRITE(data, uint32_t, reverse_32(to_write)); +#else + _WRITE(data, uint32_t, ((uint32_t)(real_value - MIN_FOUR_BYTES_INT) << 3) | FOUR_BYTES_INT_TRAILER); +#endif + } + return CPY_NONE; +} + +static char +write_str_internal(PyObject *data, PyObject *value) { + Py_ssize_t size; + const char *chunk = PyUnicode_AsUTF8AndSize(value, &size); + if (unlikely(chunk == NULL)) + return CPY_NONE_ERROR; + + // Write string length. + if (likely(size >= MIN_FOUR_BYTES_INT && size <= MAX_FOUR_BYTES_INT)) { + if (_write_short_int(data, size) == CPY_NONE_ERROR) + return CPY_NONE_ERROR; + } else { + PyErr_SetString(PyExc_ValueError, "str too long to serialize"); + return CPY_NONE_ERROR; + } + // Write string content. + _CHECK_WRITE(data, size) + char *ptr = ((WriteBufferObject *)data)->ptr; + memcpy(ptr, chunk, size); + ((WriteBufferObject *)data)->ptr += size; + return CPY_NONE; +} + +static PyObject* +write_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", "value", 0}; + static CPyArg_Parser parser = {"OO:write_str", kwlist, 0}; + PyObject *data; + PyObject *value; + if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) { + return NULL; + } + _CHECK_WRITE_BUFFER(data, NULL) + if (unlikely(!PyUnicode_Check(value))) { + PyErr_SetString(PyExc_TypeError, "value must be a str"); + return NULL; + } + if (unlikely(write_str_internal(data, value) == CPY_NONE_ERROR)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +bytes format: size as int (see below) followed by bytes +*/ + +static PyObject* +read_bytes_internal(PyObject *data) { + // Read length. + _CHECK_READ(data, 1, NULL) + uint8_t first; + _READ(&first, data, uint8_t); + if (unlikely(first == LONG_INT_TRAILER)) { + // Fail fast for invalid/tampered data. + PyErr_SetString(PyExc_ValueError, "invalid bytes size"); + return NULL; + } + CPyTagged tagged_size = _read_short_int(data, first); + if (tagged_size == CPY_INT_TAG) + return NULL; + if ((Py_ssize_t)tagged_size < 0) { + // Fail fast for invalid/tampered data. + PyErr_SetString(PyExc_ValueError, "invalid bytes size"); + return NULL; + } + Py_ssize_t size = tagged_size >> 1; + // Read bytes content. + char *ptr = ((ReadBufferObject *)data)->ptr; + _CHECK_READ(data, size, NULL) + PyObject *res = PyBytes_FromStringAndSize(ptr, (Py_ssize_t)size); + if (unlikely(res == NULL)) + return NULL; + ((ReadBufferObject *)data)->ptr += size; + return res; +} + +static PyObject* +read_bytes(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", 0}; + static CPyArg_Parser parser = {"O:read_bytes", kwlist, 0}; + PyObject *data; + if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) { + return NULL; + } + _CHECK_READ_BUFFER(data, NULL) + return read_bytes_internal(data); +} + +static char +write_bytes_internal(PyObject *data, PyObject *value) { + const char *chunk = PyBytes_AsString(value); + if (unlikely(chunk == NULL)) + return CPY_NONE_ERROR; + Py_ssize_t size = PyBytes_GET_SIZE(value); + + // Write length. + if (likely(size >= MIN_FOUR_BYTES_INT && size <= MAX_FOUR_BYTES_INT)) { + if (_write_short_int(data, size) == CPY_NONE_ERROR) + return CPY_NONE_ERROR; + } else { + PyErr_SetString(PyExc_ValueError, "bytes too long to serialize"); + return CPY_NONE_ERROR; + } + // Write bytes content. + _CHECK_WRITE(data, size) + char *ptr = ((WriteBufferObject *)data)->ptr; + memcpy(ptr, chunk, size); + ((WriteBufferObject *)data)->ptr += size; + return CPY_NONE; +} + +static PyObject* +write_bytes(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", "value", 0}; + static CPyArg_Parser parser = {"OO:write_bytes", kwlist, 0}; + PyObject *data; + PyObject *value; + if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) { + return NULL; + } + _CHECK_WRITE_BUFFER(data, NULL) + if (unlikely(!PyBytes_Check(value))) { + PyErr_SetString(PyExc_TypeError, "value must be a bytes object"); + return NULL; + } + if (unlikely(write_bytes_internal(data, value) == CPY_NONE_ERROR)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +float format: + stored using PyFloat helpers in little-endian format. +*/ + +static double +read_float_internal(PyObject *data) { + _CHECK_READ(data, 8, CPY_FLOAT_ERROR) + char *ptr = ((ReadBufferObject *)data)->ptr; + double res = PyFloat_Unpack8(ptr, 1); + if (unlikely((res == -1.0) && PyErr_Occurred())) + return CPY_FLOAT_ERROR; + ((ReadBufferObject *)data)->ptr += 8; + return res; +} + +static PyObject* +read_float(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", 0}; + static CPyArg_Parser parser = {"O:read_float", kwlist, 0}; + PyObject *data; + if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) { + return NULL; + } + _CHECK_READ_BUFFER(data, NULL) + double retval = read_float_internal(data); + if (unlikely(retval == CPY_FLOAT_ERROR && PyErr_Occurred())) { + return NULL; + } + return PyFloat_FromDouble(retval); +} + +static char +write_float_internal(PyObject *data, double value) { + _CHECK_WRITE(data, 8) + char *ptr = ((WriteBufferObject *)data)->ptr; + int res = PyFloat_Pack8(value, ptr, 1); + if (unlikely(res == -1)) + return CPY_NONE_ERROR; + ((WriteBufferObject *)data)->ptr += 8; + return CPY_NONE; +} + +static PyObject* +write_float(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", "value", 0}; + static CPyArg_Parser parser = {"OO:write_float", kwlist, 0}; + PyObject *data; + PyObject *value; + if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) { + return NULL; + } + _CHECK_WRITE_BUFFER(data, NULL) + if (unlikely(!PyFloat_Check(value))) { + PyErr_SetString(PyExc_TypeError, "value must be a float"); + return NULL; + } + if (unlikely(write_float_internal(data, PyFloat_AsDouble(value)) == CPY_NONE_ERROR)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +int format: + one byte: last bit 0, 7 bits used + two bytes: last two bits 01, 14 bits used + four bytes: last three bits 011, 29 bits used + everything else: 00001111 followed by serialized string representation + +Note: for fixed size formats we skew ranges towards more positive values, +since negative integers are much more rare. +*/ + +static CPyTagged +read_int_internal(PyObject *data) { + _CHECK_READ(data, 1, CPY_INT_TAG) + + uint8_t first; + _READ(&first, data, uint8_t); + if (likely(first != LONG_INT_TRAILER)) { + return _read_short_int(data, first); + } + + // Long integer encoding -- byte length and sign, followed by a byte array. + + // Read byte length and sign. + _CHECK_READ(data, 1, CPY_INT_TAG) + _READ(&first, data, uint8_t); + Py_ssize_t size_and_sign = _read_short_int(data, first); + if (size_and_sign == CPY_INT_TAG) + return CPY_INT_TAG; + if ((Py_ssize_t)size_and_sign < 0) { + PyErr_SetString(PyExc_ValueError, "invalid int data"); + return CPY_INT_TAG; + } + bool sign = (size_and_sign >> 1) & 1; + Py_ssize_t size = size_and_sign >> 2; + + // Construct an int object from the byte array. + _CHECK_READ(data, size, CPY_INT_TAG) + char *ptr = ((ReadBufferObject *)data)->ptr; + PyObject *num = _PyLong_FromByteArray((unsigned char *)ptr, size, 1, 0); + if (num == NULL) + return CPY_INT_TAG; + ((ReadBufferObject *)data)->ptr += size; + if (sign) { + PyObject *old = num; + num = PyNumber_Negative(old); + Py_DECREF(old); + if (num == NULL) { + return CPY_INT_TAG; + } + } + return CPyTagged_StealFromObject(num); +} + +static PyObject* +read_int(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", 0}; + static CPyArg_Parser parser = {"O:read_int", kwlist, 0}; + PyObject *data; + if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) { + return NULL; + } + _CHECK_READ_BUFFER(data, NULL) + CPyTagged retval = read_int_internal(data); + if (unlikely(retval == CPY_INT_TAG)) { + return NULL; + } + return CPyTagged_StealAsObject(retval); +} + + +static inline int hex_to_int(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return c - 'A' + 10; // Assume valid hex digit +} + +static inline char +_write_long_int(PyObject *data, CPyTagged value) { + _CHECK_WRITE(data, 1) + _WRITE(data, uint8_t, LONG_INT_TRAILER); + + PyObject *hex_str = NULL; + PyObject* int_value = CPyTagged_AsObject(value); + if (unlikely(int_value == NULL)) + goto error; + + hex_str = PyNumber_ToBase(int_value, 16); + if (hex_str == NULL) + goto error; + Py_DECREF(int_value); + int_value = NULL; + + const char *str = PyUnicode_AsUTF8(hex_str); + if (str == NULL) + goto error; + Py_ssize_t len = strlen(str); + bool neg; + if (str[0] == '-') { + str++; + len--; + neg = true; + } else { + neg = false; + } + // Skip the 0x hex prefix. + str += 2; + len -= 2; + + // Write bytes encoded length and sign. + Py_ssize_t size = (len + 1) / 2; + Py_ssize_t encoded_size = (size << 1) | neg; + if (encoded_size <= MAX_FOUR_BYTES_INT) { + if (_write_short_int(data, encoded_size) == CPY_NONE_ERROR) + goto error; + } else { + PyErr_SetString(PyExc_ValueError, "int too long to serialize"); + goto error; + } + + // Write absolute integer value as byte array in a variable-length little endian format. + int i; + for (i = len; i > 1; i -= 2) { + if (write_tag_internal( + data, hex_to_int(str[i - 1]) | (hex_to_int(str[i - 2]) << 4)) == CPY_NONE_ERROR) + goto error; + } + // The final byte may correspond to only one hex digit. + if (i == 1) { + if (write_tag_internal(data, hex_to_int(str[i - 1])) == CPY_NONE_ERROR) + goto error; + } + + Py_DECREF(hex_str); + return CPY_NONE; + + error: + + Py_XDECREF(int_value); + Py_XDECREF(hex_str); + return CPY_NONE_ERROR; +} + +static char +write_int_internal(PyObject *data, CPyTagged value) { + if (likely((value & CPY_INT_TAG) == 0)) { + Py_ssize_t real_value = CPyTagged_ShortAsSsize_t(value); + if (likely(real_value >= MIN_FOUR_BYTES_INT && real_value <= MAX_FOUR_BYTES_INT)) { + return _write_short_int(data, real_value); + } else { + return _write_long_int(data, value); + } + } else { + return _write_long_int(data, value); + } +} + +static PyObject* +write_int(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", "value", 0}; + static CPyArg_Parser parser = {"OO:write_int", kwlist, 0}; + PyObject *data; + PyObject *value; + if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) { + return NULL; + } + _CHECK_WRITE_BUFFER(data, NULL) + if (unlikely(!PyLong_Check(value))) { + PyErr_SetString(PyExc_TypeError, "value must be an int"); + return NULL; + } + CPyTagged tagged_value = CPyTagged_BorrowFromObject(value); + if (unlikely(write_int_internal(data, tagged_value) == CPY_NONE_ERROR)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +integer tag format (0 <= t <= 255): + stored as a uint8_t +*/ + +static uint8_t +read_tag_internal(PyObject *data) { + _CHECK_READ(data, 1, CPY_LL_UINT_ERROR) + uint8_t ret; + _READ(&ret, data, uint8_t); + return ret; +} + +static PyObject* +read_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", 0}; + static CPyArg_Parser parser = {"O:read_tag", kwlist, 0}; + PyObject *data; + if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) { + return NULL; + } + _CHECK_READ_BUFFER(data, NULL) + uint8_t retval = read_tag_internal(data); + if (unlikely(retval == CPY_LL_UINT_ERROR && PyErr_Occurred())) { + return NULL; + } + return PyLong_FromLong(retval); +} + +static char +write_tag_internal(PyObject *data, uint8_t value) { + _CHECK_WRITE(data, 1) + _WRITE(data, uint8_t, value); + return CPY_NONE; +} + +static PyObject* +write_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) { + static const char * const kwlist[] = {"data", "value", 0}; + static CPyArg_Parser parser = {"OO:write_tag", kwlist, 0}; + PyObject *data; + PyObject *value; + if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) { + return NULL; + } + _CHECK_WRITE_BUFFER(data, NULL) + uint8_t unboxed = CPyLong_AsUInt8(value); + if (unlikely(unboxed == CPY_LL_UINT_ERROR && PyErr_Occurred())) { + CPy_TypeError("u8", value); + return NULL; + } + if (unlikely(write_tag_internal(data, unboxed) == CPY_NONE_ERROR)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static uint8_t +cache_version_internal(void) { + return 0; +} + +static PyObject* +cache_version(PyObject *self, PyObject *Py_UNUSED(ignored)) { + return PyLong_FromLong(cache_version_internal()); +} + +static PyTypeObject * +ReadBuffer_type_internal(void) { + return &ReadBufferType; // Return borrowed reference +} + +static PyTypeObject * +WriteBuffer_type_internal(void) { + return &WriteBufferType; // Return borrowed reference +}; + +static PyMethodDef librt_internal_module_methods[] = { + {"write_bool", (PyCFunction)write_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a bool")}, + {"read_bool", (PyCFunction)read_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a bool")}, + {"write_str", (PyCFunction)write_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a string")}, + {"read_str", (PyCFunction)read_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a string")}, + {"write_bytes", (PyCFunction)write_bytes, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write bytes")}, + {"read_bytes", (PyCFunction)read_bytes, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read bytes")}, + {"write_float", (PyCFunction)write_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a float")}, + {"read_float", (PyCFunction)read_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a float")}, + {"write_int", (PyCFunction)write_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write an int")}, + {"read_int", (PyCFunction)read_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read an int")}, + {"write_tag", (PyCFunction)write_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a short int")}, + {"read_tag", (PyCFunction)read_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a short int")}, + {"cache_version", (PyCFunction)cache_version, METH_NOARGS, PyDoc_STR("cache format version")}, + {NULL, NULL, 0, NULL} +}; + +static int +NativeInternal_ABI_Version(void) { + return LIBRT_INTERNAL_ABI_VERSION; +} + +static int +NativeInternal_API_Version(void) { + return LIBRT_INTERNAL_API_VERSION; +} + +static int +librt_internal_module_exec(PyObject *m) +{ + if (PyType_Ready(&ReadBufferType) < 0) { + return -1; + } + if (PyType_Ready(&WriteBufferType) < 0) { + return -1; + } + if (PyModule_AddObjectRef(m, "ReadBuffer", (PyObject *) &ReadBufferType) < 0) { + return -1; + } + if (PyModule_AddObjectRef(m, "WriteBuffer", (PyObject *) &WriteBufferType) < 0) { + return -1; + } + + // Export mypy internal C API, be careful with the order! + static void *NativeInternal_API[LIBRT_INTERNAL_API_LEN] = { + (void *)ReadBuffer_internal, + (void *)WriteBuffer_internal, + (void *)WriteBuffer_getvalue_internal, + (void *)write_bool_internal, + (void *)read_bool_internal, + (void *)write_str_internal, + (void *)read_str_internal, + (void *)write_float_internal, + (void *)read_float_internal, + (void *)write_int_internal, + (void *)read_int_internal, + (void *)write_tag_internal, + (void *)read_tag_internal, + (void *)NativeInternal_ABI_Version, + (void *)write_bytes_internal, + (void *)read_bytes_internal, + (void *)cache_version_internal, + (void *)ReadBuffer_type_internal, + (void *)WriteBuffer_type_internal, + (void *)NativeInternal_API_Version, + }; + PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "librt.internal._C_API", NULL); + if (PyModule_Add(m, "_C_API", c_api_object) < 0) { + return -1; + } + return 0; +} + +static PyModuleDef_Slot librt_internal_module_slots[] = { + {Py_mod_exec, librt_internal_module_exec}, +#ifdef Py_MOD_GIL_NOT_USED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif + {0, NULL} +}; + +static PyModuleDef librt_internal_module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "internal", + .m_doc = "Mypy cache serialization utils", + .m_size = 0, + .m_methods = librt_internal_module_methods, + .m_slots = librt_internal_module_slots, +}; + +PyMODINIT_FUNC +PyInit_internal(void) +{ + return PyModuleDef_Init(&librt_internal_module); +} diff --git a/mypyc/lib-rt/librt_internal.h b/mypyc/lib-rt/librt_internal.h new file mode 100644 index 0000000000000..501162a279801 --- /dev/null +++ b/mypyc/lib-rt/librt_internal.h @@ -0,0 +1,111 @@ +#ifndef LIBRT_INTERNAL_H +#define LIBRT_INTERNAL_H + +// ABI version -- only an exact match is compatible. This will only be changed in +// very exceptional cases (likely never) due to strict backward compatibility +// requirements. +#define LIBRT_INTERNAL_ABI_VERSION 2 + +// API version -- more recent versions must maintain backward compatibility, i.e. +// we can add new features but not remove or change existing features (unless +// ABI version is changed, but see the comment above). + #define LIBRT_INTERNAL_API_VERSION 0 + +// Number of functions in the capsule API. If you add a new function, also increase +// LIBRT_INTERNAL_API_VERSION. +#define LIBRT_INTERNAL_API_LEN 20 + +#ifdef LIBRT_INTERNAL_MODULE + +static PyObject *ReadBuffer_internal(PyObject *source); +static PyObject *WriteBuffer_internal(void); +static PyObject *WriteBuffer_getvalue_internal(PyObject *self); +static PyObject *ReadBuffer_internal(PyObject *source); +static PyObject *ReadBuffer_internal_empty(void); +static char write_bool_internal(PyObject *data, char value); +static char read_bool_internal(PyObject *data); +static char write_str_internal(PyObject *data, PyObject *value); +static PyObject *read_str_internal(PyObject *data); +static char write_float_internal(PyObject *data, double value); +static double read_float_internal(PyObject *data); +static char write_int_internal(PyObject *data, CPyTagged value); +static CPyTagged read_int_internal(PyObject *data); +static char write_tag_internal(PyObject *data, uint8_t value); +static uint8_t read_tag_internal(PyObject *data); +static int NativeInternal_ABI_Version(void); +static char write_bytes_internal(PyObject *data, PyObject *value); +static PyObject *read_bytes_internal(PyObject *data); +static uint8_t cache_version_internal(void); +static PyTypeObject *ReadBuffer_type_internal(void); +static PyTypeObject *WriteBuffer_type_internal(void); +static int NativeInternal_API_Version(void); + +#else + +static void *NativeInternal_API[LIBRT_INTERNAL_API_LEN]; + +#define ReadBuffer_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[0]) +#define WriteBuffer_internal (*(PyObject* (*)(void)) NativeInternal_API[1]) +#define WriteBuffer_getvalue_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[2]) +#define write_bool_internal (*(char (*)(PyObject *source, char value)) NativeInternal_API[3]) +#define read_bool_internal (*(char (*)(PyObject *source)) NativeInternal_API[4]) +#define write_str_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[5]) +#define read_str_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[6]) +#define write_float_internal (*(char (*)(PyObject *source, double value)) NativeInternal_API[7]) +#define read_float_internal (*(double (*)(PyObject *source)) NativeInternal_API[8]) +#define write_int_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[9]) +#define read_int_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[10]) +#define write_tag_internal (*(char (*)(PyObject *source, uint8_t value)) NativeInternal_API[11]) +#define read_tag_internal (*(uint8_t (*)(PyObject *source)) NativeInternal_API[12]) +#define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[13]) +#define write_bytes_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[14]) +#define read_bytes_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[15]) +#define cache_version_internal (*(uint8_t (*)(void)) NativeInternal_API[16]) +#define ReadBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[17]) +#define WriteBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[18]) +#define NativeInternal_API_Version (*(int (*)(void)) NativeInternal_API[19]) + +static int +import_librt_internal(void) +{ + PyObject *mod = PyImport_ImportModule("librt.internal"); + if (mod == NULL) + return -1; + Py_DECREF(mod); // we import just for the side effect of making the below work. + void *capsule = PyCapsule_Import("librt.internal._C_API", 0); + if (capsule == NULL) + return -1; + memcpy(NativeInternal_API, capsule, sizeof(NativeInternal_API)); + if (NativeInternal_ABI_Version() != LIBRT_INTERNAL_ABI_VERSION) { + char err[128]; + snprintf(err, sizeof(err), "ABI version conflict for librt.internal, expected %d, found %d", + LIBRT_INTERNAL_ABI_VERSION, + NativeInternal_ABI_Version() + ); + PyErr_SetString(PyExc_ValueError, err); + return -1; + } + if (NativeInternal_API_Version() < LIBRT_INTERNAL_API_VERSION) { + char err[128]; + snprintf(err, sizeof(err), + "API version conflict for librt.internal, expected %d or newer, found %d (hint: upgrade librt)", + LIBRT_INTERNAL_API_VERSION, + NativeInternal_API_Version() + ); + PyErr_SetString(PyExc_ValueError, err); + return -1; + } + return 0; +} + +#endif + +static inline bool CPyReadBuffer_Check(PyObject *obj) { + return Py_TYPE(obj) == ReadBuffer_type_internal(); +} + +static inline bool CPyWriteBuffer_Check(PyObject *obj) { + return Py_TYPE(obj) == WriteBuffer_type_internal(); +} + +#endif // LIBRT_INTERNAL_H diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c index d297ece8f4170..c611907fb6016 100644 --- a/mypyc/lib-rt/list_ops.c +++ b/mypyc/lib-rt/list_ops.c @@ -29,11 +29,34 @@ PyObject *CPyList_Build(Py_ssize_t len, ...) { return res; } -PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - PyObject *result = PyList_GET_ITEM(list, n); - Py_INCREF(result); - return result; +char CPyList_Clear(PyObject *list) { + if (PyList_CheckExact(list)) { + PyList_Clear(list); + } else { + _Py_IDENTIFIER(clear); + PyObject *name = _PyUnicode_FromId(&PyId_clear); + if (name == NULL) { + return 0; + } + PyObject *res = PyObject_CallMethodNoArgs(list, name); + if (res == NULL) { + return 0; + } + } + return 1; +} + +PyObject *CPyList_Copy(PyObject *list) { + if(PyList_CheckExact(list)) { + return PyList_GetSlice(list, 0, PyList_GET_SIZE(list)); + } + _Py_IDENTIFIER(copy); + + PyObject *name = _PyUnicode_FromId(&PyId_copy); + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodNoArgs(list, name); } PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index) { @@ -208,30 +231,55 @@ bool CPyList_SetItemInt64(PyObject *list, int64_t index, PyObject *value) { } // This function should only be used to fill in brand new lists. -bool CPyList_SetItemUnsafe(PyObject *list, CPyTagged index, PyObject *value) { - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - PyList_SET_ITEM(list, n, value); - return true; - } else { - PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); - return false; +void CPyList_SetItemUnsafe(PyObject *list, Py_ssize_t index, PyObject *value) { + PyList_SET_ITEM(list, index, value); +} + +#ifdef Py_GIL_DISABLED +// The original optimized list.pop implementation doesn't work on free-threaded +// builds, so provide an alternative that is a bit slower but works. +// +// Note that this implementation isn't intended to be atomic. +static inline PyObject *list_pop_index(PyObject *list, Py_ssize_t index) { + PyObject *item = PyList_GetItemRef(list, index); + if (item == NULL) { + return NULL; + } + if (PySequence_DelItem(list, index) < 0) { + Py_DECREF(item); + return NULL; } + return item; } +#endif -PyObject *CPyList_PopLast(PyObject *obj) +PyObject *CPyList_PopLast(PyObject *list) { +#ifdef Py_GIL_DISABLED + // The other implementation causes segfaults on a free-threaded Python 3.14b4 build. + Py_ssize_t index = PyList_GET_SIZE(list) - 1; + return list_pop_index(list, index); +#else // I tried a specalized version of pop_impl for just removing the // last element and it wasn't any faster in microbenchmarks than // the generic one so I ditched it. - return list_pop_impl((PyListObject *)obj, -1); + return list_pop_impl((PyListObject *)list, -1); +#endif } PyObject *CPyList_Pop(PyObject *obj, CPyTagged index) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); +#ifdef Py_GIL_DISABLED + // We must use a slower implementation on free-threaded builds. + if (n < 0) { + n += PyList_GET_SIZE(obj); + } + return list_pop_index(obj, n); +#else return list_pop_impl((PyListObject *)obj, n); +#endif } else { PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return NULL; @@ -305,6 +353,18 @@ CPyTagged CPyList_Index(PyObject *list, PyObject *obj) { return index << 1; } +PyObject *CPySequence_Sort(PyObject *seq) { + PyObject *newlist = PySequence_List(seq); + if (newlist == NULL) + return NULL; + int res = PyList_Sort(newlist); + if (res < 0) { + Py_DECREF(newlist); + return NULL; + } + return newlist; +} + PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size) { Py_ssize_t size = CPyTagged_AsSsize_t(t_size); if (size == -1 && PyErr_Occurred()) { @@ -317,6 +377,14 @@ PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq) { return CPySequence_Multiply(seq, t_size); } +PyObject *CPySequence_InPlaceMultiply(PyObject *seq, CPyTagged t_size) { + Py_ssize_t size = CPyTagged_AsSsize_t(t_size); + if (size == -1 && PyErr_Occurred()) { + return NULL; + } + return PySequence_InPlaceRepeat(seq, size); +} + PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) { if (likely(PyList_CheckExact(obj) && CPyTagged_CheckShort(start) && CPyTagged_CheckShort(end))) { diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index 1572c4496e308..8e5bfffba7594 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + // Misc primitive operations + C helpers // // These are registered in mypyc.primitives.misc_ops. @@ -24,11 +26,15 @@ PyObject *CPyIter_Send(PyObject *iter, PyObject *val) { // Do a send, or a next if second arg is None. // (This behavior is to match the PEP 380 spec for yield from.) - _Py_IDENTIFIER(send); if (Py_IsNone(val)) { return CPyIter_Next(iter); } else { - return _PyObject_CallMethodIdOneArg(iter, &PyId_send, val); + _Py_IDENTIFIER(send); + PyObject *name = _PyUnicode_FromId(&PyId_send); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodOneArg(iter, name, val); } } @@ -223,6 +229,17 @@ PyObject *CPyType_FromTemplate(PyObject *template, if (!name) goto error; + if (template_->tp_doc) { + // cpython expects tp_doc to be heap-allocated so convert it here to + // avoid segfaults on deallocation. + Py_ssize_t size = strlen(template_->tp_doc) + 1; + char *doc = (char *)PyMem_Malloc(size); + if (!doc) + goto error; + memcpy(doc, template_->tp_doc, size); + template_->tp_doc = doc; + } + // Allocate the type and then copy the main stuff in. t = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0); if (!t) @@ -296,6 +313,21 @@ PyObject *CPyType_FromTemplate(PyObject *template, Py_XDECREF(dummy_class); + // Unlike the tp_doc slots of most other object, a heap type's tp_doc + // must be heap allocated. + if (template_->tp_doc) { + // Silently truncate the docstring if it contains a null byte + Py_ssize_t size = strlen(template_->tp_doc) + 1; + char *tp_doc = (char *)PyMem_Malloc(size); + if (tp_doc == NULL) { + PyErr_NoMemory(); + goto error; + } + + memcpy(tp_doc, template_->tp_doc, size); + t->ht_type.tp_doc = tp_doc; + } + #if PY_MINOR_VERSION == 11 // This is a hack. Python 3.11 doesn't include good public APIs to work with managed // dicts, which are the default for heap types. So we try to opt-out until Python 3.12. @@ -347,13 +379,15 @@ static int _CPy_UpdateObjFromDict(PyObject *obj, PyObject *dict) * tp: The class we are making a dataclass * dict: The dictionary containing values that dataclasses needs * annotations: The type annotation dictionary + * dataclass_type: A str object with the return value of util.py:dataclass_type() */ int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, - PyObject *dict, PyObject *annotations) { + PyObject *dict, PyObject *annotations, + PyObject *dataclass_type) { PyTypeObject *ttp = (PyTypeObject *)tp; Py_ssize_t pos; - PyObject *res; + PyObject *res = NULL; /* Make a copy of the original class __dict__ */ PyObject *orig_dict = PyDict_Copy(ttp->tp_dict); @@ -365,7 +399,8 @@ CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, pos = 0; PyObject *key; while (PyDict_Next(annotations, &pos, &key, NULL)) { - if (PyObject_DelAttr(tp, key) != 0) { + // Check and delete key. Key may be absent from tp for InitVar variables. + if (PyObject_HasAttr(tp, key) == 1 && PyObject_DelAttr(tp, key) != 0) { goto fail; } } @@ -380,17 +415,37 @@ CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, if (!res) { goto fail; } - Py_DECREF(res); + const char *dataclass_type_ptr = PyUnicode_AsUTF8(dataclass_type); + if (dataclass_type_ptr == NULL) { + goto fail; + } + if (strcmp(dataclass_type_ptr, "attr") == 0 || + strcmp(dataclass_type_ptr, "attr-auto") == 0) { + // These attributes are added or modified by @attr.s(slots=True). + const char * const keys[] = {"__attrs_attrs__", "__attrs_own_setattr__", "__init__", ""}; + for (const char * const *key_iter = keys; **key_iter != '\0'; key_iter++) { + PyObject *value = NULL; + int rv = PyObject_GetOptionalAttrString(res, *key_iter, &value); + if (rv == 1) { + PyObject_SetAttrString(tp, *key_iter, value); + Py_DECREF(value); + } else if (rv == -1) { + goto fail; + } + } + } /* Copy back the original contents of the dict */ if (_CPy_UpdateObjFromDict(tp, orig_dict) != 0) { goto fail; } + Py_DECREF(res); Py_DECREF(orig_dict); return 1; fail: + Py_XDECREF(res); Py_XDECREF(orig_dict); return 0; } @@ -512,6 +567,22 @@ void CPyDebug_Print(const char *msg) { fflush(stdout); } +void CPyDebug_PrintObject(PyObject *obj) { + // Printing can cause errors. We don't want this to affect any existing + // state so we'll save any existing error and restore it at the end. + PyObject *exc_type, *exc_value, *exc_traceback; + PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); + + if (PyObject_Print(obj, stderr, 0) == -1) { + PyErr_Print(); + } else { + fprintf(stderr, "\n"); + } + fflush(stderr); + + PyErr_Restore(exc_type, exc_value, exc_traceback); +} + int CPySequence_CheckUnpackCount(PyObject *sequence, Py_ssize_t expected) { Py_ssize_t actual = Py_SIZE(sequence); if (unlikely(actual != expected)) { @@ -563,7 +634,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; } @@ -681,7 +752,7 @@ CPy_Super(PyObject *builtins, PyObject *self) { static bool import_single(PyObject *mod_id, PyObject **mod_static, PyObject *globals_id, PyObject *globals_name, PyObject *globals) { - if (*mod_static == Py_None) { + if (Py_IsNone(*mod_static)) { CPyModule *mod = PyImport_Import(mod_id); if (mod == NULL) { return false; @@ -897,7 +968,7 @@ PyObject *CPySingledispatch_RegisterFunction(PyObject *singledispatch_func, } -// Adapated from ceval.c GET_AITER +// Adapted from ceval.c GET_AITER PyObject *CPy_GetAIter(PyObject *obj) { unaryfunc getter = NULL; @@ -935,7 +1006,7 @@ PyObject *CPy_GetAIter(PyObject *obj) return iter; } -// Adapated from ceval.c GET_ANEXT +// Adapted from ceval.c GET_ANEXT PyObject *CPy_GetANext(PyObject *aiter) { unaryfunc getter = NULL; @@ -987,7 +1058,49 @@ PyObject *CPy_GetANext(PyObject *aiter) return NULL; } -#ifdef CPY_3_12_FEATURES +#if CPY_3_11_FEATURES + +// Return obj.__name__ (specialized to type objects, which are the most common target). +PyObject *CPy_GetName(PyObject *obj) { + if (PyType_Check(obj)) { + return PyType_GetName((PyTypeObject *)obj); + } + _Py_IDENTIFIER(__name__); + PyObject *name = _PyUnicode_FromId(&PyId___name__); /* borrowed */ + return PyObject_GetAttr(obj, name); +} + +#endif + +#ifdef MYPYC_LOG_TRACE + +// This is only compiled in if trace logging is enabled by user + +static int TraceCounter = 0; +static const int TRACE_EVERY_NTH = 1009; // Should be a prime number +#define TRACE_LOG_FILE_NAME "mypyc_trace.txt" +static FILE *TraceLogFile = NULL; + +// Log a tracing event on every Nth call +void CPyTrace_LogEvent(const char *location, const char *line, const char *op, const char *details) { + if (TraceLogFile == NULL) { + if ((TraceLogFile = fopen(TRACE_LOG_FILE_NAME, "w")) == NULL) { + fprintf(stderr, "error: Could not open trace file %s\n", TRACE_LOG_FILE_NAME); + abort(); + } + } + if (TraceCounter == 0) { + fprintf(TraceLogFile, "%s:%s:%s:%s\n", location, line, op, details); + } + TraceCounter++; + if (TraceCounter == TRACE_EVERY_NTH) { + TraceCounter = 0; + } +} + +#endif + +#if CPY_3_12_FEATURES // Copied from Python 3.12.3, since this struct is internal to CPython. It defines // the structure of typing.TypeAliasType objects. We need it since compute_value is @@ -1017,3 +1130,13 @@ void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_valu } #endif + +#if CPY_3_14_FEATURES + +#include "internal/pycore_object.h" + +void CPy_SetImmortal(PyObject *obj) { + _Py_SetImmortal(obj); +} + +#endif diff --git a/mypyc/lib-rt/module_shim.tmpl b/mypyc/lib-rt/module_shim.tmpl index 6e772efd34ec2..28cce9478d256 100644 --- a/mypyc/lib-rt/module_shim.tmpl +++ b/mypyc/lib-rt/module_shim.tmpl @@ -5,8 +5,11 @@ PyInit_{modname}(void) {{ PyObject *tmp; if (!(tmp = PyImport_ImportModule("{libname}"))) return NULL; + PyObject *capsule = PyObject_GetAttrString(tmp, "init_{full_modname}"); Py_DECREF(tmp); - void *init_func = PyCapsule_Import("{libname}.init_{full_modname}", 0); + if (capsule == NULL) return NULL; + void *init_func = PyCapsule_GetPointer(capsule, "{libname}.init_{full_modname}"); + Py_DECREF(capsule); if (!init_func) {{ return NULL; }} diff --git a/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl b/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl new file mode 100644 index 0000000000000..b9bfe9c91962e --- /dev/null +++ b/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl @@ -0,0 +1,41 @@ +#include + +static int {modname}_exec(PyObject *module) +{{ + PyObject *tmp; + if (!(tmp = PyImport_ImportModule("{libname}"))) return -1; + PyObject *capsule = PyObject_GetAttrString(tmp, "exec_{full_modname}"); + Py_DECREF(tmp); + if (capsule == NULL) return -1; + void *exec_func = PyCapsule_GetPointer(capsule, "{libname}.exec_{full_modname}"); + Py_DECREF(capsule); + if (!exec_func) return -1; + if (((int (*)(PyObject *))exec_func)(module) != 0) return -1; + return 0; +}} + +static PyModuleDef_Slot {modname}_slots[] = {{ + {{Py_mod_exec, {modname}_exec}}, + {{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}}, + {{Py_mod_gil, Py_MOD_GIL_NOT_USED}}, + {{0, NULL}}, +}}; + +static struct PyModuleDef {modname}_module = {{ + PyModuleDef_HEAD_INIT, + .m_name = "{modname}", + .m_doc = NULL, + .m_methods = NULL, + .m_size = 0, + .m_slots = {modname}_slots, +}}; + +PyMODINIT_FUNC +PyInit_{modname}(void) +{{ + return PyModuleDef_Init(&{modname}_module); +}} + +// distutils sometimes spuriously tells cl to export CPyInit___init__, +// so provide that so it chills out +PyMODINIT_FUNC PyInit___init__(void) {{ return PyInit_{modname}(); }} diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 9967f0a13b4f3..4168d3c53ee28 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -23,6 +23,31 @@ #define CPy_NOINLINE #endif +#ifndef Py_GIL_DISABLED + +// Everything is running in the same thread, so no need for thread locals +#define CPyThreadLocal + +#else + +// 1. Use C11 standard thread_local storage, if available +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) +#define CPyThreadLocal _Thread_local + +// 2. Microsoft Visual Studio fallback +#elif defined(_MSC_VER) +#define CPyThreadLocal __declspec(thread) + +// 3. GNU thread local storage for GCC/Clang targets that still need it +#elif defined(__GNUC__) || defined(__clang__) +#define CPyThreadLocal __thread + +#else +#error "Can't define CPyThreadLocal for this compiler/target (consider using a non-free-threaded Python build)" +#endif + +#endif // Py_GIL_DISABLED + // INCREF and DECREF that assert the pointer is not NULL. // asserts are disabled in release builds so there shouldn't be a perf hit. // I'm honestly kind of surprised that this isn't done by default. @@ -31,6 +56,48 @@ // Here just for consistency #define CPy_XDECREF(p) Py_XDECREF(p) +#ifndef Py_GIL_DISABLED + +// The *_NO_IMM operations below perform refcount manipulation for +// non-immortal objects (Python 3.12 and later). +// +// Py_INCREF and other CPython operations check for immortality. This +// can be expensive when we know that an object cannot be immortal. +// +// This optimization cannot be performed in free-threaded mode so we +// fall back to just calling the normal incref/decref operations. + +static inline void CPy_INCREF_NO_IMM(PyObject *op) +{ + op->ob_refcnt++; +} + +static inline void CPy_DECREF_NO_IMM(PyObject *op) +{ + if (--op->ob_refcnt == 0) { + _Py_Dealloc(op); + } +} + +static inline void CPy_XDECREF_NO_IMM(PyObject *op) +{ + if (op != NULL && --op->ob_refcnt == 0) { + _Py_Dealloc(op); + } +} + +#define CPy_INCREF_NO_IMM(op) CPy_INCREF_NO_IMM((PyObject *)(op)) +#define CPy_DECREF_NO_IMM(op) CPy_DECREF_NO_IMM((PyObject *)(op)) +#define CPy_XDECREF_NO_IMM(op) CPy_XDECREF_NO_IMM((PyObject *)(op)) + +#else + +#define CPy_INCREF_NO_IMM(op) CPy_INCREF(op) +#define CPy_DECREF_NO_IMM(op) CPy_DECREF(op) +#define CPy_XDECREF_NO_IMM(op) CPy_XDECREF(op) + +#endif + // Tagged integer -- our representation of Python 'int' objects. // Small enough integers are represented as unboxed integers (shifted // left by 1); larger integers (larger than 63 bits on a 64-bit @@ -72,8 +139,10 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) { return x << 1; } -// Are we targeting Python 3.12 or newer? +// Are we targeting Python 3.X or newer? +#define CPY_3_11_FEATURES (PY_VERSION_HEX >= 0x030b0000) #define CPY_3_12_FEATURES (PY_VERSION_HEX >= 0x030c0000) +#define CPY_3_14_FEATURES (PY_VERSION_HEX >= 0x030e0000) #if CPY_3_12_FEATURES @@ -95,13 +164,6 @@ static inline CPyTagged CPyTagged_ShortFromSsize_t(Py_ssize_t x) { // Number of digits, assuming int is non-negative #define CPY_LONG_SIZE_UNSIGNED(o) CPY_LONG_SIZE(o) -static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { - if (n == 0) - o->long_value.lv_tag = CPY_SIGN_ZERO; - else - o->long_value.lv_tag = n << CPY_NON_SIZE_BITS; -} - #else #define CPY_LONG_DIGIT(o, n) ((o)->ob_digit[n]) @@ -109,13 +171,12 @@ static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { #define CPY_LONG_SIZE_SIGNED(o) ((o)->ob_base.ob_size) #define CPY_LONG_SIZE_UNSIGNED(o) ((o)->ob_base.ob_size) -static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { - o->ob_base.ob_size = n; -} - #endif // Are we targeting Python 3.13 or newer? #define CPY_3_13_FEATURES (PY_VERSION_HEX >= 0x030d0000) +// Are we targeting Python 3.14 or newer? +#define CPY_3_14_FEATURES (PY_VERSION_HEX >= 0x030e0000) + #endif diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index 1b59f93de7ece..b16075fcc9b86 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -7,7 +7,7 @@ // https://github.com/python/pythoncapi_compat // // Latest version: -// https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h +// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h // // SPDX-License-Identifier: 0BSD @@ -19,11 +19,15 @@ extern "C" { #endif #include +#include // offsetof() // Python 3.11.0b4 added PyFrame_Back() to Python.h #if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) # include "frameobject.h" // PyFrameObject, PyFrame_GetBack() #endif +#if PY_VERSION_HEX < 0x030C00A3 +# include // T_SHORT, READONLY +#endif #ifndef _Py_CAST @@ -33,11 +37,13 @@ extern "C" { // Static inline functions should use _Py_NULL rather than using directly NULL // to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, // _Py_NULL is defined as nullptr. -#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ - || (defined(__cplusplus) && __cplusplus >= 201103) -# define _Py_NULL nullptr -#else -# define _Py_NULL NULL +#ifndef _Py_NULL +# if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ + || (defined(__cplusplus) && __cplusplus >= 201103) +# define _Py_NULL nullptr +# else +# define _Py_NULL NULL +# endif #endif // Cast argument to PyObject* type. @@ -45,6 +51,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) @@ -280,7 +293,7 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name) // bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) static inline PyInterpreterState * PyThreadState_GetInterpreter(PyThreadState *tstate) { @@ -573,7 +586,7 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) return 0; } *pobj = Py_NewRef(obj); - return (*pobj != NULL); + return 1; } #endif @@ -911,7 +924,7 @@ static inline int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) { PyObject **dict = _PyObject_GetDictPtr(obj); - if (*dict == NULL) { + if (dict == NULL || *dict == NULL) { return -1; } Py_VISIT(*dict); @@ -922,7 +935,7 @@ static inline void PyObject_ClearManagedDict(PyObject *obj) { PyObject **dict = _PyObject_GetDictPtr(obj); - if (*dict == NULL) { + if (dict == NULL || *dict == NULL) { return; } Py_CLEAR(*dict); @@ -1197,11 +1210,11 @@ static inline int PyTime_PerfCounter(PyTime_t *result) #endif // gh-111389 added hash constants to Python 3.13.0a5. These constants were -// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9. +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8. #if (!defined(PyHASH_BITS) \ && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ - && PYPY_VERSION_NUM >= 0x07090000))) + && PYPY_VERSION_NUM >= 0x07030800))) # define PyHASH_BITS _PyHASH_BITS # define PyHASH_MODULUS _PyHASH_MODULUS # define PyHASH_INF _PyHASH_INF @@ -1338,9 +1351,182 @@ 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_WriteASCII(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + return _PyUnicodeWriter_WriteASCIIString((_PyUnicodeWriter*)writer, + str, size); +} + +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 %s", + Py_TYPE(str)->tp_name); + 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)) { @@ -1353,6 +1539,1054 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign) } #endif +// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2 +#if PY_VERSION_HEX < 0x030E00A2 +static inline int PyLong_IsPositive(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == 1; +} + +static inline int PyLong_IsNegative(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == -1; +} + +static inline int PyLong_IsZero(PyObject *obj) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + return _PyLong_Sign(obj) == 0; +} +#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 + + +// gh-102471 added import and export API for integers to 3.14.0a2. +#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +// Helpers to access PyLongObject internals. +static inline void +_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) +{ +#if PY_VERSION_HEX >= 0x030C0000 + op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3); +#elif PY_VERSION_HEX >= 0x030900A4 + Py_SET_SIZE(op, sign * size); +#else + Py_SIZE(op) = sign * size; +#endif +} + +static inline Py_ssize_t +_PyLong_DigitCount(const PyLongObject *op) +{ +#if PY_VERSION_HEX >= 0x030C0000 + return (Py_ssize_t)(op->long_value.lv_tag >> 3); +#else + return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); +#endif +} + +static inline digit* +_PyLong_GetDigits(const PyLongObject *op) +{ +#if PY_VERSION_HEX >= 0x030C0000 + return (digit*)(op->long_value.ob_digit); +#else + return (digit*)(op->ob_digit); +#endif +} + +typedef struct PyLongLayout { + uint8_t bits_per_digit; + uint8_t digit_size; + int8_t digits_order; + int8_t digit_endianness; +} PyLongLayout; + +typedef struct PyLongExport { + int64_t value; + uint8_t negative; + Py_ssize_t ndigits; + const void *digits; + Py_uintptr_t _reserved; +} PyLongExport; + +typedef struct PyLongWriter PyLongWriter; + +static inline const PyLongLayout* +PyLong_GetNativeLayout(void) +{ + static const PyLongLayout PyLong_LAYOUT = { + PyLong_SHIFT, + sizeof(digit), + -1, // least significant first + PY_LITTLE_ENDIAN ? -1 : 1, + }; + + return &PyLong_LAYOUT; +} + +static inline int +PyLong_Export(PyObject *obj, PyLongExport *export_long) +{ + if (!PyLong_Check(obj)) { + memset(export_long, 0, sizeof(*export_long)); + PyErr_Format(PyExc_TypeError, "expected int, got %s", + Py_TYPE(obj)->tp_name); + return -1; + } + + // Fast-path: try to convert to a int64_t + PyLongObject *self = (PyLongObject*)obj; + int overflow; +#if SIZEOF_LONG == 8 + long value = PyLong_AsLongAndOverflow(obj, &overflow); +#else + // Windows has 32-bit long, so use 64-bit long long instead + long long value = PyLong_AsLongLongAndOverflow(obj, &overflow); +#endif + Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t)); + // the function cannot fail since obj is a PyLongObject + assert(!(value == -1 && PyErr_Occurred())); + + if (!overflow) { + export_long->value = value; + export_long->negative = 0; + export_long->ndigits = 0; + export_long->digits = 0; + export_long->_reserved = 0; + } + else { + export_long->value = 0; + export_long->negative = _PyLong_Sign(obj) < 0; + export_long->ndigits = _PyLong_DigitCount(self); + if (export_long->ndigits == 0) { + export_long->ndigits = 1; + } + export_long->digits = _PyLong_GetDigits(self); + export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj); + } + return 0; +} + +static inline void +PyLong_FreeExport(PyLongExport *export_long) +{ + PyObject *obj = (PyObject*)export_long->_reserved; + + if (obj) { + export_long->_reserved = 0; + Py_DECREF(obj); + } +} + +static inline PyLongWriter* +PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) +{ + if (ndigits <= 0) { + PyErr_SetString(PyExc_ValueError, "ndigits must be positive"); + return NULL; + } + assert(digits != NULL); + + PyLongObject *obj = _PyLong_New(ndigits); + if (obj == NULL) { + return NULL; + } + _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits); + + *digits = _PyLong_GetDigits(obj); + return (PyLongWriter*)obj; +} + +static inline void +PyLongWriter_Discard(PyLongWriter *writer) +{ + PyLongObject *obj = (PyLongObject *)writer; + + assert(Py_REFCNT(obj) == 1); + Py_DECREF(obj); +} + +static inline PyObject* +PyLongWriter_Finish(PyLongWriter *writer) +{ + PyObject *obj = (PyObject *)writer; + PyLongObject *self = (PyLongObject*)obj; + Py_ssize_t j = _PyLong_DigitCount(self); + Py_ssize_t i = j; + int sign = _PyLong_Sign(obj); + + assert(Py_REFCNT(obj) == 1); + + // Normalize and get singleton if possible + while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) { + --i; + } + if (i != j) { + if (i == 0) { + sign = 0; + } + _PyLong_SetSignAndDigitCount(self, sign, i); + } + if (i <= 1) { + long val = sign * (long)(_PyLong_GetDigits(self)[0]); + Py_DECREF(obj); + return PyLong_FromLong(val); + } + + return obj; +} +#endif + + +#if PY_VERSION_HEX < 0x030C00A3 +# define Py_T_SHORT T_SHORT +# define Py_T_INT T_INT +# define Py_T_LONG T_LONG +# define Py_T_FLOAT T_FLOAT +# define Py_T_DOUBLE T_DOUBLE +# define Py_T_STRING T_STRING +# define _Py_T_OBJECT T_OBJECT +# define Py_T_CHAR T_CHAR +# define Py_T_BYTE T_BYTE +# define Py_T_UBYTE T_UBYTE +# define Py_T_USHORT T_USHORT +# define Py_T_UINT T_UINT +# define Py_T_ULONG T_ULONG +# define Py_T_STRING_INPLACE T_STRING_INPLACE +# define Py_T_BOOL T_BOOL +# define Py_T_OBJECT_EX T_OBJECT_EX +# define Py_T_LONGLONG T_LONGLONG +# define Py_T_ULONGLONG T_ULONGLONG +# define Py_T_PYSSIZET T_PYSSIZET + +# if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +# define _Py_T_NONE T_NONE +# endif + +# define Py_READONLY READONLY +# define Py_AUDIT_READ READ_RESTRICTED +# define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED +#endif + + +// gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4 +#if PY_VERSION_HEX < 0x030E00A4 +static inline FILE* Py_fopen(PyObject *path, const char *mode) +{ +#if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION) + PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode); + + return _Py_fopen_obj(path, mode); +#else + FILE *f; + PyObject *bytes; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_FSConverter(path, &bytes)) { + return NULL; + } +#else + if (!PyString_Check(path)) { + PyErr_SetString(PyExc_TypeError, "except str"); + return NULL; + } + bytes = Py_NewRef(path); +#endif + const char *path_bytes = PyBytes_AS_STRING(bytes); + + f = fopen(path_bytes, mode); + Py_DECREF(bytes); + + if (f == NULL) { + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); + return NULL; + } + return f; +#endif +} + +static inline int Py_fclose(FILE *file) +{ + return fclose(file); +} +#endif + + +#if 0x03080000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) +static inline PyObject* +PyConfig_Get(const char *name) +{ + typedef enum { + _PyConfig_MEMBER_INT, + _PyConfig_MEMBER_UINT, + _PyConfig_MEMBER_ULONG, + _PyConfig_MEMBER_BOOL, + _PyConfig_MEMBER_WSTR, + _PyConfig_MEMBER_WSTR_OPT, + _PyConfig_MEMBER_WSTR_LIST, + } PyConfigMemberType; + + typedef struct { + const char *name; + size_t offset; + PyConfigMemberType type; + const char *sys_attr; + } PyConfigSpec; + +#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ + {#MEMBER, offsetof(PyConfig, MEMBER), \ + _PyConfig_MEMBER_##TYPE, sys_attr} + + static const PyConfigSpec config_spec[] = { + PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"), + PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"), + PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"), + PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"), + PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"), + PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL), +#if 0x03090000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"), +#endif + PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"), + PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"), + PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"), +#endif + PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"), + PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), + PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL), +#if 0x030D0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL), +#endif +#ifdef Py_GIL_DISABLED + PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL), +#ifdef MS_WINDOWS + PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"), +#endif + PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL), +#endif + }; + +#undef PYTHONCAPI_COMPAT_SPEC + + const PyConfigSpec *spec; + int found = 0; + for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) { + spec = &config_spec[i]; + if (strcmp(spec->name, name) == 0) { + found = 1; + break; + } + } + if (found) { + if (spec->sys_attr != NULL) { + PyObject *value = PySys_GetObject(spec->sys_attr); + if (value == NULL) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); + return NULL; + } + return Py_NewRef(value); + } + + PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); + + const PyConfig *config = _Py_GetConfig(); + void *member = (char *)config + spec->offset; + switch (spec->type) { + case _PyConfig_MEMBER_INT: + case _PyConfig_MEMBER_UINT: + { + int value = *(int *)member; + return PyLong_FromLong(value); + } + case _PyConfig_MEMBER_BOOL: + { + int value = *(int *)member; + return PyBool_FromLong(value != 0); + } + case _PyConfig_MEMBER_ULONG: + { + unsigned long value = *(unsigned long *)member; + return PyLong_FromUnsignedLong(value); + } + case _PyConfig_MEMBER_WSTR: + case _PyConfig_MEMBER_WSTR_OPT: + { + wchar_t *wstr = *(wchar_t **)member; + if (wstr != NULL) { + return PyUnicode_FromWideChar(wstr, -1); + } + else { + return Py_NewRef(Py_None); + } + } + case _PyConfig_MEMBER_WSTR_LIST: + { + const PyWideStringList *list = (const PyWideStringList *)member; + PyObject *tuple = PyTuple_New(list->length); + if (tuple == NULL) { + return NULL; + } + + for (Py_ssize_t i = 0; i < list->length; i++) { + PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); + if (item == NULL) { + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; + } + default: + Py_UNREACHABLE(); + } + } + + PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name); + return NULL; +} + +static inline int +PyConfig_GetInt(const char *name, int *value) +{ + PyObject *obj = PyConfig_Get(name); + if (obj == NULL) { + return -1; + } + + if (!PyLong_Check(obj)) { + Py_DECREF(obj); + PyErr_Format(PyExc_TypeError, "config option %s is not an int", name); + return -1; + } + + int as_int = PyLong_AsInt(obj); + Py_DECREF(obj); + if (as_int == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_OverflowError, + "config option %s value does not fit into a C int", name); + return -1; + } + + *value = as_int; + return 0; +} +#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) + +// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14.0b1. +// Adapted from _PyObject_IsUniquelyReferenced() implementation. +#if PY_VERSION_HEX < 0x030E00B0 +static inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj) +{ +#if !defined(Py_GIL_DISABLED) + return Py_REFCNT(obj) == 1; +#else + // NOTE: the entire ob_ref_shared field must be zero, including flags, to + // ensure that other threads cannot concurrently create new references to + // this object. + return (_Py_IsOwnedByCurrentThread(obj) && + _Py_atomic_load_uint32_relaxed(&obj->ob_ref_local) == 1 && + _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared) == 0); +#endif +} +#endif + + +#if PY_VERSION_HEX < 0x030F0000 +static inline PyObject* +PySys_GetAttrString(const char *name) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *value = Py_XNewRef(PySys_GetObject(name)); +#else + PyObject *value = Py_XNewRef(PySys_GetObject((char*)name)); +#endif + if (value != NULL) { + return value; + } + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", name); + } + return NULL; +} + +static inline PyObject* +PySys_GetAttr(PyObject *name) +{ +#if PY_VERSION_HEX >= 0x03000000 + const char *name_str = PyUnicode_AsUTF8(name); +#else + const char *name_str = PyString_AsString(name); +#endif + if (name_str == NULL) { + return NULL; + } + + return PySys_GetAttrString(name_str); +} + +static inline int +PySys_GetOptionalAttrString(const char *name, PyObject **value) +{ +#if PY_VERSION_HEX >= 0x03000000 + *value = Py_XNewRef(PySys_GetObject(name)); +#else + *value = Py_XNewRef(PySys_GetObject((char*)name)); +#endif + if (*value != NULL) { + return 1; + } + return 0; +} + +static inline int +PySys_GetOptionalAttr(PyObject *name, PyObject **value) +{ +#if PY_VERSION_HEX >= 0x03000000 + const char *name_str = PyUnicode_AsUTF8(name); +#else + const char *name_str = PyString_AsString(name); +#endif + if (name_str == NULL) { + *value = NULL; + return -1; + } + + return PySys_GetOptionalAttrString(name_str, value); +} +#endif // PY_VERSION_HEX < 0x030F00A1 + + +#if PY_VERSION_HEX < 0x030F00A1 +typedef struct PyBytesWriter { + char small_buffer[256]; + PyObject *obj; + Py_ssize_t size; +} PyBytesWriter; + +static inline Py_ssize_t +_PyBytesWriter_GetAllocated(PyBytesWriter *writer) +{ + if (writer->obj == NULL) { + return sizeof(writer->small_buffer); + } + else { + return PyBytes_GET_SIZE(writer->obj); + } +} + + +static inline int +_PyBytesWriter_Resize_impl(PyBytesWriter *writer, Py_ssize_t size, + int resize) +{ + int overallocate = resize; + assert(size >= 0); + + if (size <= _PyBytesWriter_GetAllocated(writer)) { + return 0; + } + + if (overallocate) { +#ifdef MS_WINDOWS + /* On Windows, overallocate by 50% is the best factor */ + if (size <= (PY_SSIZE_T_MAX - size / 2)) { + size += size / 2; + } +#else + /* On Linux, overallocate by 25% is the best factor */ + if (size <= (PY_SSIZE_T_MAX - size / 4)) { + size += size / 4; + } +#endif + } + + if (writer->obj != NULL) { + if (_PyBytes_Resize(&writer->obj, size)) { + return -1; + } + assert(writer->obj != NULL); + } + else { + writer->obj = PyBytes_FromStringAndSize(NULL, size); + if (writer->obj == NULL) { + return -1; + } + + if (resize) { + assert((size_t)size > sizeof(writer->small_buffer)); + memcpy(PyBytes_AS_STRING(writer->obj), + writer->small_buffer, + sizeof(writer->small_buffer)); + } + } + return 0; +} + +static inline void* +PyBytesWriter_GetData(PyBytesWriter *writer) +{ + if (writer->obj == NULL) { + return writer->small_buffer; + } + else { + return PyBytes_AS_STRING(writer->obj); + } +} + +static inline Py_ssize_t +PyBytesWriter_GetSize(PyBytesWriter *writer) +{ + return writer->size; +} + +static inline void +PyBytesWriter_Discard(PyBytesWriter *writer) +{ + if (writer == NULL) { + return; + } + + Py_XDECREF(writer->obj); + PyMem_Free(writer); +} + +static inline PyBytesWriter* +PyBytesWriter_Create(Py_ssize_t size) +{ + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "size must be >= 0"); + return NULL; + } + + PyBytesWriter *writer = (PyBytesWriter*)PyMem_Malloc(sizeof(PyBytesWriter)); + if (writer == NULL) { + PyErr_NoMemory(); + return NULL; + } + + writer->obj = NULL; + writer->size = 0; + + if (size >= 1) { + if (_PyBytesWriter_Resize_impl(writer, size, 0) < 0) { + PyBytesWriter_Discard(writer); + return NULL; + } + writer->size = size; + } + return writer; +} + +static inline PyObject* +PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size) +{ + PyObject *result; + if (size == 0) { + result = PyBytes_FromStringAndSize("", 0); + } + else if (writer->obj != NULL) { + if (size != PyBytes_GET_SIZE(writer->obj)) { + if (_PyBytes_Resize(&writer->obj, size)) { + goto error; + } + } + result = writer->obj; + writer->obj = NULL; + } + else { + result = PyBytes_FromStringAndSize(writer->small_buffer, size); + } + PyBytesWriter_Discard(writer); + return result; + +error: + PyBytesWriter_Discard(writer); + return NULL; +} + +static inline PyObject* +PyBytesWriter_Finish(PyBytesWriter *writer) +{ + return PyBytesWriter_FinishWithSize(writer, writer->size); +} + +static inline PyObject* +PyBytesWriter_FinishWithPointer(PyBytesWriter *writer, void *buf) +{ + Py_ssize_t size = (char*)buf - (char*)PyBytesWriter_GetData(writer); + if (size < 0 || size > _PyBytesWriter_GetAllocated(writer)) { + PyBytesWriter_Discard(writer); + PyErr_SetString(PyExc_ValueError, "invalid end pointer"); + return NULL; + } + + return PyBytesWriter_FinishWithSize(writer, size); +} + +static inline int +PyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size) +{ + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "size must be >= 0"); + return -1; + } + if (_PyBytesWriter_Resize_impl(writer, size, 1) < 0) { + return -1; + } + writer->size = size; + return 0; +} + +static inline int +PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t size) +{ + if (size < 0 && writer->size + size < 0) { + PyErr_SetString(PyExc_ValueError, "invalid size"); + return -1; + } + if (size > PY_SSIZE_T_MAX - writer->size) { + PyErr_NoMemory(); + return -1; + } + size = writer->size + size; + + if (_PyBytesWriter_Resize_impl(writer, size, 1) < 0) { + return -1; + } + writer->size = size; + return 0; +} + +static inline void* +PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, + Py_ssize_t size, void *buf) +{ + Py_ssize_t pos = (char*)buf - (char*)PyBytesWriter_GetData(writer); + if (PyBytesWriter_Grow(writer, size) < 0) { + return NULL; + } + return (char*)PyBytesWriter_GetData(writer) + pos; +} + +static inline int +PyBytesWriter_WriteBytes(PyBytesWriter *writer, + const void *bytes, Py_ssize_t size) +{ + if (size < 0) { + size_t len = strlen((const char*)bytes); + if (len > (size_t)PY_SSIZE_T_MAX) { + PyErr_NoMemory(); + return -1; + } + size = (Py_ssize_t)len; + } + + Py_ssize_t pos = writer->size; + if (PyBytesWriter_Grow(writer, size) < 0) { + return -1; + } + char *buf = (char*)PyBytesWriter_GetData(writer); + memcpy(buf + pos, bytes, (size_t)size); + return 0; +} + +static inline int +PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 2, 3))); + +static inline int +PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyBytes_FromFormatV(format, vargs); + va_end(vargs); + + if (str == NULL) { + return -1; + } + int res = PyBytesWriter_WriteBytes(writer, + PyBytes_AS_STRING(str), + PyBytes_GET_SIZE(str)); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030F00A1 + + +#if PY_VERSION_HEX < 0x030F00A1 +static inline PyObject* +PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) +{ + PyObject *tuple = PyTuple_New(size); + if (tuple == NULL) { + return NULL; + } + for (Py_ssize_t i=0; i < size; i++) { + PyObject *item = array[i]; + PyTuple_SET_ITEM(tuple, i, Py_NewRef(item)); + } + return tuple; +} +#endif + + +#if PY_VERSION_HEX < 0x030F00A1 +static inline Py_hash_t +PyUnstable_Unicode_GET_CACHED_HASH(PyObject *op) +{ +#ifdef PYPY_VERSION + (void)op; // unused argument + return -1; +#elif PY_VERSION_HEX >= 0x03000000 + return ((PyASCIIObject*)op)->hash; +#else + return ((PyUnicodeObject*)op)->hash; +#endif +} +#endif + #ifdef __cplusplus } diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index bf7e5203758d5..7019c12cf59a5 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -17,13 +17,9 @@ #ifndef Py_BUILD_CORE #define Py_BUILD_CORE #endif -#include "internal/pycore_bytesobject.h" // _PyBytes_Join -#include "internal/pycore_call.h" // _PyObject_CallMethodIdNoArgs, _PyObject_CallMethodIdObjArgs, _PyObject_CallMethodIdOneArg #include "internal/pycore_genobject.h" // _PyGen_FetchStopIterationValue -#include "internal/pycore_object.h" // _PyType_CalculateMetaclass #include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause, _PyErr_SetKeyError #include "internal/pycore_setobject.h" // _PySet_Update -#include "internal/pycore_unicodeobject.h" // _PyUnicode_EQ, _PyUnicode_FastCopyCharacters #endif #if CPY_3_12_FEATURES @@ -72,7 +68,7 @@ update_bases(PyObject *bases) } continue; } - new_base = _PyObject_Vectorcall(meth, stack, 1, NULL); + new_base = PyObject_Vectorcall(meth, stack, 1, NULL); Py_DECREF(meth); if (!new_base) { goto error; @@ -121,7 +117,7 @@ init_subclass(PyTypeObject *type, PyObject *kwds) PyObject *super, *func, *result; PyObject *args[2] = {(PyObject *)type, (PyObject *)type}; - super = _PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); + super = PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); if (super == NULL) { return -1; } @@ -395,55 +391,15 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { #define _CPyObject_HasAttrId _PyObject_HasAttrId #endif -#if PY_VERSION_HEX < 0x03090000 -// OneArgs and NoArgs functions got added in 3.9 -#define _PyObject_CallMethodIdNoArgs(self, 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_13_FEATURES - -// These are copied from genobject.c in Python 3.13 - -/* Returns a borrowed reference */ -static inline PyCodeObject * -_PyGen_GetCode(PyGenObject *gen) { - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); - return _PyFrame_GetCode(frame); -} - -static int -gen_is_coroutine(PyObject *o) -{ - if (PyGen_CheckExact(o)) { - PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); - if (code->co_flags & CO_ITERABLE_COROUTINE) { - return 1; - } - } - return 0; -} - -#elif CPY_3_12_FEATURES +#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..72dfc15d8588d 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -1,4 +1,4 @@ -"""Build script for mypyc C runtime library unit tests. +"""Build script for mypyc C runtime library and C API unit tests. The tests are written in C++ and use the Google Test framework. """ @@ -6,66 +6,165 @@ from __future__ import annotations import os +import platform import subprocess import sys -from distutils.command.build_ext import build_ext -from distutils.core import Extension, setup +from distutils import ccompiler, sysconfig from typing import Any -kwargs: dict[str, Any] -if sys.platform == "darwin": - kwargs = {"language": "c++"} - compile_args = [] -else: - kwargs = {} - compile_args = ["--std=c++11"] +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext + +C_APIS_TO_TEST = [ + "init.c", + "int_ops.c", + "float_ops.c", + "list_ops.c", + "exc_ops.c", + "generic_ops.c", + "pythonsupport.c", +] + +EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT = { + "unix": { + "base64/arch/ssse3": ["-mssse3"], + "base64/arch/sse41": ["-msse4.1"], + "base64/arch/sse42": ["-msse4.2"], + "base64/arch/avx2": ["-mavx2"], + "base64/arch/avx": ["-mavx"], + }, + "msvc": { + "base64/arch/sse42": ["/arch:SSE4.2"], + "base64/arch/avx2": ["/arch:AVX2"], + "base64/arch/avx": ["/arch:AVX"], + }, +} + +ccompiler.CCompiler.__spawn = ccompiler.CCompiler.spawn # type: ignore[attr-defined] +X86_64 = platform.machine() in ("x86_64", "AMD64", "amd64") + + +def spawn(self, cmd, **kwargs) -> None: # type: ignore[no-untyped-def] + compiler_type: str = self.compiler_type + extra_options = EXTRA_FLAGS_PER_COMPILER_TYPE_PER_PATH_COMPONENT[compiler_type] + new_cmd = list(cmd) + if X86_64 and extra_options is not None: + # filenames are closer to the end of command line + for argument in reversed(new_cmd): + # Check if the matching argument contains a source filename. + if not str(argument).endswith(".c"): + continue + for path in extra_options.keys(): + if path in str(argument): + if compiler_type == "bcpp": + compiler = new_cmd.pop() + # Borland accepts a source file name at the end, + # insert the options before it + new_cmd.extend(extra_options[path]) + new_cmd.append(compiler) + else: + new_cmd.extend(extra_options[path]) -class build_ext_custom(build_ext): - def get_library_names(self): + # path component is found, no need to search any further + break + self.__spawn(new_cmd, **kwargs) + + +ccompiler.CCompiler.spawn = spawn # type: ignore[method-assign] + + +class BuildExtGtest(build_ext): + def get_library_names(self) -> list[str]: return ["gtest"] - def run(self): + def run(self) -> None: + # Build Google Test, the C++ framework we use for testing C code. + # The source code for Google Test is copied to this repository. gtest_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), "..", "external", "googletest") ) - os.makedirs(self.build_temp, exist_ok=True) - - # Build Google Test, the C++ framework we use for testing C code. - # The source code for Google Test is copied to this repository. subprocess.check_call( ["make", "-f", os.path.join(gtest_dir, "make", "Makefile"), f"GTEST_DIR={gtest_dir}"], cwd=self.build_temp, ) - self.library_dirs = [self.build_temp] - return build_ext.run(self) -setup( - name="test_capi", - version="0.1", - ext_modules=[ - Extension( - "test_capi", - [ - "test_capi.cc", - "init.c", - "int_ops.c", - "float_ops.c", - "list_ops.c", - "exc_ops.c", - "generic_ops.c", - "pythonsupport.c", - ], - depends=["CPy.h", "mypyc_util.h", "pythonsupport.h"], - extra_compile_args=["-Wno-unused-function", "-Wno-sign-compare"] + compile_args, - libraries=["gtest"], - include_dirs=["../external/googletest", "../external/googletest/include"], - **kwargs, - ) - ], - cmdclass={"build_ext": build_ext_custom}, -) +if "--run-capi-tests" in sys.argv: + sys.argv.pop() + + kwargs: dict[str, Any] + if sys.platform == "darwin": + kwargs = {"language": "c++"} + compile_args = [] + else: + kwargs = {} + compile_args = ["--std=c++11"] + + setup( + name="test_capi", + version="0.1", + ext_modules=[ + Extension( + "test_capi", + ["test_capi.cc"] + C_APIS_TO_TEST, + depends=["CPy.h", "mypyc_util.h", "pythonsupport.h"], + extra_compile_args=["-Wno-unused-function", "-Wno-sign-compare"] + compile_args, + libraries=["gtest"], + include_dirs=["../external/googletest", "../external/googletest/include"], + **kwargs, + ) + ], + cmdclass={"build_ext": BuildExtGtest}, + ) +else: + # TODO: we need a way to share our preferred C flags and get_extension() logic with + # mypyc/build.py without code duplication. + compiler = ccompiler.new_compiler() + sysconfig.customize_compiler(compiler) + cflags: list[str] = [] + if compiler.compiler_type == "unix": # type: ignore[attr-defined] + cflags += ["-O3"] + elif compiler.compiler_type == "msvc": # type: ignore[attr-defined] + cflags += ["/O2"] + + setup( + ext_modules=[ + Extension( + "librt.internal", + [ + "librt_internal.c", + "init.c", + "int_ops.c", + "exc_ops.c", + "pythonsupport.c", + "getargsfast.c", + ], + include_dirs=["."], + extra_compile_args=cflags, + ), + Extension( + "librt.base64", + [ + "librt_base64.c", + "base64/lib.c", + "base64/codec_choose.c", + "base64/tables/tables.c", + "base64/arch/generic/codec.c", + "base64/arch/ssse3/codec.c", + "base64/arch/sse41/codec.c", + "base64/arch/sse42/codec.c", + "base64/arch/avx/codec.c", + "base64/arch/avx2/codec.c", + "base64/arch/avx512/codec.c", + "base64/arch/neon32/codec.c", + "base64/arch/neon64/codec.c", + ], + include_dirs=[".", "base64"], + extra_compile_args=cflags, + ), + ] + ) diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 4ba181bcce85b..721a2bbb10b98 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + // String primitive operations // // These are registered in mypyc.primitives.str_ops. @@ -5,6 +7,94 @@ #include #include "CPy.h" +// The _PyUnicode_CheckConsistency definition has been moved to the internal API +// https://github.com/python/cpython/pull/106398 +#if defined(Py_DEBUG) && defined(CPY_3_13_FEATURES) +#include "internal/pycore_unicodeobject.h" +#endif + +// Copied from cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +#define BLOOM_MASK unsigned long +#define BLOOM(mask, ch) ((mask & (1UL << ((ch) & (BLOOM_WIDTH - 1))))) +#if LONG_BIT >= 128 +#define BLOOM_WIDTH 128 +#elif LONG_BIT >= 64 +#define BLOOM_WIDTH 64 +#elif LONG_BIT >= 32 +#define BLOOM_WIDTH 32 +#else +#error "LONG_BIT is smaller than 32" +#endif + +// Copied from cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +// This is needed for str.strip("..."). +static inline BLOOM_MASK +make_bloom_mask(int kind, const void* ptr, Py_ssize_t len) +{ +#define BLOOM_UPDATE(TYPE, MASK, PTR, LEN) \ + do { \ + TYPE *data = (TYPE *)PTR; \ + TYPE *end = data + LEN; \ + Py_UCS4 ch; \ + for (; data != end; data++) { \ + ch = *data; \ + MASK |= (1UL << (ch & (BLOOM_WIDTH - 1))); \ + } \ + break; \ + } while (0) + + /* calculate simple bloom-style bitmask for a given unicode string */ + + BLOOM_MASK mask; + + mask = 0; + switch (kind) { + case PyUnicode_1BYTE_KIND: + BLOOM_UPDATE(Py_UCS1, mask, ptr, len); + break; + case PyUnicode_2BYTE_KIND: + BLOOM_UPDATE(Py_UCS2, mask, ptr, len); + break; + case PyUnicode_4BYTE_KIND: + BLOOM_UPDATE(Py_UCS4, mask, ptr, len); + break; + default: + Py_UNREACHABLE(); + } + return mask; + +#undef BLOOM_UPDATE +} + +static inline char _CPyStr_Equal_NoIdentCheck(PyObject *str1, PyObject *str2, Py_ssize_t str2_length) { + // This helper function only exists to deduplicate code in CPyStr_Equal and CPyStr_EqualLiteral + Py_ssize_t str1_length = PyUnicode_GET_LENGTH(str1); + if (str1_length != str2_length) + return 0; + int kind = PyUnicode_KIND(str1); + if (PyUnicode_KIND(str2) != kind) + return 0; + const void *data1 = PyUnicode_DATA(str1); + const void *data2 = PyUnicode_DATA(str2); + return memcmp(data1, data2, str1_length * kind) == 0; +} + +// Adapted from CPython 3.13.1 (_PyUnicode_Equal) +char CPyStr_Equal(PyObject *str1, PyObject *str2) { + if (str1 == str2) { + return 1; + } + Py_ssize_t str2_length = PyUnicode_GET_LENGTH(str2); + return _CPyStr_Equal_NoIdentCheck(str1, str2, str2_length); +} + +char CPyStr_EqualLiteral(PyObject *str, PyObject *literal_str, Py_ssize_t literal_length) { + if (str == literal_str) { + return 1; + } + return _CPyStr_Equal_NoIdentCheck(str, literal_str, literal_length); +} + PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) { if (PyUnicode_READY(str) != -1) { if (CPyTagged_CheckShort(index)) { @@ -42,6 +132,11 @@ PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) { } } +PyObject *CPyStr_GetItemUnsafe(PyObject *str, Py_ssize_t index) { + // This is unsafe since we don't check for overflow when doing <<. + return CPyStr_GetItem(str, index << 1); +} + // A simplification of _PyUnicode_JoinArray() from CPython 3.9.6 PyObject *CPyStr_Build(Py_ssize_t len, ...) { Py_ssize_t i; @@ -129,10 +224,35 @@ PyObject *CPyStr_Build(Py_ssize_t len, ...) { assert(res_offset == PyUnicode_GET_LENGTH(res)); } +#ifdef Py_DEBUG assert(_PyUnicode_CheckConsistency(res, 1)); +#endif return res; } +CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction) { + CPyTagged end = PyUnicode_GET_LENGTH(str) << 1; + return CPyStr_FindWithEnd(str, substr, start, end, direction); +} + +CPyTagged CPyStr_FindWithEnd(PyObject *str, PyObject *substr, CPyTagged start, CPyTagged end, int direction) { + Py_ssize_t temp_start = CPyTagged_AsSsize_t(start); + if (temp_start == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return CPY_INT_TAG; + } + Py_ssize_t temp_end = CPyTagged_AsSsize_t(end); + if (temp_end == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return CPY_INT_TAG; + } + Py_ssize_t index = PyUnicode_Find(str, substr, temp_start, temp_end, direction); + if (unlikely(index == -2)) { + return CPY_INT_TAG; + } + return index << 1; +} + PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split) { Py_ssize_t temp_max_split = CPyTagged_AsSsize_t(max_split); if (temp_max_split == -1 && PyErr_Occurred()) { @@ -142,6 +262,133 @@ PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split) { return PyUnicode_Split(str, sep, temp_max_split); } +PyObject *CPyStr_RSplit(PyObject *str, PyObject *sep, CPyTagged max_split) { + Py_ssize_t temp_max_split = CPyTagged_AsSsize_t(max_split); + if (temp_max_split == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return NULL; + } + return PyUnicode_RSplit(str, sep, temp_max_split); +} + +// This function has been copied from _PyUnicode_XStrip in cpython.git:Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +static PyObject *_PyStr_XStrip(PyObject *self, int striptype, PyObject *sepobj) { + const void *data; + int kind; + Py_ssize_t i, j, len; + BLOOM_MASK sepmask; + Py_ssize_t seplen; + + // This check is needed from Python 3.9 and earlier. + if (PyUnicode_READY(self) == -1 || PyUnicode_READY(sepobj) == -1) + return NULL; + + kind = PyUnicode_KIND(self); + data = PyUnicode_DATA(self); + len = PyUnicode_GET_LENGTH(self); + seplen = PyUnicode_GET_LENGTH(sepobj); + sepmask = make_bloom_mask(PyUnicode_KIND(sepobj), + PyUnicode_DATA(sepobj), + seplen); + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, i); + if (!BLOOM(sepmask, ch)) + break; + if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0) + break; + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + j--; + while (j >= i) { + Py_UCS4 ch = PyUnicode_READ(kind, data, j); + if (!BLOOM(sepmask, ch)) + break; + if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0) + break; + j--; + } + + j++; + } + + return PyUnicode_Substring(self, i, j); +} + +// Copied from do_strip function in cpython.git/Objects/unicodeobject.c@0ef4ffeefd1737c18dc9326133c7894d58108c2e. +PyObject *_CPyStr_Strip(PyObject *self, int strip_type, PyObject *sep) { + if (sep == NULL || Py_IsNone(sep)) { + Py_ssize_t len, i, j; + + // This check is needed from Python 3.9 and earlier. + if (PyUnicode_READY(self) == -1) + return NULL; + + len = PyUnicode_GET_LENGTH(self); + + if (PyUnicode_IS_ASCII(self)) { + const Py_UCS1 *data = PyUnicode_1BYTE_DATA(self); + + i = 0; + if (strip_type != RIGHTSTRIP) { + while (i < len) { + Py_UCS1 ch = data[i]; + if (!_Py_ascii_whitespace[ch]) + break; + i++; + } + } + + j = len; + if (strip_type != LEFTSTRIP) { + j--; + while (j >= i) { + Py_UCS1 ch = data[j]; + if (!_Py_ascii_whitespace[ch]) + break; + j--; + } + j++; + } + } + else { + int kind = PyUnicode_KIND(self); + const void *data = PyUnicode_DATA(self); + + i = 0; + if (strip_type != RIGHTSTRIP) { + while (i < len) { + Py_UCS4 ch = PyUnicode_READ(kind, data, i); + if (!Py_UNICODE_ISSPACE(ch)) + break; + i++; + } + } + + j = len; + if (strip_type != LEFTSTRIP) { + j--; + while (j >= i) { + Py_UCS4 ch = PyUnicode_READ(kind, data, j); + if (!Py_UNICODE_ISSPACE(ch)) + break; + j--; + } + j++; + } + } + + return PyUnicode_Substring(self, i, j); + } + return _PyStr_XStrip(self, strip_type, sep); +} + PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, PyObject *new_substr, CPyTagged max_replace) { Py_ssize_t temp_max_replace = CPyTagged_AsSsize_t(max_replace); @@ -152,18 +399,74 @@ PyObject *CPyStr_Replace(PyObject *str, PyObject *old_substr, return PyUnicode_Replace(str, old_substr, new_substr, temp_max_replace); } -bool CPyStr_Startswith(PyObject *self, PyObject *subobj) { +int CPyStr_Startswith(PyObject *self, PyObject *subobj) { Py_ssize_t start = 0; Py_ssize_t end = PyUnicode_GET_LENGTH(self); + if (PyTuple_Check(subobj)) { + Py_ssize_t i; + for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { + PyObject *substring = PyTuple_GET_ITEM(subobj, i); + if (!PyUnicode_Check(substring)) { + PyErr_Format(PyExc_TypeError, + "tuple for startswith must only contain str, " + "not %.100s", + Py_TYPE(substring)->tp_name); + return 2; + } + int result = PyUnicode_Tailmatch(self, substring, start, end, -1); + if (result) { + return 1; + } + } + return 0; + } return PyUnicode_Tailmatch(self, subobj, start, end, -1); } -bool CPyStr_Endswith(PyObject *self, PyObject *subobj) { +int CPyStr_Endswith(PyObject *self, PyObject *subobj) { Py_ssize_t start = 0; Py_ssize_t end = PyUnicode_GET_LENGTH(self); + if (PyTuple_Check(subobj)) { + Py_ssize_t i; + for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { + PyObject *substring = PyTuple_GET_ITEM(subobj, i); + if (!PyUnicode_Check(substring)) { + PyErr_Format(PyExc_TypeError, + "tuple for endswith must only contain str, " + "not %.100s", + Py_TYPE(substring)->tp_name); + return 2; + } + int result = PyUnicode_Tailmatch(self, substring, start, end, 1); + if (result) { + return 1; + } + } + return 0; + } return PyUnicode_Tailmatch(self, subobj, start, end, 1); } +PyObject *CPyStr_Removeprefix(PyObject *self, PyObject *prefix) { + Py_ssize_t end = PyUnicode_GET_LENGTH(self); + int match = PyUnicode_Tailmatch(self, prefix, 0, end, -1); + if (match) { + Py_ssize_t prefix_end = PyUnicode_GET_LENGTH(prefix); + return PyUnicode_Substring(self, prefix_end, end); + } + return Py_NewRef(self); +} + +PyObject *CPyStr_Removesuffix(PyObject *self, PyObject *suffix) { + Py_ssize_t end = PyUnicode_GET_LENGTH(self); + int match = PyUnicode_Tailmatch(self, suffix, 0, end, 1); + if (match) { + Py_ssize_t suffix_end = PyUnicode_GET_LENGTH(suffix); + return PyUnicode_Substring(self, 0, end - suffix_end); + } + return Py_NewRef(self); +} + /* This does a dodgy attempt to append in place */ PyObject *CPyStr_Append(PyObject *o1, PyObject *o2) { PyUnicode_Append(&o1, o2); @@ -225,6 +528,45 @@ PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors) { } } +PyObject *CPy_DecodeUTF8(PyObject *bytes) { + if (PyBytes_CheckExact(bytes)) { + char *buffer = PyBytes_AsString(bytes); // Borrowed reference + if (buffer == NULL) { + return NULL; + } + Py_ssize_t size = PyBytes_Size(bytes); + return PyUnicode_DecodeUTF8(buffer, size, "strict"); + } else { + return PyUnicode_FromEncodedObject(bytes, "utf-8", "strict"); + } +} + +PyObject *CPy_DecodeASCII(PyObject *bytes) { + if (PyBytes_CheckExact(bytes)) { + char *buffer = PyBytes_AsString(bytes); // Borrowed reference + if (buffer == NULL) { + return NULL; + } + Py_ssize_t size = PyBytes_Size(bytes); + return PyUnicode_DecodeASCII(buffer, size, "strict");; + } else { + return PyUnicode_FromEncodedObject(bytes, "ascii", "strict"); + } +} + +PyObject *CPy_DecodeLatin1(PyObject *bytes) { + if (PyBytes_CheckExact(bytes)) { + char *buffer = PyBytes_AsString(bytes); // Borrowed reference + if (buffer == NULL) { + return NULL; + } + Py_ssize_t size = PyBytes_Size(bytes); + return PyUnicode_DecodeLatin1(buffer, size, "strict"); + } else { + return PyUnicode_FromEncodedObject(bytes, "latin1", "strict"); + } +} + PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors) { const char *enc = NULL; const char *err = NULL; @@ -243,3 +585,39 @@ PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors) { return NULL; } } + +Py_ssize_t CPyStr_Count(PyObject *unicode, PyObject *substring, CPyTagged start) { + Py_ssize_t temp_start = CPyTagged_AsSsize_t(start); + if (temp_start == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return -1; + } + Py_ssize_t end = PyUnicode_GET_LENGTH(unicode); + return PyUnicode_Count(unicode, substring, temp_start, end); +} + +Py_ssize_t CPyStr_CountFull(PyObject *unicode, PyObject *substring, CPyTagged start, CPyTagged end) { + Py_ssize_t temp_start = CPyTagged_AsSsize_t(start); + if (temp_start == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return -1; + } + Py_ssize_t temp_end = CPyTagged_AsSsize_t(end); + if (temp_end == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); + return -1; + } + return PyUnicode_Count(unicode, substring, temp_start, temp_end); +} + + +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/lib-rt/tuple_ops.c b/mypyc/lib-rt/tuple_ops.c index 64418974666fe..1df73f1907e2f 100644 --- a/mypyc/lib-rt/tuple_ops.c +++ b/mypyc/lib-rt/tuple_ops.c @@ -46,16 +46,17 @@ PyObject *CPySequenceTuple_GetSlice(PyObject *obj, CPyTagged start, CPyTagged en return CPyObject_GetSlice(obj, start, end); } +// No error checking +PyObject *CPySequenceTuple_GetItemUnsafe(PyObject *tuple, Py_ssize_t index) +{ + PyObject *result = PyTuple_GET_ITEM(tuple, index); + Py_INCREF(result); + return result; +} + // PyTuple_SET_ITEM does no error checking, // and should only be used to fill in brand new tuples. -bool CPySequenceTuple_SetItemUnsafe(PyObject *tuple, CPyTagged index, PyObject *value) +void CPySequenceTuple_SetItemUnsafe(PyObject *tuple, Py_ssize_t index, PyObject *value) { - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - PyTuple_SET_ITEM(tuple, n, value); - return true; - } else { - PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); - return false; - } + PyTuple_SET_ITEM(tuple, index, value); } diff --git a/mypyc/lower/list_ops.py b/mypyc/lower/list_ops.py index 0d2e3e7169d8a..631008db5db66 100644 --- a/mypyc/lower/list_ops.py +++ b/mypyc/lower/list_ops.py @@ -1,7 +1,7 @@ from __future__ import annotations from mypyc.common import PLATFORM_SIZE -from mypyc.ir.ops import GetElementPtr, Integer, IntOp, LoadMem, SetMem, Value +from mypyc.ir.ops import GetElementPtr, Integer, IntOp, SetMem, Value from mypyc.ir.rtypes import ( PyListObject, c_pyssize_t_rprimitive, @@ -22,7 +22,7 @@ def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> V base = args[0] index_value = args[1] value = args[2] - assert isinstance(index_value, Integer) + assert isinstance(index_value, Integer), index_value index = index_value.numeric_value() if index == 0: ptr = base @@ -42,4 +42,30 @@ def buf_init_item(builder: LowLevelIRBuilder, args: list[Value], line: int) -> V @lower_primitive_op("list_items") def list_items(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: ob_item_ptr = builder.add(GetElementPtr(args[0], PyListObject, "ob_item", line)) - return builder.add(LoadMem(pointer_rprimitive, ob_item_ptr, line)) + return builder.load_mem(ob_item_ptr, pointer_rprimitive) + + +def list_item_ptr(builder: LowLevelIRBuilder, obj: Value, index: Value, line: int) -> Value: + """Get a pointer to a list item (index must be valid and non-negative). + + Type of index must be c_pyssize_t_rprimitive, and obj must refer to a list object. + """ + # List items are represented as an array of pointers. Pointer to the item obj[index] is + # + index * . + items = list_items(builder, [obj], line) + delta = builder.add( + IntOp( + c_pyssize_t_rprimitive, + index, + Integer(PLATFORM_SIZE, c_pyssize_t_rprimitive), + IntOp.MUL, + ) + ) + return builder.add(IntOp(pointer_rprimitive, items, delta, IntOp.ADD)) + + +@lower_primitive_op("list_get_item_unsafe") +def list_get_item_unsafe(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + index = builder.coerce(args[1], c_pyssize_t_rprimitive, line) + item_ptr = list_item_ptr(builder, args[0], index, line) + return builder.load_mem(item_ptr, object_rprimitive) diff --git a/mypyc/lower/misc_ops.py b/mypyc/lower/misc_ops.py index 1effcd4f42ac4..3c42257c0dbe3 100644 --- a/mypyc/lower/misc_ops.py +++ b/mypyc/lower/misc_ops.py @@ -1,7 +1,7 @@ from __future__ import annotations -from mypyc.ir.ops import GetElementPtr, LoadMem, Value -from mypyc.ir.rtypes import PyVarObject, c_pyssize_t_rprimitive +from mypyc.ir.ops import ComparisonOp, GetElementPtr, Integer, LoadMem, Value +from mypyc.ir.rtypes import PyVarObject, c_pyssize_t_rprimitive, object_rprimitive from mypyc.irbuild.ll_builder import LowLevelIRBuilder from mypyc.lower.registry import lower_primitive_op @@ -10,3 +10,9 @@ def var_object_size(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: elem_address = builder.add(GetElementPtr(args[0], PyVarObject, "ob_size")) return builder.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + + +@lower_primitive_op("propagate_if_error") +def propagate_if_error_op(builder: LowLevelIRBuilder, args: list[Value], line: int) -> Value: + # Return False on NULL. The primitive uses ERR_FALSE, so this is an error. + return builder.add(ComparisonOp(args[0], Integer(0, object_rprimitive), ComparisonOp.NEQ)) diff --git a/mypyc/lower/registry.py b/mypyc/lower/registry.py index 084d57df46080..a20990fe39ae4 100644 --- a/mypyc/lower/registry.py +++ b/mypyc/lower/registry.py @@ -1,20 +1,22 @@ from __future__ import annotations -from typing import Callable, Final, List +from typing import Callable, Final, Optional, TypeVar from mypyc.ir.ops import Value from mypyc.irbuild.ll_builder import LowLevelIRBuilder -LowerFunc = Callable[[LowLevelIRBuilder, List[Value], int], Value] +LowerFunc = Callable[[LowLevelIRBuilder, list[Value], int], Value] +LowerFuncOpt = Callable[[LowLevelIRBuilder, list[Value], int], Optional[Value]] +lowering_registry: Final[dict[str, LowerFuncOpt]] = {} -lowering_registry: Final[dict[str, LowerFunc]] = {} +LF = TypeVar("LF", LowerFunc, LowerFuncOpt) -def lower_primitive_op(name: str) -> Callable[[LowerFunc], LowerFunc]: +def lower_primitive_op(name: str) -> Callable[[LF], LF]: """Register a handler that generates low-level IR for a primitive op.""" - def wrapper(f: LowerFunc) -> LowerFunc: + def wrapper(f: LF) -> LF: assert name not in lowering_registry lowering_registry[name] = f return f diff --git a/mypyc/namegen.py b/mypyc/namegen.py index 675dae9001c71..1e05531021752 100644 --- a/mypyc/namegen.py +++ b/mypyc/namegen.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable class NameGenerator: @@ -34,20 +34,30 @@ class NameGenerator: The generated should be internal to a build and thus the mapping is arbitrary. Just generating names '1', '2', ... would be correct, - though not very usable. + though not very usable. The generated names may be visible in CPU + profiles and when debugging using native debuggers. """ - def __init__(self, groups: Iterable[list[str]]) -> None: + def __init__(self, groups: Iterable[list[str]], *, separate: bool = False) -> None: """Initialize with a list of modules in each compilation group. The names of modules are used to shorten names referring to modules, for convenience. Arbitrary module names are supported for generated names, but uncompiled modules will use long names. + + If separate is True, assume separate compilation. This implies + that we don't have knowledge of all sources that will be linked + together. In this case we won't trim module prefixes, since we + don't have enough information to determine common module prefixes. """ self.module_map: dict[str, str] = {} for names in groups: - self.module_map.update(make_module_translation_map(names)) + if not separate: + self.module_map.update(make_module_translation_map(names)) + else: + for name in names: + self.module_map[name] = name + "." self.translations: dict[tuple[str, str], str] = {} self.used_names: set[str] = set() @@ -100,10 +110,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..9f16c07dc231a 100644 --- a/mypyc/options.py +++ b/mypyc/options.py @@ -14,6 +14,11 @@ 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, + group_name: str | None = None, + log_trace: bool = False, + depends_on_librt_internal: bool = False, + experimental_features: bool = False, ) -> None: self.strip_asserts = strip_asserts self.multi_file = multi_file @@ -30,3 +35,28 @@ 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 + # Override the automatic group name derived from the hash of module names. + # This affects the names of generated .c, .h and shared library files. + # This is only supported when compiling exactly one group, and a shared + # library is generated (with shims). This can be used to make the output + # file names more predictable. + self.group_name = group_name + # If enabled, write a trace log of events based on executed operations to + # mypyc_trace.txt when compiled module is executed. This is useful for + # performance analysis. + self.log_trace = log_trace + # If enabled, add capsule imports of librt.internal API. This should be used + # only for mypy itself, third-party code compiled with mypyc should not use + # librt.internal. + self.depends_on_librt_internal = depends_on_librt_internal + # Some experimental features are only available when building librt in + # experimental mode (e.g. use _experimental suffix in librt run test). + # These can't be used with a librt wheel installed from PyPI. + self.experimental_features = experimental_features diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index d7a7f3e2f59b8..c88e89d1a2bad 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -2,9 +2,10 @@ from __future__ import annotations -from mypyc.ir.ops import ERR_MAGIC +from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( RUnion, + bit_rprimitive, bytes_rprimitive, c_int_rprimitive, c_pyssize_t_rprimitive, @@ -35,6 +36,15 @@ error_kind=ERR_MAGIC, ) +# translate isinstance(obj, bytes) +isinstance_bytes = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyBytes_Check", + error_kind=ERR_NEVER, +) + # bytearray(obj) function_op( name="builtins.bytearray", @@ -44,6 +54,15 @@ error_kind=ERR_MAGIC, ) +# translate isinstance(obj, bytearray) +isinstance_bytearray = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyByteArray_Check", + error_kind=ERR_NEVER, +) + # bytes ==/!= (return -1/0/1) bytes_compare = custom_op( arg_types=[bytes_rprimitive, bytes_rprimitive], @@ -99,3 +118,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/dict_ops.py b/mypyc/primitives/dict_ops.py index ce7b9bb8d70e1..f98bcc8ac2ec2 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -53,7 +53,7 @@ ) # Construct a dictionary from another dictionary. -function_op( +dict_copy_op = function_op( name="builtins.dict", arg_types=[dict_rprimitive], return_type=dict_rprimitive, @@ -71,6 +71,15 @@ error_kind=ERR_MAGIC, ) +# translate isinstance(obj, dict) +isinstance_dict = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyDict_Check", + error_kind=ERR_NEVER, +) + # dict[key] dict_get_item_op = method_op( name="__getitem__", @@ -89,6 +98,15 @@ error_kind=ERR_NEG_INT, ) +# dict[key] = value (exact dict only, no subclasses) +# NOTE: this is currently for internal use only, and not used for CallExpr specialization +exact_dict_set_item_op = custom_op( + arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive], + return_type=c_int_rprimitive, + c_function_name="PyDict_SetItem", + error_kind=ERR_NEG_INT, +) + # key in dict binary_op( name="in", @@ -289,7 +307,7 @@ # check that len(dict) == const during iteration dict_check_size_op = custom_op( - arg_types=[dict_rprimitive, int_rprimitive], + arg_types=[dict_rprimitive, c_pyssize_t_rprimitive], return_type=bit_rprimitive, c_function_name="CPyDict_CheckSize", error_kind=ERR_FALSE, diff --git a/mypyc/primitives/exc_ops.py b/mypyc/primitives/exc_ops.py index ad105056158a8..e1234f807afae 100644 --- a/mypyc/primitives/exc_ops.py +++ b/mypyc/primitives/exc_ops.py @@ -4,7 +4,7 @@ from mypyc.ir.ops import ERR_ALWAYS, ERR_FALSE, ERR_NEVER from mypyc.ir.rtypes import bit_rprimitive, exc_rtuple, object_rprimitive, void_rtype -from mypyc.primitives.registry import custom_op +from mypyc.primitives.registry import custom_op, custom_primitive_op # If the argument is a class, raise an instance of the class. Otherwise, assume # that the argument is an exception object, and raise it. @@ -41,7 +41,7 @@ no_err_occurred_op = custom_op( arg_types=[], return_type=bit_rprimitive, - c_function_name="CPy_NoErrOccured", + c_function_name="CPy_NoErrOccurred", error_kind=ERR_FALSE, ) @@ -62,6 +62,16 @@ error_kind=ERR_FALSE, ) +# If argument is NULL, propagate currently raised exception (in this case +# an exception must have been raised). If this can be used, it's faster +# than using PyErr_Occurred(). +propagate_if_error_op = custom_primitive_op( + "propagate_if_error", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + error_kind=ERR_FALSE, +) + # Catches a propagating exception and makes it the "currently # handled exception" (by sticking it into sys.exc_info()). Returns the # exception that was previously being handled, which must be restored diff --git a/mypyc/primitives/float_ops.py b/mypyc/primitives/float_ops.py index 14e8d4caf09cf..542192add5420 100644 --- a/mypyc/primitives/float_ops.py +++ b/mypyc/primitives/float_ops.py @@ -4,6 +4,7 @@ from mypyc.ir.ops import ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER from mypyc.ir.rtypes import ( + bit_rprimitive, bool_rprimitive, float_rprimitive, int_rprimitive, @@ -166,3 +167,12 @@ c_function_name="CPyFloat_IsNaN", error_kind=ERR_NEVER, ) + +# translate isinstance(obj, float) +isinstance_float = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyFloat_Check", + error_kind=ERR_NEVER, +) diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index fe42767db11e5..1003fda8d9ae8 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -26,6 +26,7 @@ ERR_NEG_INT, binary_op, custom_op, + custom_primitive_op, function_op, method_op, unary_op, @@ -281,7 +282,7 @@ object_rprimitive, ], # Keyword arg names tuple (or NULL) return_type=object_rprimitive, - c_function_name="_PyObject_Vectorcall", + c_function_name="PyObject_Vectorcall", error_kind=ERR_MAGIC, ) @@ -307,6 +308,15 @@ error_kind=ERR_MAGIC, ) +# Call callable object with positional args only: func(*args) +# Arguments are (func, *args tuple). +py_call_with_posargs_op = custom_op( + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name="PyObject_CallObject", + error_kind=ERR_MAGIC, +) + # Call method with positional arguments: obj.method(arg1, ...) # Arguments are (object, attribute name, arg1, ...). py_method_call_op = custom_op( @@ -382,3 +392,35 @@ c_function_name="CPy_GetANext", error_kind=ERR_MAGIC, ) + +# x.__name__ (requires Python 3.11+) +name_op = custom_primitive_op( + name="__name__", + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name="CPy_GetName", + error_kind=ERR_MAGIC, +) + +# look-up name in tp_dict but don't raise AttributeError on failure +generic_getattr = custom_op( + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name="CPyObject_GenericGetAttr", + error_kind=ERR_NEVER, + returns_null=True, +) + +generic_setattr = custom_op( + arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], + return_type=c_int_rprimitive, + c_function_name="CPyObject_GenericSetAttr", + error_kind=ERR_NEG_INT, +) + +setup_object = custom_op( + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name="CPy_SetupObject", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 2eff233403f48..8f43140dd2559 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -32,11 +32,11 @@ void_rtype, ) from mypyc.primitives.registry import ( - CFunctionDescription, binary_op, custom_op, function_op, load_address_op, + method_op, unary_op, ) @@ -84,25 +84,25 @@ error_kind=ERR_MAGIC, ) -# str(int) -int_to_str_op = function_op( - name="builtins.str", - arg_types=[int_rprimitive], - return_type=str_rprimitive, - c_function_name="CPyTagged_Str", - error_kind=ERR_MAGIC, - priority=2, -) - -# We need a specialization for str on bools also since the int one is wrong... -function_op( - name="builtins.str", - arg_types=[bool_rprimitive], - return_type=str_rprimitive, - c_function_name="CPyBool_Str", - error_kind=ERR_MAGIC, - priority=3, -) +for name in ("builtins.str", "builtins.repr"): + # str(int) and repr(int) + int_to_str_op = function_op( + name=name, + arg_types=[int_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyTagged_Str", + error_kind=ERR_MAGIC, + priority=2, + ) + # We need a specialization for str on bools also since the int one is wrong... + function_op( + name=name, + arg_types=[bool_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyBool_Str", + error_kind=ERR_MAGIC, + priority=3, + ) def int_binary_primitive( @@ -176,7 +176,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, @@ -303,3 +303,21 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: c_function_name="CPyUInt8_Overflow", error_kind=ERR_ALWAYS, ) + +# translate isinstance(obj, int) +isinstance_int = function_op( + name="builtints.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyLong_Check", + error_kind=ERR_NEVER, +) + +# int.bit_length() +method_op( + name="bit_length", + arg_types=[int_rprimitive], + return_type=int_rprimitive, + c_function_name="CPyTagged_BitLength", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index cb75e19a8dea3..b9d20a25bea35 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -13,6 +13,7 @@ object_rprimitive, pointer_rprimitive, short_int_rprimitive, + void_rtype, ) from mypyc.primitives.registry import ( ERR_NEG_INT, @@ -27,6 +28,15 @@ # Get the 'builtins.list' type object. load_address_op(name="builtins.list", type=object_rprimitive, src="PyList_Type") +# sorted(obj) +function_op( + name="builtins.sorted", + arg_types=[object_rprimitive], + return_type=list_rprimitive, + c_function_name="CPySequence_Sort", + error_kind=ERR_MAGIC, +) + # list(obj) to_list = function_op( name="builtins.list", @@ -46,6 +56,15 @@ extra_int_constants=[(0, int_rprimitive)], ) +# translate isinstance(obj, list) +isinstance_list = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyList_Check", + error_kind=ERR_NEVER, +) + new_list_op = custom_op( arg_types=[c_pyssize_t_rprimitive], return_type=list_rprimitive, @@ -134,10 +153,10 @@ # This is unsafe because it assumes that the index is a non-negative short integer # that is in-bounds for the list. -list_get_item_unsafe_op = custom_op( - arg_types=[list_rprimitive, short_int_rprimitive], +list_get_item_unsafe_op = custom_primitive_op( + name="list_get_item_unsafe", + arg_types=[list_rprimitive, c_pyssize_t_rprimitive], return_type=object_rprimitive, - c_function_name="CPyList_GetItemUnsafe", error_kind=ERR_NEVER, ) @@ -165,10 +184,10 @@ # PyList_SET_ITEM does no error checking, # and should only be used to fill in brand new lists. new_list_set_item_op = custom_op( - arg_types=[list_rprimitive, int_rprimitive, object_rprimitive], - return_type=bit_rprimitive, + arg_types=[list_rprimitive, c_pyssize_t_rprimitive, object_rprimitive], + return_type=void_rtype, c_function_name="CPyList_SetItemUnsafe", - error_kind=ERR_FALSE, + error_kind=ERR_NEVER, steals=[False, False, True], ) @@ -200,7 +219,7 @@ ) # list.pop(index) -list_pop = method_op( +method_op( name="pop", arg_types=[list_rprimitive, int_rprimitive], return_type=object_rprimitive, @@ -262,6 +281,42 @@ error_kind=ERR_MAGIC, ) +# list.clear() +method_op( + name="clear", + arg_types=[list_rprimitive], + return_type=bit_rprimitive, + c_function_name="CPyList_Clear", + error_kind=ERR_FALSE, +) + +# list.copy() +method_op( + name="copy", + arg_types=[list_rprimitive], + return_type=list_rprimitive, + c_function_name="CPyList_Copy", + error_kind=ERR_MAGIC, +) + +# list + list +binary_op( + name="+", + arg_types=[list_rprimitive, list_rprimitive], + return_type=list_rprimitive, + c_function_name="PySequence_Concat", + error_kind=ERR_MAGIC, +) + +# list += list +binary_op( + name="+=", + arg_types=[list_rprimitive, object_rprimitive], + return_type=list_rprimitive, + c_function_name="PySequence_InPlaceConcat", + error_kind=ERR_MAGIC, +) + # list * int binary_op( name="*", @@ -280,6 +335,15 @@ error_kind=ERR_MAGIC, ) +# list *= int +binary_op( + name="*=", + arg_types=[list_rprimitive, int_rprimitive], + return_type=list_rprimitive, + c_function_name="CPySequence_InPlaceMultiply", + error_kind=ERR_MAGIC, +) + # list[begin:end] list_slice_op = custom_op( arg_types=[list_rprimitive, int_rprimitive, int_rprimitive], diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index e9016e24c46d8..bb225a76acd88 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -2,19 +2,25 @@ from __future__ import annotations -from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER +from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_MAGIC_OVERLAPPING, ERR_NEVER from mypyc.ir.rtypes import ( + KNOWN_NATIVE_TYPES, bit_rprimitive, bool_rprimitive, + bytes_rprimitive, c_int_rprimitive, c_pointer_rprimitive, c_pyssize_t_rprimitive, + cstring_rprimitive, dict_rprimitive, + float_rprimitive, int_rprimitive, + none_rprimitive, object_pointer_rprimitive, object_rprimitive, pointer_rprimitive, str_rprimitive, + uint8_rprimitive, void_rtype, ) from mypyc.primitives.registry import ( @@ -23,6 +29,7 @@ custom_primitive_op, function_op, load_address_op, + method_op, ) # Get the 'bool' type object. @@ -191,6 +198,15 @@ truncated_type=bool_rprimitive, ) +# isinstance(obj, bool) +isinstance_bool = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyBool_Check", + error_kind=ERR_NEVER, +) + # slice(start, stop, step) new_slice_op = function_op( name="builtins.slice", @@ -204,7 +220,7 @@ type_op = function_op( name="builtins.type", arg_types=[object_rprimitive], - c_function_name="PyObject_Type", + c_function_name="CPy_TYPE", return_type=object_rprimitive, error_kind=ERR_NEVER, ) @@ -224,7 +240,13 @@ # Create a dataclass from an extension class. See # CPyDataclass_SleightOfHand for more docs. dataclass_sleight_of_hand = custom_op( - arg_types=[object_rprimitive, object_rprimitive, dict_rprimitive, dict_rprimitive], + arg_types=[ + object_rprimitive, + object_rprimitive, + dict_rprimitive, + dict_rprimitive, + str_rprimitive, + ], return_type=bit_rprimitive, c_function_name="CPyDataclass_SleightOfHand", error_kind=ERR_FALSE, @@ -277,3 +299,179 @@ return_type=void_rtype, error_kind=ERR_NEVER, ) + +debug_print_op = custom_primitive_op( + name="debug_print", + c_function_name="CPyDebug_PrintObject", + arg_types=[object_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, +) + +# Log an event to a trace log, which is written to a file during execution. +log_trace_event = custom_primitive_op( + name="log_trace_event", + c_function_name="CPyTrace_LogEvent", + # (fullname of function/location, line number, operation name, operation details) + arg_types=[cstring_rprimitive, cstring_rprimitive, cstring_rprimitive, cstring_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, +) + +# Mark object as immortal -- it won't be freed via reference counting, as +# the reference count won't be updated any longer. Immortal objects support +# fast concurrent read-only access from multiple threads when using free +# threading, since this eliminates contention from concurrent reference count +# updates. +# +# Needs at least Python 3.14. +set_immortal_op = custom_primitive_op( + name="set_immmortal", + c_function_name="CPy_SetImmortal", + arg_types=[object_rprimitive], + return_type=void_rtype, + error_kind=ERR_NEVER, +) + +write_buffer_rprimitive = KNOWN_NATIVE_TYPES["librt.internal.WriteBuffer"] +read_buffer_rprimitive = KNOWN_NATIVE_TYPES["librt.internal.ReadBuffer"] + +# ReadBuffer(source) +function_op( + name="librt.internal.ReadBuffer", + arg_types=[bytes_rprimitive], + return_type=read_buffer_rprimitive, + c_function_name="ReadBuffer_internal", + error_kind=ERR_MAGIC, +) + +# WriteBuffer() +function_op( + name="librt.internal.WriteBuffer", + arg_types=[], + return_type=write_buffer_rprimitive, + c_function_name="WriteBuffer_internal", + error_kind=ERR_MAGIC, +) + +method_op( + name="getvalue", + arg_types=[write_buffer_rprimitive], + return_type=bytes_rprimitive, + c_function_name="WriteBuffer_getvalue_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.write_bool", + arg_types=[object_rprimitive, bool_rprimitive], + return_type=none_rprimitive, + c_function_name="write_bool_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.read_bool", + arg_types=[object_rprimitive], + return_type=bool_rprimitive, + c_function_name="read_bool_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.write_str", + arg_types=[object_rprimitive, str_rprimitive], + return_type=none_rprimitive, + c_function_name="write_str_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.read_str", + arg_types=[object_rprimitive], + return_type=str_rprimitive, + c_function_name="read_str_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.write_bytes", + arg_types=[object_rprimitive, bytes_rprimitive], + return_type=none_rprimitive, + c_function_name="write_bytes_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.read_bytes", + arg_types=[object_rprimitive], + return_type=bytes_rprimitive, + c_function_name="read_bytes_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.write_float", + arg_types=[object_rprimitive, float_rprimitive], + return_type=none_rprimitive, + c_function_name="write_float_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.read_float", + arg_types=[object_rprimitive], + return_type=float_rprimitive, + c_function_name="read_float_internal", + error_kind=ERR_MAGIC_OVERLAPPING, +) + +function_op( + name="librt.internal.write_int", + arg_types=[object_rprimitive, int_rprimitive], + return_type=none_rprimitive, + c_function_name="write_int_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.read_int", + arg_types=[object_rprimitive], + return_type=int_rprimitive, + c_function_name="read_int_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.write_tag", + arg_types=[object_rprimitive, uint8_rprimitive], + return_type=none_rprimitive, + c_function_name="write_tag_internal", + error_kind=ERR_MAGIC, +) + +function_op( + name="librt.internal.read_tag", + arg_types=[object_rprimitive], + return_type=uint8_rprimitive, + c_function_name="read_tag_internal", + error_kind=ERR_MAGIC_OVERLAPPING, +) + +function_op( + name="librt.internal.cache_version", + arg_types=[], + return_type=uint8_rprimitive, + c_function_name="cache_version_internal", + error_kind=ERR_NEVER, +) + +function_op( + name="librt.base64.b64encode", + arg_types=[bytes_rprimitive], + return_type=bytes_rprimitive, + c_function_name="LibRTBase64_b64encode_internal", + error_kind=ERR_MAGIC, + experimental=True, + capsule="librt.base64", +) diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 5190b01adf4a4..2f66b19155013 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -61,6 +61,8 @@ class CFunctionDescription(NamedTuple): extra_int_constants: list[tuple[int, RType]] priority: int is_pure: bool + returns_null: bool + capsule: str | None # A description for C load operations including LoadGlobal and LoadAddress @@ -70,17 +72,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 +101,8 @@ def method_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: + capsule: str | None = None, +) -> PrimitiveDescription: """Define a c function call op that replaces a method call. This will be automatically generated by matching against the AST. @@ -129,7 +132,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, @@ -143,6 +146,8 @@ def method_op( extra_int_constants, priority, is_pure=is_pure, + experimental=False, + capsule=capsule, ) ops.append(desc) return desc @@ -161,8 +166,10 @@ 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. + experimental: bool = False, + capsule: str | None = None, +) -> PrimitiveDescription: + """Define a C function call op that replaces a function call. This will be automatically generated by matching against the AST. @@ -175,20 +182,22 @@ 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, + experimental=experimental, + capsule=capsule, ) ops.append(desc) return desc @@ -208,6 +217,7 @@ def binary_op( steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, + capsule: str | None = None, ) -> PrimitiveDescription: """Define a c function call op for a binary operation. @@ -235,6 +245,8 @@ def binary_op( extra_int_constants=extra_int_constants, priority=priority, is_pure=False, + experimental=False, + capsule=capsule, ) ops.append(desc) return desc @@ -253,6 +265,7 @@ def custom_op( is_borrowed: bool = False, *, is_pure: bool = False, + returns_null: bool = False, ) -> CFunctionDescription: """Create a one-off CallC op that can't be automatically generated from the AST. @@ -274,6 +287,8 @@ def custom_op( extra_int_constants, 0, is_pure=is_pure, + returns_null=returns_null, + capsule=None, ) @@ -290,6 +305,7 @@ def custom_primitive_op( steals: StealsDescription = False, is_borrowed: bool = False, is_pure: bool = False, + capsule: str | None = None, ) -> PrimitiveDescription: """Define a primitive op that can't be automatically generated based on the AST. @@ -311,6 +327,8 @@ def custom_primitive_op( extra_int_constants=extra_int_constants, priority=0, is_pure=is_pure, + experimental=False, + capsule=capsule, ) @@ -327,8 +345,9 @@ 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. + capsule: str | None = None, +) -> PrimitiveDescription: + """Define a primitive op for an unary operation. This will be automatically generated by matching against the AST. @@ -338,20 +357,22 @@ 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, + experimental=False, + capsule=capsule, ) ops.append(desc) return desc @@ -371,4 +392,5 @@ def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription: import mypyc.primitives.list_ops import mypyc.primitives.misc_ops import mypyc.primitives.str_ops -import mypyc.primitives.tuple_ops # noqa: F401 +import mypyc.primitives.tuple_ops +import mypyc.primitives.weakref_ops # noqa: F401 diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index fcfb7847dc7d5..786de008746dd 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -1,12 +1,13 @@ -"""Primitive set (and frozenset) ops.""" +"""Primitive set and frozenset ops.""" from __future__ import annotations -from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC +from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( bit_rprimitive, bool_rprimitive, c_int_rprimitive, + frozenset_rprimitive, object_rprimitive, pointer_rprimitive, set_rprimitive, @@ -22,7 +23,7 @@ # Get the 'builtins.set' type object. load_address_op(name="builtins.set", type=object_rprimitive, src="PySet_Type") -# Get the 'builtins.frozenset' tyoe object. +# Get the 'builtins.frozenset' type object. load_address_op(name="builtins.frozenset", type=object_rprimitive, src="PyFrozenSet_Type") # Construct an empty set. @@ -44,15 +45,43 @@ error_kind=ERR_MAGIC, ) +# Construct an empty frozenset +function_op( + name="builtins.frozenset", + arg_types=[], + return_type=frozenset_rprimitive, + c_function_name="PyFrozenSet_New", + error_kind=ERR_MAGIC, + extra_int_constants=[(0, pointer_rprimitive)], +) + # frozenset(obj) function_op( name="builtins.frozenset", arg_types=[object_rprimitive], - return_type=object_rprimitive, + return_type=frozenset_rprimitive, c_function_name="PyFrozenSet_New", error_kind=ERR_MAGIC, ) +# translate isinstance(obj, set) +isinstance_set = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PySet_Check", + error_kind=ERR_NEVER, +) + +# translate isinstance(obj, frozenset) +isinstance_frozenset = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyFrozenSet_Check", + error_kind=ERR_NEVER, +) + # item in set set_in_op = binary_op( name="in", @@ -64,6 +93,17 @@ ordering=[1, 0], ) +# item in frozenset +binary_op( + name="in", + arg_types=[object_rprimitive, frozenset_rprimitive], + return_type=c_int_rprimitive, + c_function_name="PySet_Contains", + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive, + ordering=[1, 0], +) + # set.remove(obj) method_op( name="remove", diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 2ff1fbdb4b3ed..d39f1f872763e 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -15,11 +15,13 @@ object_rprimitive, pointer_rprimitive, str_rprimitive, + tuple_rprimitive, ) from mypyc.primitives.registry import ( ERR_NEG_INT, binary_op, custom_op, + custom_primitive_op, function_op, load_address_op, method_op, @@ -37,6 +39,24 @@ error_kind=ERR_MAGIC, ) +# repr(obj) +function_op( + name="builtins.repr", + arg_types=[object_rprimitive], + return_type=str_rprimitive, + c_function_name="PyObject_Repr", + error_kind=ERR_MAGIC, +) + +# translate isinstance(obj, str) +isinstance_str = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyUnicode_Check", + error_kind=ERR_NEVER, +) + # str1 + str2 binary_op( name="+", @@ -59,6 +79,23 @@ steals=[True, False], ) +# str1 == str2 (very common operation, so we provide our own) +str_eq = custom_primitive_op( + name="str_eq", + c_function_name="CPyStr_Equal", + arg_types=[str_rprimitive, str_rprimitive], + return_type=bool_rprimitive, + error_kind=ERR_NEVER, +) + +str_eq_literal = custom_primitive_op( + name="str_eq_literal", + c_function_name="CPyStr_EqualLiteral", + arg_types=[str_rprimitive, str_rprimitive, c_pyssize_t_rprimitive], + return_type=bool_rprimitive, + error_kind=ERR_NEVER, +) + unicode_compare = custom_op( arg_types=[str_rprimitive, str_rprimitive], return_type=c_int_rprimitive, @@ -75,6 +112,15 @@ error_kind=ERR_MAGIC, ) +# This is unsafe since it assumes that the index is within reasonable bounds. +# In the future this might do no bounds checking at all. +str_get_item_unsafe_op = custom_op( + arg_types=[str_rprimitive, c_pyssize_t_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyStr_GetItemUnsafe", + error_kind=ERR_MAGIC, +) + # str[begin:end] str_slice_op = custom_op( arg_types=[str_rprimitive, int_rprimitive, int_rprimitive], @@ -83,6 +129,40 @@ error_kind=ERR_MAGIC, ) +# item in str +binary_op( + name="in", + arg_types=[str_rprimitive, str_rprimitive], + return_type=c_int_rprimitive, + c_function_name="PyUnicode_Contains", + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive, + ordering=[1, 0], +) + +# str.find(...) and str.rfind(...) +str_find_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive, int_rprimitive] +str_find_functions = ["CPyStr_Find", "CPyStr_Find", "CPyStr_FindWithEnd"] +str_find_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], [], []] +str_rfind_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], [], []] +for i in range(len(str_find_types) - 1): + method_op( + name="find", + arg_types=str_find_types[0 : i + 2], + return_type=int_rprimitive, + c_function_name=str_find_functions[i], + extra_int_constants=str_find_constants[i] + [(1, c_int_rprimitive)], + error_kind=ERR_MAGIC, + ) + method_op( + name="rfind", + arg_types=str_find_types[0 : i + 2], + return_type=int_rprimitive, + c_function_name=str_find_functions[i], + extra_int_constants=str_rfind_constants[i] + [(-1, c_int_rprimitive)], + error_kind=ERR_MAGIC, + ) + # str.join(obj) method_op( name="join", @@ -100,27 +180,85 @@ var_arg_type=str_rprimitive, ) +# str.strip, str.lstrip, str.rstrip +for strip_prefix in ["l", "r", ""]: + method_op( + name=f"{strip_prefix}strip", + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name=f"CPyStr_{strip_prefix.upper()}Strip", + error_kind=ERR_NEVER, + ) + method_op( + name=f"{strip_prefix}strip", + arg_types=[str_rprimitive], + return_type=str_rprimitive, + c_function_name=f"CPyStr_{strip_prefix.upper()}Strip", + # This 0 below is implicitly treated as NULL in C. + extra_int_constants=[(0, c_int_rprimitive)], + error_kind=ERR_NEVER, + ) + # str.startswith(str) method_op( name="startswith", arg_types=[str_rprimitive, str_rprimitive], - return_type=bool_rprimitive, + return_type=c_int_rprimitive, c_function_name="CPyStr_Startswith", + truncated_type=bool_rprimitive, error_kind=ERR_NEVER, ) +# str.startswith(tuple) +method_op( + name="startswith", + arg_types=[str_rprimitive, tuple_rprimitive], + return_type=bool_rprimitive, + c_function_name="CPyStr_Startswith", + error_kind=ERR_MAGIC, +) + # str.endswith(str) method_op( name="endswith", arg_types=[str_rprimitive, str_rprimitive], + return_type=c_int_rprimitive, + c_function_name="CPyStr_Endswith", + truncated_type=bool_rprimitive, + error_kind=ERR_NEVER, +) + +# str.endswith(tuple) +method_op( + name="endswith", + arg_types=[str_rprimitive, tuple_rprimitive], return_type=bool_rprimitive, c_function_name="CPyStr_Endswith", + error_kind=ERR_MAGIC, +) + +# str.removeprefix(str) +method_op( + name="removeprefix", + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyStr_Removeprefix", + error_kind=ERR_NEVER, +) + +# str.removesuffix(str) +method_op( + name="removesuffix", + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name="CPyStr_Removesuffix", error_kind=ERR_NEVER, ) -# str.split(...) +# str.split(...) and str.rsplit(...) str_split_types: list[RType] = [str_rprimitive, str_rprimitive, int_rprimitive] str_split_functions = ["PyUnicode_Split", "PyUnicode_Split", "CPyStr_Split"] +str_rsplit_functions = ["PyUnicode_RSplit", "PyUnicode_RSplit", "CPyStr_RSplit"] str_split_constants: list[list[tuple[int, RType]]] = [ [(0, pointer_rprimitive), (-1, c_int_rprimitive)], [(-1, c_int_rprimitive)], @@ -135,6 +273,73 @@ extra_int_constants=str_split_constants[i], error_kind=ERR_MAGIC, ) + method_op( + name="rsplit", + arg_types=str_split_types[0 : i + 1], + return_type=list_rprimitive, + c_function_name=str_rsplit_functions[i], + extra_int_constants=str_split_constants[i], + error_kind=ERR_MAGIC, + ) + +# str.splitlines(...) +str_splitlines_types: list[RType] = [str_rprimitive, bool_rprimitive] +str_splitlines_constants: list[list[tuple[int, RType]]] = [[(0, c_int_rprimitive)], []] +for i in range(2): + method_op( + name="splitlines", + arg_types=str_splitlines_types[0 : i + 1], + return_type=list_rprimitive, + c_function_name="PyUnicode_Splitlines", + extra_int_constants=str_splitlines_constants[i], + error_kind=ERR_NEVER, + ) + +# str.partition(str) +method_op( + name="partition", + arg_types=[str_rprimitive, str_rprimitive], + return_type=tuple_rprimitive, + c_function_name="PyUnicode_Partition", + error_kind=ERR_MAGIC, +) + +# str.rpartition(str) +method_op( + name="rpartition", + arg_types=[str_rprimitive, str_rprimitive], + return_type=tuple_rprimitive, + c_function_name="PyUnicode_RPartition", + error_kind=ERR_MAGIC, +) + +# str.count(substring) +method_op( + name="count", + arg_types=[str_rprimitive, str_rprimitive], + return_type=c_pyssize_t_rprimitive, + c_function_name="CPyStr_Count", + error_kind=ERR_NEG_INT, + extra_int_constants=[(0, c_pyssize_t_rprimitive)], +) + +# str.count(substring, start) +method_op( + name="count", + arg_types=[str_rprimitive, str_rprimitive, int_rprimitive], + return_type=c_pyssize_t_rprimitive, + c_function_name="CPyStr_Count", + error_kind=ERR_NEG_INT, +) + +# str.count(substring, start, end) +method_op( + name="count", + arg_types=[str_rprimitive, str_rprimitive, int_rprimitive, int_rprimitive], + return_type=c_pyssize_t_rprimitive, + c_function_name="CPyStr_CountFull", + error_kind=ERR_NEG_INT, +) # str.replace(old, new) method_op( @@ -190,7 +395,7 @@ extra_int_constants=[(0, pointer_rprimitive)], ) -# obj.decode(encoding, errors) +# bytes.decode(encoding, errors) method_op( name="decode", arg_types=[bytes_rprimitive, str_rprimitive, str_rprimitive], @@ -199,6 +404,30 @@ error_kind=ERR_MAGIC, ) +# bytes.decode(encoding) - utf8 strict specialization +bytes_decode_utf8_strict = custom_op( + arg_types=[bytes_rprimitive], + return_type=str_rprimitive, + c_function_name="CPy_DecodeUTF8", + error_kind=ERR_MAGIC, +) + +# bytes.decode(encoding) - ascii strict specialization +bytes_decode_ascii_strict = custom_op( + arg_types=[bytes_rprimitive], + return_type=str_rprimitive, + c_function_name="CPy_DecodeASCII", + error_kind=ERR_MAGIC, +) + +# bytes.decode(encoding) - latin1 strict specialization +bytes_decode_latin1_strict = custom_op( + arg_types=[bytes_rprimitive], + return_type=str_rprimitive, + c_function_name="CPy_DecodeLatin1", + error_kind=ERR_MAGIC, +) + # str.encode() method_op( name="encode", @@ -219,6 +448,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 +480,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/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index 0ea0243dc18b0..ab23f8c441f5b 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -6,7 +6,7 @@ from __future__ import annotations -from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC +from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( bit_rprimitive, c_pyssize_t_rprimitive, @@ -14,8 +14,9 @@ list_rprimitive, object_rprimitive, tuple_rprimitive, + void_rtype, ) -from mypyc.primitives.registry import custom_op, function_op, load_address_op, method_op +from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, method_op # Get the 'builtins.tuple' type object. load_address_op(name="builtins.tuple", type=object_rprimitive, src="PyTuple_Type") @@ -29,6 +30,15 @@ error_kind=ERR_MAGIC, ) +# This is unsafe because it assumes that the index is a non-negative integer +# that is in-bounds for the tuple. +tuple_get_item_unsafe_op = custom_op( + arg_types=[tuple_rprimitive, c_pyssize_t_rprimitive], + return_type=object_rprimitive, + c_function_name="CPySequenceTuple_GetItemUnsafe", + error_kind=ERR_NEVER, +) + # Construct a boxed tuple from items: (item1, item2, ...) new_tuple_op = custom_op( arg_types=[c_pyssize_t_rprimitive], @@ -45,13 +55,20 @@ error_kind=ERR_MAGIC, ) +load_empty_tuple_constant_op = custom_op( + arg_types=[], + return_type=tuple_rprimitive, + c_function_name="CPyTuple_LoadEmptyTupleConstant", + error_kind=ERR_NEVER, +) + # PyTuple_SET_ITEM does no error checking, # and should only be used to fill in brand new tuples. new_tuple_set_item_op = custom_op( - arg_types=[tuple_rprimitive, int_rprimitive, object_rprimitive], - return_type=bit_rprimitive, + arg_types=[tuple_rprimitive, c_pyssize_t_rprimitive, object_rprimitive], + return_type=void_rtype, c_function_name="CPySequenceTuple_SetItemUnsafe", - error_kind=ERR_FALSE, + error_kind=ERR_NEVER, steals=[False, False, True], ) @@ -66,7 +83,7 @@ ) # Construct tuple from an arbitrary (iterable) object. -function_op( +sequence_tuple_op = function_op( name="builtins.tuple", arg_types=[object_rprimitive], return_type=tuple_rprimitive, @@ -74,6 +91,42 @@ error_kind=ERR_MAGIC, ) +# translate isinstance(obj, tuple) +isinstance_tuple = function_op( + name="builtins.isinstance", + arg_types=[object_rprimitive], + return_type=bit_rprimitive, + c_function_name="PyTuple_Check", + error_kind=ERR_NEVER, +) + +# tuple + tuple +binary_op( + name="+", + arg_types=[tuple_rprimitive, tuple_rprimitive], + return_type=tuple_rprimitive, + c_function_name="PySequence_Concat", + error_kind=ERR_MAGIC, +) + +# tuple * int +binary_op( + name="*", + arg_types=[tuple_rprimitive, int_rprimitive], + return_type=tuple_rprimitive, + c_function_name="CPySequence_Multiply", + error_kind=ERR_MAGIC, +) + +# int * tuple +binary_op( + name="*", + arg_types=[int_rprimitive, tuple_rprimitive], + return_type=tuple_rprimitive, + c_function_name="CPySequence_RMultiply", + error_kind=ERR_MAGIC, +) + # tuple[begin:end] tuple_slice_op = custom_op( arg_types=[tuple_rprimitive, int_rprimitive, int_rprimitive], diff --git a/mypyc/primitives/weakref_ops.py b/mypyc/primitives/weakref_ops.py new file mode 100644 index 0000000000000..21379d3b2c820 --- /dev/null +++ b/mypyc/primitives/weakref_ops.py @@ -0,0 +1,40 @@ +from mypyc.ir.ops import ERR_MAGIC +from mypyc.ir.rtypes import object_rprimitive, pointer_rprimitive +from mypyc.primitives.registry import function_op + +# Weakref operations + +new_ref_op = function_op( + name="weakref.ReferenceType", + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name="PyWeakref_NewRef", + extra_int_constants=[(0, pointer_rprimitive)], + error_kind=ERR_MAGIC, +) + +new_ref__with_callback_op = function_op( + name="weakref.ReferenceType", + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name="PyWeakref_NewRef", + error_kind=ERR_MAGIC, +) + +new_proxy_op = function_op( + name="_weakref.proxy", + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name="PyWeakref_NewProxy", + extra_int_constants=[(0, pointer_rprimitive)], + error_kind=ERR_MAGIC, +) + +new_proxy_with_callback_op = function_op( + name="_weakref.proxy", + arg_types=[object_rprimitive, object_rprimitive], + # steals=[True, False], + return_type=object_rprimitive, + c_function_name="PyWeakref_NewProxy", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/py.typed b/mypyc/py.typed new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mypyc/test-data/alwaysdefined.test b/mypyc/test-data/alwaysdefined.test index e8c44d8fc548f..ecbc8c410d6dc 100644 --- a/mypyc/test-data/alwaysdefined.test +++ b/mypyc/test-data/alwaysdefined.test @@ -166,8 +166,7 @@ IfConditionalAndNonConditional1: [x] IfConditionalAndNonConditional2: [] [case testAlwaysDefinedExpressions] -from typing import Dict, List, Set, Optional, cast -from typing_extensions import Final +from typing import Dict, Final, List, Set, Optional, cast import other @@ -307,7 +306,7 @@ def f() -> int: [file other.py] # Not compiled -from typing_extensions import Final +from typing import Final Y: Final = 3 diff --git a/mypyc/test-data/annotate-basic.test b/mypyc/test-data/annotate-basic.test new file mode 100644 index 0000000000000..c9e1c4b64a32e --- /dev/null +++ b/mypyc/test-data/annotate-basic.test @@ -0,0 +1,477 @@ +[case testAnnotateNonNativeAttribute] +from typing import Any + +def f1(x): + return x.foo # A: Get non-native attribute "foo". + +def f2(x: Any) -> object: + return x.foo # A: Get non-native attribute "foo". + +def f3(x): + x.bar = 1 # A: Set non-native attribute "bar". + +class C: + foo: int + + def method(self) -> int: + return self.foo + +def good1(x: C) -> int: + return x.foo + +[case testAnnotateMethod] +class C: + def method(self, x): + return x + "y" # A: Generic "+" operation. + +[case testAnnotateGenericBinaryOperations] +def generic_add(x): + return x + 1 # A: Generic "+" operation. + +def generic_sub(x): + return x - 1 # A: Generic "-" operation. + +def generic_mul(x): + return x * 1 # A: Generic "*" operation. + +def generic_div(x): + return x / 1 # A: Generic "/" operation. + +def generic_floor_div(x): + return x // 1 # A: Generic "//" operation. + +def generic_unary_plus(x): + return +x # A: Generic unary "+" operation. + +def generic_unary_minus(x): + return -x # A: Generic unary "-" operation. + +def native_int_ops(x: int, y: int) -> int: + a = x + 1 - y + return x * a // y + +[case testAnnotateGenericBitwiseOperations] +def generic_and(x): + return x & 1 # A: Generic "&" operation. + +def generic_or(x): + return x | 1 # A: Generic "|" operation. + +def generic_xor(x): + return x ^ 1 # A: Generic "^" operation. + +def generic_left_shift(x): + return x << 1 # A: Generic "<<" operation. + +def generic_right_shift(x): + return x >> 1 # A: Generic ">>" operation. + +def generic_invert(x): + return ~x # A: Generic "~" operation. + +def native_int_ops(x: int, y: int) -> int: + a = (x & 1) << y + return (x | a) >> (y ^ 1) + +[case testAnnotateGenericComparisonOperations] +def generic_eq(x, y): + return x == y # A: Generic comparison operation. + +def generic_ne(x, y): + return x != y # A: Generic comparison operation. + +def generic_lt(x, y): + return x < y # A: Generic comparison operation. + +def generic_le(x, y): + return x <= y # A: Generic comparison operation. + +def generic_gt(x, y): + return x > y # A: Generic comparison operation. + +def generic_ge(x, y): + return x >= y # A: Generic comparison operation. + +def int_comparisons(x: int, y: int) -> int: + if x == y: + return 0 + if x < y: + return 1 + if x > y: + return 2 + return 3 + +[case testAnnotateTwoOperationsOnLine] +def f(x): + return x.foo + 1 # A: Get non-native attribute "foo". Generic "+" operation. + +[case testAnnotateNonNativeMethod] +from typing import Any + +def f1(x): + return x.foo() # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). + +def f2(x: Any) -> None: + x.foo(1) # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). + x.foo(a=1) # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). + t = (1, 'x') + x.foo(*t) # A: Get non-native attribute "foo". Generic call operation. + d = {"a": 1} + x.foo(*d) # A: Get non-native attribute "foo". Generic call operation. + +class C: + def foo(self) -> int: + return 0 + +def g(c: C) -> int: + return c.foo() + +[case testAnnotateGlobalVariableAccess] +from typing import Final +import nonnative + +x = 0 +y: Final = 0 + +def read() -> int: + return x # A: Access global "x" through namespace dictionary (hint: access is faster if you can make it Final). + +def assign(a: int) -> None: + global x + x = a # A: Access global "x" through namespace dictionary (hint: access is faster if you can make it Final). + +def read_final() -> int: + return y + +def read_nonnative() -> int: + return nonnative.z # A: Get non-native attribute "z". + +[file nonnative.py] +z = 2 + +[case testAnnotateNestedFunction] +def f1() -> None: + def g() -> None: # A: A nested function object is allocated each time statement is executed. A module-level function would be faster. + pass + + g() + +def f2() -> int: + l = lambda: 1 # A: A new object is allocated for lambda each time it is evaluated. A module-level function would be faster. + return l() + +[case testAnnotateGetSetItem] +from typing import List, Dict + +def f1(x, y): + return x[y] # A: Generic indexing operation. + +def f2(x, y, z): + x[y] = z # A: Generic indexed assignment. + +def list_get_item(x: List[int], y: int) -> int: + return x[y] + +def list_set_item(x: List[int], y: int) -> None: + x[y] = 5 + +def dict_get_item(d: Dict[str, str]) -> str: + return d['x'] + +def dict_set_item(d: Dict[str, str]) -> None: + d['x'] = 'y' + +[case testAnnotateStrMethods] +def startswith(x: str) -> bool: + return x.startswith('foo') + +def islower(x: str) -> bool: + return x.islower() # A: Call non-native method "islower" (it may be defined in a non-native class, or decorated). + +[case testAnnotateSpecificStdlibFeatures] +import functools +import itertools +from functools import partial +from itertools import chain, groupby, islice + +def f(x: int, y: int) -> None: pass + +def use_partial1() -> None: + p = partial(f, 1) # A: "functools.partial" is inefficient in compiled code. + p(2) + +def use_partial2() -> None: + p = functools.partial(f, 1) # A: "functools.partial" is inefficient in compiled code. + p(2) + +def use_chain1() -> None: + for x in chain([1, 3], [4, 5]): # A: "itertools.chain" is inefficient in compiled code (hint: replace with for loops). + pass + +def use_chain2() -> None: + for x in itertools.chain([1, 3], [4, 5]): # A: "itertools.chain" is inefficient in compiled code (hint: replace with for loops). + pass + +def use_groupby1() -> None: + for a, b in groupby([('A', 'B')]): # A: "itertools.groupby" is inefficient in compiled code. + pass + +def use_groupby2() -> None: + for a, b in itertools.groupby([('A', 'B')]): # A: "itertools.groupby" is inefficient in compiled code. + pass + +def use_islice() -> None: + for x in islice([1, 2, 3], 1, 2): # A: "itertools.islice" is inefficient in compiled code (hint: replace with for loop over index range). + pass + +[case testAnnotateGenericForLoop] +from typing import Iterable, Sequence, Iterator, List + +def f1(a): + for x in a: # A: For loop uses generic operations (iterable has type "Any"). + pass + +def f2(a: Iterable[str]) -> None: + for x in a: # A: For loop uses generic operations (iterable has the abstract type "typing.Iterable"). + pass + +def f3(a: Sequence[str]) -> None: + for x in a: # A: For loop uses generic operations (iterable has the abstract type "typing.Sequence"). + pass + +def f4(a: Iterator[str]) -> None: + for x in a: # A: For loop uses generic operations (iterable has the abstract type "typing.Iterator"). + pass + +def good1(a: List[str]) -> None: + for x in a: + pass + +class C: + def __iter__(self) -> Iterator[str]: + assert False + +def good2(a: List[str]) -> None: + for x in a: + pass + +[case testAnnotateGenericComprehensionOrGenerator] +from typing import List, Iterable + +def f1(a): + return [x for x in a] # A: Comprehension or generator uses generic operations (iterable has type "Any"). + +def f2(a: Iterable[int]): + return {x for x in a} # A: Comprehension or generator uses generic operations (iterable has the abstract type "typing.Iterable"). + +def f3(a): + return {x: 1 for x in a} # A: Comprehension uses generic operations (iterable has type "Any"). + +def f4(a): + return (x for x in a) # A: Comprehension or generator uses generic operations (iterable has type "Any"). + +def good1(a: List[int]) -> List[int]: + return [x + 1 for x in a] + +[case testAnnotateIsinstance] +from typing import Protocol, runtime_checkable, Union + +@runtime_checkable +class P(Protocol): + def foo(self) -> None: ... + +class C: pass + +class D(C): + def bar(self) -> None: pass + +def bad1(x: object) -> bool: + return isinstance(x, P) # A: Expensive isinstance() check against protocol "P". + +def bad2(x: object) -> bool: + return isinstance(x, (str, P)) # A: Expensive isinstance() check against protocol "P". + +def good1(x: C) -> bool: + if isinstance(x, D): + x.bar() + return isinstance(x, D) + +def good2(x: Union[int, str]) -> int: + if isinstance(x, int): + return x + 1 + else: + return int(x + "1") +[typing fixtures/typing-full.pyi] + +[case testAnnotateDeepcopy] +from typing import Any +import copy + +def f(x: Any) -> Any: + return copy.deepcopy(x) # A: "copy.deepcopy" tends to be slow. Make a shallow copy if possible. + +[case testAnnotateContextManager] +from typing import Iterator +from contextlib import contextmanager + +@contextmanager +def slow_ctx_manager() -> Iterator[None]: + yield + +class FastCtxManager: + def __enter__(self) -> None: pass + def __exit__(self, a, b, c) -> None: pass + +def f1(x) -> None: + with slow_ctx_manager(): # A: "slow_ctx_manager" uses @contextmanager, which is slow in compiled code. Use a native class with "__enter__" and "__exit__" methods instead. + x.foo # A: Get non-native attribute "foo". + +def f2(x) -> None: + with FastCtxManager(): + x.foo # A: Get non-native attribute "foo". + +[case testAnnotateAvoidNoiseAtTopLevel] +from typing import Final + +class C(object): + x = "s" + y: Final = 1 + +x = "s" +y: Final = 1 + +def f1() -> None: + x = object # A: Get non-native attribute "object". + +[case testAnnotateCreateNonNativeInstance] +from typing import NamedTuple +from dataclasses import dataclass + +from nonnative import C + +def f1() -> None: + c = C() # A: Creating an instance of non-native class "C" is slow. + c.foo() # A: Call non-native method "foo" (it may be defined in a non-native class, or decorated). + +class NT(NamedTuple): + x: int + y: str + +def f2() -> int: + o = NT(1, "x") # A: Creating an instance of non-native class "NT" is slow. + return o.x + +def f3() -> int: + o = NT(x=1, y="x") # A: Creating an instance of non-native class "NT" is slow. + a, b = o + return a + +@dataclass +class D: + x: int + +def f4() -> int: + o = D(1) # A: Class "D" is only partially native, and constructing an instance is slow. + return o.x + +class Nat: + x: int + +class Deriv(Nat): + def __init__(self, y: int) -> None: + self.y = y + +def good1() -> int: + n = Nat() + d = Deriv(y=1) + return n.x + d.x + d.y + +[file nonnative.py] +class C: + def foo(self) -> None: pass + +[case testAnnotateGetAttrAndSetAttrBuiltins] +def f1(x, s: str): + return getattr("x", s) # A: Dynamic attribute lookup. + +def f2(x, s: str): + setattr(x, s, None) # A: Dynamic attribute set. + +[case testAnnotateSpecialAssignments] +from typing import TypeVar, NamedTuple, List, TypedDict, NewType + +# Even though these are slow, we don't complain about them since there is generally +# no better way (and at module top level these are very unlikely to be bottlenecks) +A = List[int] +T = TypeVar("T", bound=List[int]) +NT = NamedTuple("NT", [("x", List[int])]) +TD = TypedDict("TD", {"x": List[int]}) +New = NewType("New", List[int]) +[typing fixtures/typing-full.pyi] + +[case testAnnotateCallDecoratedNativeFunctionOrMethod] +from typing import TypeVar, Callable, Any + +F = TypeVar("F", bound=Callable[..., Any]) + +def mydeco(f: F) -> F: + return f + +@mydeco +def d(x: int) -> int: + return x + +def f1() -> int: + return d(1) # A: Calling a decorated function ("d") is inefficient, even if it's native. + +class C: + @mydeco + def d(self) -> None: + pass + + +def f2() -> None: + c = C() + c.d() # A: Call non-native method "d" (it may be defined in a non-native class, or decorated). + +[case testAnnotateCallDifferentKindsOfMethods] +from abc import ABC, abstractmethod + +class C: + @staticmethod + def s() -> None: ... + + @classmethod + def c(cls) -> None: ... + + @property + def p(self) -> int: + return 0 + + @property + def p2(self) -> int: + return 0 + + @p2.setter + def p2(self, x: int) -> None: + pass + +def f1() -> int: + c = C() + c.s() + c.c() + c.p2 = 1 + return c.p + c.p2 + +class A(ABC): + @abstractmethod + def m(self) -> int: + raise NotImplementedError # A: Get non-native attribute "NotImplementedError". + +class D(A): + def m(self) -> int: + return 1 + +def f2() -> int: + d = D() + return d.m() diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test index 672e879fbe1ea..392ad3620790b 100644 --- a/mypyc/test-data/commandline.test +++ b/mypyc/test-data/commandline.test @@ -101,14 +101,71 @@ assert a.f(10) == 100 def f(x: int) -> int: return x*x -[case testErrorOutput] +[case testErrorOutput1] # cmd: test.py [file test.py] -from typing import List, Any, AsyncIterable -from typing_extensions import Final -from mypy_extensions import trait, mypyc_attr from functools import singledispatch +from mypy_extensions import trait +from typing import Any + +def decorator(x: Any) -> Any: + return x + +class NeverMetaclass(type): # E: Inheriting from most builtin types is unimplemented \ + # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \ + # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes + pass + +class Concrete1: + pass + +@trait +class Trait1: + pass + +class Concrete2: + pass + +@decorator +class NonExt(Concrete1): # E: Non-extension classes may not inherit from extension classes + pass + +class NopeMultipleInheritanceAndBadOrder3(Trait1, Concrete1, Concrete2): # E: Non-trait base must appear first in parent list + pass + +class NopeBadOrder(Trait1, Concrete2): # E: Non-trait base must appear first in parent list + pass + +class Foo: + pass + +@singledispatch +def a(arg) -> None: + pass + +@decorator # E: Calling decorator after registering function not supported +@a.register +def g(arg: int) -> None: + pass + +@a.register +@decorator +def h(arg: str) -> None: + pass + +@decorator +@decorator # E: Calling decorator after registering function not supported +@a.register +def i(arg: Foo) -> None: + pass + +[case testErrorOutput2] +# cmd: test.py + +[file test.py] +from typing import Final, List, Any, AsyncIterable +from mypy_extensions import trait, mypyc_attr def busted(b: bool) -> None: for i in range(1, 10, 0): # E: range() step can't be zero @@ -139,9 +196,6 @@ Foo.lol = 50 # E: Only class variables defined as ClassVar can be assigned to def decorator(x: Any) -> Any: return x -class NeverMetaclass(type): # E: Inheriting from most builtin types is unimplemented - pass - class Concrete1: pass @@ -160,11 +214,6 @@ class Concrete2: class Trait2(Concrete2): pass -@decorator -class NonExt(Concrete1): # E: Non-extension classes may not inherit from extension classes - pass - - class NopeMultipleInheritance(Concrete1, Concrete2): # E: Multiple inheritance is not supported (except for traits) pass @@ -174,13 +223,6 @@ class NopeMultipleInheritanceAndBadOrder(Concrete1, Trait1, Concrete2): # E: Mu class NopeMultipleInheritanceAndBadOrder2(Concrete1, Concrete2, Trait1): # E: Multiple inheritance is not supported (except for traits) pass -class NopeMultipleInheritanceAndBadOrder3(Trait1, Concrete1, Concrete2): # E: Non-trait base must appear first in parent list # E: Multiple inheritance is not supported (except for traits) - pass - -class NopeBadOrder(Trait1, Concrete2): # E: Non-trait base must appear first in parent list - pass - - @decorator class NonExt2: @property # E: Property setters not supported in non-extension classes @@ -200,9 +242,9 @@ wtvr = next(i for i in range(10) if i == 5) d1 = {1: 2} -# Make sure we can produce an error when we hit the awful None case +# Since PR 18180, the following pattern should pose no problems anymore: def f(l: List[object]) -> None: - x = None # E: Local variable "x" has inferred type None; add an annotation + x = None for i in l: if x is None: x = i @@ -218,28 +260,55 @@ class AllowInterp2(PureTrait): # E: Base class "test.PureTrait" does not allow async def async_generators() -> AsyncIterable[int]: yield 1 # E: async generators are unimplemented -@singledispatch -def a(arg) -> None: - pass - -@decorator # E: Calling decorator after registering function not supported -@a.register -def g(arg: int) -> None: - pass - -@a.register -@decorator -def h(arg: str) -> None: - pass - -@decorator -@decorator # E: Calling decorator after registering function not supported -@a.register -def i(arg: Foo) -> None: - pass - [case testOnlyWarningOutput] # cmd: test.py [file test.py] names = (str(v) for v in [1, 2, 3]) # W: Treating generator comprehension as list + +[case testSubPackage] +# cmd: pkg/sub/foo.py +from pkg.sub import foo + +[file pkg/__init__.py] + +[file pkg/sub/__init__.py] +print("importing...") +from . import foo +print("done") + +[file pkg/sub/foo.py] +print("imported foo") + +[out] +importing... +imported foo +done + +[case testImportFromInitPy] +# cmd: foo.py +import foo + +[file pkg2/__init__.py] + +[file pkg2/mod2.py] +class A: + class B: + pass + +[file pkg1/__init__.py] +from pkg2.mod2 import A + +[file foo.py] +import pkg1 +from typing import TypedDict + +class Eggs(TypedDict): + obj1: pkg1.A.B + +print(type(Eggs(obj1=pkg1.A.B())["obj1"]).__name__) +print(type(Eggs(obj1=pkg1.A.B())["obj1"]).__module__) + +[out] +B +pkg2.mod2 diff --git a/mypyc/test-data/driver/driver.py b/mypyc/test-data/driver/driver.py index c9d179224a30d..395be6e1630e4 100644 --- a/mypyc/test-data/driver/driver.py +++ b/mypyc/test-data/driver/driver.py @@ -9,14 +9,23 @@ import sys import native +import asyncio +import inspect + +evloop = asyncio.new_event_loop() failures = [] +tests_run = 0 for name in dir(native): if name.startswith('test_'): test_func = getattr(native, name) + tests_run += 1 try: - test_func() + if inspect.iscoroutinefunction(test_func): + evloop.run_until_complete(test_func) + else: + test_func() except Exception as e: failures.append((name, sys.exc_info())) @@ -46,3 +55,5 @@ def extract_line(tb): print(f'<< {failures[-1][0]} >>') sys.stdout.flush() raise failures[-1][1][1] + +assert tests_run > 0, 'Default test driver did not find any functions prefixed "test_" to run.' diff --git a/mypyc/test-data/exceptions-freq.test b/mypyc/test-data/exceptions-freq.test index a655eed44d90e..b0e4cd6d35f72 100644 --- a/mypyc/test-data/exceptions-freq.test +++ b/mypyc/test-data/exceptions-freq.test @@ -97,7 +97,7 @@ L2: hot blocks: [0, 1] [case testRareBranch_freq] -from typing_extensions import Final +from typing import Final x: Final = str() diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 1ec03dd9a6716..18983b2c92e94 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -163,9 +163,12 @@ def g(): r5 :: str r6 :: object r7 :: str - r8, r9 :: object - r10 :: bit - r11 :: None + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: bit + r13 :: None L0: L1: r0 = builtins :: module @@ -173,7 +176,7 @@ L1: r2 = CPyObject_GetAttr(r0, r1) if is_error(r2) goto L3 (error at g:3) else goto L2 L2: - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) dec_ref r2 if is_error(r3) goto L3 (error at g:3) else goto L10 L3: @@ -184,9 +187,11 @@ L3: r8 = CPyObject_GetAttr(r6, r7) if is_error(r8) goto L6 (error at g:5) else goto L4 L4: - r9 = PyObject_CallFunctionObjArgs(r8, r5, 0) + r9 = [r5] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 1, 0) dec_ref r8 - if is_error(r9) goto L6 (error at g:5) else goto L11 + if is_error(r11) goto L6 (error at g:5) else goto L11 L5: CPy_RestoreExcInfo(r4) dec_ref r4 @@ -194,20 +199,20 @@ L5: L6: CPy_RestoreExcInfo(r4) dec_ref r4 - r10 = CPy_KeepPropagating() - if not r10 goto L9 else goto L7 :: bool + r12 = CPy_KeepPropagating() + if not r12 goto L9 else goto L7 :: bool L7: unreachable L8: return 1 L9: - r11 = :: None - return r11 + r13 = :: None + return r13 L10: dec_ref r3 goto L8 L11: - dec_ref r9 + dec_ref r11 goto L5 [case testGenopsTryFinally] @@ -229,9 +234,12 @@ def a(): r10 :: str r11 :: object r12 :: str - r13, r14 :: object - r15 :: bit - r16 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object + r17 :: bit + r18 :: str L0: L1: r0 = builtins :: module @@ -239,7 +247,7 @@ L1: r2 = CPyObject_GetAttr(r0, r1) if is_error(r2) goto L5 (error at a:3) else goto L2 L2: - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) dec_ref r2 if is_error(r3) goto L5 (error at a:3) else goto L19 L3: @@ -262,9 +270,11 @@ L6: r13 = CPyObject_GetAttr(r11, r12) if is_error(r13) goto L20 (error at a:6) else goto L7 L7: - r14 = PyObject_CallFunctionObjArgs(r13, r10, 0) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) dec_ref r13 - if is_error(r14) goto L20 (error at a:6) else goto L21 + if is_error(r16) goto L20 (error at a:6) else goto L21 L8: if is_error(r7) goto L11 else goto L22 L9: @@ -282,15 +292,15 @@ L14: CPy_RestoreExcInfo(r7) xdec_ref r7 L15: - r15 = CPy_KeepPropagating() - if not r15 goto L18 else goto L16 :: bool + r17 = CPy_KeepPropagating() + if not r17 goto L18 else goto L16 :: bool L16: unreachable L17: unreachable L18: - r16 = :: str - return r16 + r18 = :: str + return r18 L19: dec_ref r3 goto L3 @@ -298,7 +308,7 @@ L20: xdec_ref r5 goto L13 L21: - dec_ref r14 + dec_ref r16 goto L8 L22: xdec_ref r5 @@ -446,8 +456,11 @@ def f(b): r6 :: str r7 :: object r8 :: bool - r9 :: object - r10 :: None + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: bool + r13 :: None L0: r0 = :: str v = r0 @@ -455,50 +468,59 @@ L0: inc_ref r1 u = r1 L1: - if b goto L10 else goto L11 :: bool + if b goto L13 else goto L14 :: bool L2: r2 = 'b' inc_ref r2 v = r2 r3 = v == u r4 = r3 ^ 1 - if r4 goto L11 else goto L1 :: bool + if r4 goto L14 else goto L1 :: bool L3: r5 = builtins :: module r6 = 'print' r7 = CPyObject_GetAttr(r5, r6) - if is_error(r7) goto L12 (error at f:7) else goto L4 + if is_error(r7) goto L15 (error at f:7) else goto L4 L4: - if is_error(v) goto L13 else goto L7 + if is_error(v) goto L16 else goto L7 L5: r8 = raise UnboundLocalError('local variable "v" referenced before assignment') - if not r8 goto L9 (error at f:7) else goto L6 :: bool + if not r8 goto L12 (error at f:-1) else goto L6 :: bool L6: unreachable L7: - r9 = PyObject_CallFunctionObjArgs(r7, v, 0) + r9 = [v] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r7, r10, 1, 0) dec_ref r7 - xdec_ref v - if is_error(r9) goto L9 (error at f:7) else goto L14 + if is_error(r11) goto L15 (error at f:7) else goto L17 L8: - return 1 + if is_error(v) goto L9 else goto L11 L9: - r10 = :: None - return r10 + r12 = raise UnboundLocalError('local variable "v" referenced before assignment') + if not r12 goto L12 (error at f:-1) else goto L10 :: bool L10: + unreachable +L11: + xdec_ref v + return 1 +L12: + r13 = :: None + return r13 +L13: xdec_ref v goto L2 -L11: +L14: dec_ref u goto L3 -L12: +L15: xdec_ref v - goto L9 -L13: + goto L12 +L16: dec_ref r7 goto L5 -L14: - dec_ref r9 +L17: + dec_ref r11 goto L8 [case testExceptionWithOverlappingErrorValue] diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index ac95ffe2c047a..5033100223a3d 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -3,7 +3,7 @@ import _typeshed from typing import ( - TypeVar, Generic, List, Iterator, Iterable, Dict, Optional, Tuple, Any, Set, + Self, TypeVar, Generic, List, Iterator, Iterable, Dict, Optional, Tuple, Any, Set, overload, Mapping, Union, Callable, Sequence, FrozenSet, Protocol ) @@ -39,9 +39,14 @@ def __pow__(self, other: T_contra, modulo: _M) -> T_co: ... ] class object: + __class__: type + def __new__(cls) -> Self: pass def __init__(self) -> None: pass def __eq__(self, x: object) -> bool: pass def __ne__(self, x: object) -> bool: pass + def __str__(self) -> str: pass + def __setattr__(self, k: str, v: object) -> None: pass + def __delattr__(self, k: str) -> None: pass class type: def __init__(self, o: object) -> None: ... @@ -81,6 +86,7 @@ def __lt__(self, n: int) -> bool: pass def __gt__(self, n: int) -> bool: pass def __le__(self, n: int) -> bool: pass def __ge__(self, n: int) -> bool: pass + def bit_length(self) -> int: pass class str: @overload @@ -102,15 +108,27 @@ def __getitem__(self, i: int) -> str: pass def __getitem__(self, i: slice) -> str: pass def __contains__(self, item: str) -> bool: pass def __iter__(self) -> Iterator[str]: ... - def split(self, sep: Optional[str] = None, max: Optional[int] = None) -> List[str]: pass - def strip (self, item: str) -> str: pass + def find(self, sub: str, start: Optional[int] = None, end: Optional[int] = None, /) -> int: ... + def rfind(self, sub: str, start: Optional[int] = None, end: Optional[int] = None, /) -> int: ... + def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass + def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]: pass + def splitlines(self, keepends: bool = False) -> List[str]: ... + def strip (self, item: Optional[str] = None) -> str: pass + def lstrip(self, item: Optional[str] = None) -> str: pass + def rstrip(self, item: Optional[str] = None) -> str: pass def join(self, x: Iterable[str]) -> str: pass def format(self, *args: Any, **kwargs: Any) -> str: ... 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 startswith(self, x: Union[str, Tuple[str, ...]], start: int=..., end: int=...) -> bool: ... + def endswith(self, x: Union[str, Tuple[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: ... + def partition(self, sep: str, /) -> Tuple[str, str, str]: ... + def rpartition(self, sep: str, /) -> Tuple[str, str, str]: ... + def removeprefix(self, prefix: str, /) -> str: ... + def removesuffix(self, suffix: str, /) -> str: ... + def islower(self) -> bool: ... + def count(self, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: pass class float: def __init__(self, x: object) -> None: pass @@ -159,7 +177,8 @@ def __getitem__(self, i: int) -> int: ... @overload def __getitem__(self, i: slice) -> bytes: ... def join(self, x: Iterable[object]) -> bytes: ... - def decode(self, x: str=..., y: str=...) -> str: ... + def decode(self, encoding: str=..., errors: str=...) -> str: ... + def __iter__(self) -> Iterator[int]: ... class bytearray: @overload @@ -197,6 +216,12 @@ def __getitem__(self, i: slice) -> Tuple[T_co, ...]: pass def __len__(self) -> int: pass def __iter__(self) -> Iterator[T_co]: ... def __contains__(self, item: object) -> int: ... + @overload + def __add__(self, value: Tuple[T_co, ...], /) -> Tuple[T_co, ...]: ... + @overload + def __add__(self, value: Tuple[_T, ...], /) -> Tuple[T_co | _T, ...]: ... + def __mul__(self, value: int, /) -> Tuple[T_co, ...]: ... + def __rmul__(self, value: int, /) -> Tuple[T_co, ...]: ... class function: pass @@ -210,19 +235,26 @@ def __setitem__(self, i: int, o: _T) -> None: pass def __delitem__(self, i: int) -> None: pass def __mul__(self, i: int) -> List[_T]: pass def __rmul__(self, i: int) -> List[_T]: pass + def __imul__(self, i: int) -> List[_T]: ... def __iter__(self) -> Iterator[_T]: pass def __len__(self) -> int: pass def __contains__(self, item: object) -> int: ... - def __add__(self, x: List[_T]) -> List[_T]: ... + @overload + def __add__(self, value: List[_T], /) -> List[_T]: ... + @overload + def __add__(self, value: List[_S], /) -> List[_S | _T]: ... + def __iadd__(self, value: Iterable[_T], /) -> List[_T]: ... # type: ignore[misc] def append(self, x: _T) -> None: pass def pop(self, i: int = -1) -> _T: pass - def count(self, _T) -> int: pass + def count(self, x: _T) -> int: pass def extend(self, l: Iterable[_T]) -> None: pass def insert(self, i: int, x: _T) -> None: pass def sort(self) -> None: pass def reverse(self) -> None: pass def remove(self, o: _T) -> None: pass def index(self, o: _T) -> int: pass + def clear(self) -> None: pass + def copy(self) -> List[_T]: pass class dict(Mapping[_K, _V]): @overload @@ -314,6 +346,7 @@ class RuntimeError(Exception): pass class UnicodeEncodeError(RuntimeError): pass class UnicodeDecodeError(RuntimeError): pass class NotImplementedError(RuntimeError): pass +class ReferenceError(Exception): pass class StopIteration(Exception): value: Any @@ -326,12 +359,17 @@ class GeneratorExit(BaseException): pass def any(i: Iterable[_T]) -> bool: pass def all(i: Iterable[_T]) -> bool: pass -def sum(i: Iterable[_T]) -> int: pass +@overload +def sum(i: Iterable[bool]) -> int: pass +@overload +def sum(i: Iterable[_T]) -> _T: pass +@overload +def sum(i: Iterable[_T], start: _T) -> _T: pass def reversed(object: Sequence[_T]) -> Iterator[_T]: ... def id(o: object) -> int: pass # This type is obviously wrong but the test stubs don't have Sized anymore def len(o: object) -> int: pass -def print(*object) -> None: pass +def print(*args: object) -> None: pass def isinstance(x: object, t: object) -> bool: pass def iter(i: Iterable[_T]) -> Iterator[_T]: pass @overload @@ -340,8 +378,10 @@ def next(i: Iterator[_T]) -> _T: pass def next(i: Iterator[_T], default: _T) -> _T: pass def hash(o: object) -> int: ... def globals() -> Dict[str, Any]: ... +def hasattr(obj: object, name: str) -> bool: ... def getattr(obj: object, name: str, default: Any = None) -> Any: ... def setattr(obj: object, name: str, value: Any) -> None: ... +def delattr(obj: object, name: str) -> None: ... def enumerate(x: Iterable[_T]) -> Iterator[Tuple[int, _T]]: ... @overload def zip(x: Iterable[_T], y: Iterable[_S]) -> Iterator[Tuple[_T, _S]]: ... @@ -359,6 +399,7 @@ def pow(base: __SupportsPow2[T_contra, T_co], exp: T_contra, mod: None = None) - def pow(base: __SupportsPow3NoneOnly[T_contra, T_co], exp: T_contra, mod: None = None) -> T_co: ... @overload def pow(base: __SupportsPow3[T_contra, _M, T_co], exp: T_contra, mod: _M) -> T_co: ... +def sorted(iterable: Iterable[_T]) -> list[_T]: ... def exit() -> None: ... def min(x: _T, y: _T) -> _T: ... def max(x: _T, y: _T) -> _T: ... diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index f210faf711091..b2700f731ddd6 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -44,8 +44,8 @@ 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__}" + except BaseException as e: + 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/fixtures/typing-full.pyi b/mypyc/test-data/fixtures/typing-full.pyi index 6b6aba6802b15..25aaaf700d051 100644 --- a/mypyc/test-data/fixtures/typing-full.pyi +++ b/mypyc/test-data/fixtures/typing-full.pyi @@ -11,13 +11,15 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass class _SpecialForm: - def __getitem__(self, index): ... + def __getitem__(self, index: Any) -> Any: ... +class TypeVar: + def __init__(self, name: str, *args: Any, bound: Any = None): ... + def __or__(self, other: Any) -> Any: ... cast = 0 overload = 0 Any = object() Optional = 0 -TypeVar = 0 Generic = 0 Protocol = 0 Tuple = 0 @@ -30,6 +32,7 @@ Final = 0 TypedDict = 0 NoReturn = 0 NewType = 0 +Self = 0 Callable: _SpecialForm Union: _SpecialForm Literal: _SpecialForm diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 3bfb1587fb3b1..55783a9a94980 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -37,7 +37,6 @@ def f(a: Any, n: int, c: C) -> None: c.n = a a = n n = a - a.a = n [out] def f(a, n, c): a :: object @@ -49,10 +48,6 @@ def f(a, n, c): r3 :: bool r4 :: object r5 :: int - r6 :: str - r7 :: object - r8 :: i32 - r9 :: bit L0: r0 = box(int, n) c.a = r0; r1 = is_error @@ -62,10 +57,6 @@ L0: a = r4 r5 = unbox(int, a) n = r5 - r6 = 'a' - r7 = box(int, n) - r8 = PyObject_SetAttr(a, r6, r7) - r9 = r8 >= 0 :: signed return 1 [case testCoerceAnyInOps] diff --git a/mypyc/test-data/irbuild-base64.test b/mypyc/test-data/irbuild-base64.test new file mode 100644 index 0000000000000..bc73b7e354316 --- /dev/null +++ b/mypyc/test-data/irbuild-base64.test @@ -0,0 +1,37 @@ +[case testBase64_experimental] +from librt.base64 import b64encode + +def enc(b: bytes) -> bytes: + return b64encode(b) +[out] +def enc(b): + b, r0 :: bytes +L0: + r0 = LibRTBase64_b64encode_internal(b) + return r0 + +[case testBase64ExperimentalDisabled] +from librt.base64 import b64encode + +def enc(b: bytes) -> bytes: + return b64encode(b) +[out] +def enc(b): + b :: bytes + r0 :: dict + r1 :: str + r2 :: object + r3 :: object[1] + r4 :: object_ptr + r5 :: object + r6 :: bytes +L0: + r0 = __main__.globals :: static + r1 = 'b64encode' + r2 = CPyDict_GetItem(r0, r1) + r3 = [b] + r4 = load_address r3 + r5 = PyObject_Vectorcall(r2, r4, 1, 0) + keep_alive b + r6 = cast(bytes, r5) + return r6 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 11df241b50748..612f3266fd793 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -484,16 +484,22 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object - r5 :: int + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int L0: r0 = testmodule :: module r1 = 'factorial' r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - return r5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + return r7 [case testImport_toplevel] import sys @@ -581,7 +587,7 @@ L2: r33 = single :: module r34 = 'hello' r35 = CPyObject_GetAttr(r33, r34) - r36 = PyObject_CallFunctionObjArgs(r35, 0) + r36 = PyObject_Vectorcall(r35, 0, 0, 0) return 1 [case testFromImport_toplevel] @@ -600,36 +606,42 @@ def f(x): x :: int r0 :: dict r1 :: str - r2, r3, r4 :: object - r5 :: int - r6 :: dict - r7 :: str - r8, r9 :: object - r10, r11 :: int - r12 :: dict - r13 :: str - r14, r15 :: object - r16, r17 :: int + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int + r8 :: dict + r9 :: str + r10, r11 :: object + r12, r13 :: int + r14 :: dict + r15 :: str + r16, r17 :: object + r18, r19 :: int L0: r0 = __main__.globals :: static r1 = 'g' r2 = CPyDict_GetItem(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - r6 = __main__.globals :: static - r7 = 'h' - r8 = CPyDict_GetItem(r6, r7) - r9 = PyObject_CallFunctionObjArgs(r8, 0) - r10 = unbox(int, r9) - r11 = CPyTagged_Add(r5, r10) - r12 = __main__.globals :: static - r13 = 'two' - r14 = CPyDict_GetItem(r12, r13) - r15 = PyObject_CallFunctionObjArgs(r14, 0) - r16 = unbox(int, r15) - r17 = CPyTagged_Add(r11, r16) - return r17 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + r8 = __main__.globals :: static + r9 = 'h' + r10 = CPyDict_GetItem(r8, r9) + r11 = PyObject_Vectorcall(r10, 0, 0, 0) + r12 = unbox(int, r11) + r13 = CPyTagged_Add(r7, r12) + r14 = __main__.globals :: static + r15 = 'two' + r16 = CPyDict_GetItem(r14, r15) + r17 = PyObject_Vectorcall(r16, 0, 0, 0) + r18 = unbox(int, r17) + r19 = CPyTagged_Add(r13, r18) + return r19 def __top_level__(): r0, r1 :: object r2 :: bit @@ -673,13 +685,19 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object L0: r0 = builtins :: module r1 = 'print' r2 = CPyObject_GetAttr(r0, r1) r3 = object 5 - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 return 1 [case testPrint] @@ -691,13 +709,19 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object L0: r0 = builtins :: module r1 = 'print' r2 = CPyObject_GetAttr(r0, r1) r3 = object 5 - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 return 1 [case testUnicodeLiteral] @@ -726,7 +750,7 @@ L0: r1 = b'1234' return r1 -[case testPyMethodCall1] +[case testPyMethodCall1_64bit] from typing import Any def f(x: Any) -> int: y: int = x.pop() @@ -735,20 +759,30 @@ def f(x: Any) -> int: def f(x): x :: object r0 :: str - r1 :: object - r2, y :: int - r3 :: str - r4 :: object - r5 :: int + r1 :: object[1] + r2 :: object_ptr + r3 :: object + r4, y :: int + r5 :: str + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9 :: int L0: r0 = 'pop' - r1 = CPyObject_CallMethodObjArgs(x, r0, 0) - r2 = unbox(int, r1) - y = r2 - r3 = 'pop' - r4 = CPyObject_CallMethodObjArgs(x, r3, 0) - r5 = unbox(int, r4) - return r5 + r1 = [x] + r2 = load_address r1 + r3 = PyObject_VectorcallMethod(r0, r2, 9223372036854775809, 0) + keep_alive x + r4 = unbox(int, r3) + y = r4 + r5 = 'pop' + r6 = [x] + r7 = load_address r6 + r8 = PyObject_VectorcallMethod(r5, r7, 9223372036854775809, 0) + keep_alive x + r9 = unbox(int, r8) + return r9 [case testObjectType] def g(y: object) -> None: @@ -1105,16 +1139,22 @@ def call_python_function(x): x :: int r0 :: dict r1 :: str - r2, r3, r4 :: object - r5 :: int + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int L0: r0 = __main__.globals :: static r1 = 'f' r2 = CPyDict_GetItem(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - return r5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + return r7 def return_float(): L0: return 5.0 @@ -1133,11 +1173,11 @@ def call_callable_type(): L0: r0 = return_callable_type() f = r0 - r1 = PyObject_CallFunctionObjArgs(f, 0) + r1 = PyObject_Vectorcall(f, 0, 0, 0) r2 = unbox(float, r1) return r2 -[case testCallableTypesWithKeywordArgs] +[case testCallableTypesWithKeywordArgs_64bit] from typing import List def call_python_function_with_keyword_arg(x: str) -> int: @@ -1151,58 +1191,51 @@ def call_python_method_with_keyword_args(xs: List[int], first: int, second: int) [out] def call_python_function_with_keyword_arg(x): x :: str - r0 :: object - r1 :: str - r2 :: tuple - r3 :: object - r4 :: dict - r5 :: object + r0, r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4, r5 :: object r6 :: int L0: r0 = load_address PyLong_Type - r1 = 'base' - r2 = PyTuple_Pack(1, x) - r3 = object 2 - r4 = CPyDict_Build(1, r1, r3) - r5 = PyObject_Call(r0, r2, r4) + r1 = object 2 + r2 = [x, r1] + r3 = load_address r2 + r4 = ('base',) + r5 = PyObject_Vectorcall(r0, r3, 1, r4) + keep_alive x, r1 r6 = unbox(int, r5) return r6 def call_python_method_with_keyword_args(xs, first, second): xs :: list first, second :: int r0 :: str - r1 :: object - r2 :: str - r3 :: object - r4 :: tuple - r5 :: object - r6 :: dict - r7 :: object - r8 :: str - r9 :: object - r10, r11 :: str - r12 :: tuple - r13, r14 :: object - r15 :: dict - r16 :: object + r1, r2 :: object + r3 :: object[3] + r4 :: object_ptr + r5, r6 :: object + r7 :: str + r8, r9 :: object + r10 :: object[3] + r11 :: object_ptr + r12, r13 :: object L0: r0 = 'insert' - r1 = CPyObject_GetAttr(xs, r0) - r2 = 'x' - r3 = object 0 - r4 = PyTuple_Pack(1, r3) - r5 = box(int, first) - r6 = CPyDict_Build(1, r2, r5) - r7 = PyObject_Call(r1, r4, r6) - r8 = 'insert' - r9 = CPyObject_GetAttr(xs, r8) - r10 = 'x' - r11 = 'i' - r12 = PyTuple_Pack(0) - r13 = box(int, second) - r14 = object 1 - r15 = CPyDict_Build(2, r10, r13, r11, r14) - r16 = PyObject_Call(r9, r12, r15) + r1 = object 0 + r2 = box(int, first) + r3 = [xs, r1, r2] + r4 = load_address r3 + r5 = ('x',) + r6 = PyObject_VectorcallMethod(r0, r4, 9223372036854775810, r5) + keep_alive xs, r1, r2 + r7 = 'insert' + r8 = box(int, second) + r9 = object 1 + r10 = [xs, r8, r9] + r11 = load_address r10 + r12 = ('x', 'i') + r13 = PyObject_VectorcallMethod(r7, r11, 9223372036854775809, r12) + keep_alive xs, r8, r9 return xs [case testObjectAsBoolean] @@ -1368,7 +1401,7 @@ L0: r0 = builtins :: module r1 = 'Exception' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) CPy_Raise(r3) unreachable def bar(): @@ -1396,7 +1429,10 @@ def f(): r3 :: int r4 :: object r5 :: str - r6, r7, r8 :: object + r6, r7 :: object + r8 :: object[1] + r9 :: object_ptr + r10 :: object L0: r0 = __main__.globals :: static r1 = 'x' @@ -1406,7 +1442,10 @@ L0: r5 = 'print' r6 = CPyObject_GetAttr(r4, r5) r7 = box(int, r3) - r8 = PyObject_CallFunctionObjArgs(r6, r7, 0) + r8 = [r7] + r9 = load_address r8 + r10 = PyObject_Vectorcall(r6, r9, 1, 0) + keep_alive r7 return 1 def __top_level__(): r0, r1 :: object @@ -1424,7 +1463,10 @@ def __top_level__(): r13 :: int r14 :: object r15 :: str - r16, r17, r18 :: object + r16, r17 :: object + r18 :: object[1] + r19 :: object_ptr + r20 :: object L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -1448,7 +1490,10 @@ L2: r15 = 'print' r16 = CPyObject_GetAttr(r14, r15) r17 = box(int, r13) - r18 = PyObject_CallFunctionObjArgs(r16, r17, 0) + r18 = [r17] + r19 = load_address r18 + r20 = PyObject_Vectorcall(r16, r19, 1, 0) + keep_alive r17 return 1 [case testCallOverloaded] @@ -1465,16 +1510,22 @@ def f(x: str) -> int: ... def f(): r0 :: object r1 :: str - r2, r3, r4 :: object - r5 :: str + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: str L0: r0 = m :: module r1 = 'f' r2 = CPyObject_GetAttr(r0, r1) r3 = object 1 - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = cast(str, r4) - return r5 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = cast(str, r6) + return r7 [case testCallOverloadedNative] from typing import overload, Union @@ -1530,24 +1581,18 @@ def main() -> None: [out] def foo(x): x :: union[int, str] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4 :: __main__.B - r5 :: __main__.A + r0 :: bit + r1 :: __main__.B + r2 :: __main__.A L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyLong_Check(x) + if r0 goto L1 else goto L2 :: bool L1: - r4 = B() - return r4 + r1 = B() + return r1 L2: - r5 = A() - return r5 + r2 = A() + return r2 def main(): r0 :: object r1 :: __main__.A @@ -1629,26 +1674,17 @@ def g(): r0 :: tuple[int, int, int] r1 :: dict r2 :: str - r3 :: object - r4 :: list - r5, r6 :: object - r7 :: tuple - r8 :: dict - r9 :: object - r10 :: tuple[int, int, int] + r3, r4, r5 :: object + r6 :: tuple[int, int, int] L0: r0 = (2, 4, 6) r1 = __main__.globals :: static r2 = 'f' r3 = CPyDict_GetItem(r1, r2) - r4 = PyList_New(0) - r5 = box(tuple[int, int, int], r0) - r6 = CPyList_Extend(r4, r5) - r7 = PyList_AsTuple(r4) - r8 = PyDict_New() - r9 = PyObject_Call(r3, r7, r8) - r10 = unbox(tuple[int, int, int], r9) - return r10 + r4 = box(tuple[int, int, int], r0) + r5 = PyObject_CallObject(r3, r4) + r6 = unbox(tuple[int, int, int], r5) + return r6 def h(): r0 :: tuple[int, int] r1 :: dict @@ -1659,9 +1695,8 @@ def h(): r6 :: ptr r7, r8 :: object r9 :: tuple - r10 :: dict - r11 :: object - r12 :: tuple[int, int, int] + r10 :: object + r11 :: tuple[int, int, int] L0: r0 = (4, 6) r1 = __main__.globals :: static @@ -1675,10 +1710,9 @@ L0: r7 = box(tuple[int, int], r0) r8 = CPyList_Extend(r4, r7) r9 = PyList_AsTuple(r4) - r10 = PyDict_New() - r11 = PyObject_Call(r3, r9, r10) - r12 = unbox(tuple[int, int, int], r11) - return r12 + r10 = PyObject_CallObject(r3, r9) + r11 = unbox(tuple[int, int, int], r10) + return r11 [case testStar2Args] from typing import Tuple @@ -1701,12 +1735,10 @@ def g(): r6, r7 :: dict r8 :: str r9 :: object - r10 :: dict - r11 :: i32 - r12 :: bit - r13 :: tuple - r14 :: object - r15 :: tuple[int, int, int] + r10 :: tuple + r11 :: dict + r12 :: object + r13 :: tuple[int, int, int] L0: r0 = 'a' r1 = 'b' @@ -1718,13 +1750,11 @@ L0: r7 = __main__.globals :: static r8 = 'f' r9 = CPyDict_GetItem(r7, r8) - r10 = PyDict_New() - r11 = CPyDict_UpdateInDisplay(r10, r6) - r12 = r11 >= 0 :: signed - r13 = PyTuple_Pack(0) - r14 = PyObject_Call(r9, r13, r10) - r15 = unbox(tuple[int, int, int], r14) - return r15 + r10 = CPyTuple_LoadEmptyTupleConstant() + r11 = PyDict_Copy(r6) + r12 = PyObject_Call(r9, r10, r11) + r13 = unbox(tuple[int, int, int], r12) + return r13 def h(): r0, r1 :: str r2, r3 :: object @@ -1844,18 +1874,16 @@ def f(): r0, r1 :: list r2, r3, r4 :: object r5 :: ptr - r6 :: short_int - r7 :: native_int - r8 :: short_int - r9 :: bit - r10 :: object - r11, x :: int - r12, r13 :: bit - r14 :: int - r15 :: object - r16 :: i32 - r17 :: bit - r18 :: short_int + r6, r7 :: native_int + r8 :: bit + r9 :: object + r10, x :: int + r11, r12 :: bit + r13 :: int + r14 :: object + r15 :: i32 + r16 :: bit + r17 :: native_int L0: r0 = PyList_New(0) r1 = PyList_New(3) @@ -1870,30 +1898,29 @@ L0: r6 = 0 L1: r7 = var_object_size r1 - r8 = r7 << 1 - r9 = int_lt r6, r8 - if r9 goto L2 else goto L8 :: bool + r8 = r6 < r7 :: signed + if r8 goto L2 else goto L8 :: bool L2: - r10 = CPyList_GetItemUnsafe(r1, r6) - r11 = unbox(int, r10) - x = r11 - r12 = int_ne x, 4 - if r12 goto L4 else goto L3 :: bool + r9 = list_get_item_unsafe r1, r6 + r10 = unbox(int, r9) + x = r10 + r11 = int_ne x, 4 + if r11 goto L4 else goto L3 :: bool L3: goto L7 L4: - r13 = int_ne x, 6 - if r13 goto L6 else goto L5 :: bool + r12 = int_ne x, 6 + if r12 goto L6 else goto L5 :: bool L5: goto L7 L6: - r14 = CPyTagged_Multiply(x, x) - r15 = box(int, r14) - r16 = PyList_Append(r0, r15) - r17 = r16 >= 0 :: signed + r13 = CPyTagged_Multiply(x, x) + r14 = box(int, r13) + r15 = PyList_Append(r0, r14) + r16 = r15 >= 0 :: signed L7: - r18 = r6 + 2 - r6 = r18 + r17 = r6 + 1 + r6 = r17 goto L1 L8: return r0 @@ -1908,18 +1935,16 @@ def f(): r1 :: list r2, r3, r4 :: object r5 :: ptr - r6 :: short_int - r7 :: native_int - r8 :: short_int - r9 :: bit - r10 :: object - r11, x :: int - r12, r13 :: bit - r14 :: int - r15, r16 :: object - r17 :: i32 - r18 :: bit - r19 :: short_int + r6, r7 :: native_int + r8 :: bit + r9 :: object + r10, x :: int + r11, r12 :: bit + r13 :: int + r14, r15 :: object + r16 :: i32 + r17 :: bit + r18 :: native_int L0: r0 = PyDict_New() r1 = PyList_New(3) @@ -1934,31 +1959,30 @@ L0: r6 = 0 L1: r7 = var_object_size r1 - r8 = r7 << 1 - r9 = int_lt r6, r8 - if r9 goto L2 else goto L8 :: bool + r8 = r6 < r7 :: signed + if r8 goto L2 else goto L8 :: bool L2: - r10 = CPyList_GetItemUnsafe(r1, r6) - r11 = unbox(int, r10) - x = r11 - r12 = int_ne x, 4 - if r12 goto L4 else goto L3 :: bool + r9 = list_get_item_unsafe r1, r6 + r10 = unbox(int, r9) + x = r10 + r11 = int_ne x, 4 + if r11 goto L4 else goto L3 :: bool L3: goto L7 L4: - r13 = int_ne x, 6 - if r13 goto L6 else goto L5 :: bool + r12 = int_ne x, 6 + if r12 goto L6 else goto L5 :: bool L5: goto L7 L6: - r14 = CPyTagged_Multiply(x, x) - r15 = box(int, x) - r16 = box(int, r14) - r17 = CPyDict_SetItem(r0, r15, r16) - r18 = r17 >= 0 :: signed + r13 = CPyTagged_Multiply(x, x) + r14 = box(int, x) + r15 = box(int, r13) + r16 = PyDict_SetItem(r0, r14, r15) + r17 = r16 >= 0 :: signed L7: - r19 = r6 + 2 - r6 = r19 + r18 = r6 + 1 + r6 = r18 goto L1 L8: return r0 @@ -1972,74 +1996,66 @@ def f(l: List[Tuple[int, int, int]]) -> List[int]: [out] def f(l): l :: list - r0 :: short_int - r1 :: native_int - r2 :: short_int - r3 :: bit - r4 :: object - r5 :: tuple[int, int, int] - r6, x, r7, y, r8, z :: int - r9 :: short_int - r10 :: native_int - r11 :: list - r12 :: short_int - r13 :: native_int - r14 :: short_int - r15 :: bit - r16 :: object - r17 :: tuple[int, int, int] - r18, x_2, r19, y_2, r20, z_2, r21, r22 :: int - r23 :: object - r24 :: bit - r25 :: short_int + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4 :: tuple[int, int, int] + r5, x, r6, y, r7, z :: int + r8, r9 :: native_int + r10 :: list + r11, r12 :: native_int + r13 :: bit + r14 :: object + r15 :: tuple[int, int, int] + r16, x_2, r17, y_2, r18, z_2, r19, r20 :: int + r21 :: object + r22 :: native_int L0: r0 = 0 L1: r1 = var_object_size l - r2 = r1 << 1 - r3 = int_lt r0, r2 - if r3 goto L2 else goto L4 :: bool + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(l, r0) - r5 = unbox(tuple[int, int, int], r4) - r6 = r5[0] - x = r6 - r7 = r5[1] - y = r7 - r8 = r5[2] - z = r8 + r3 = list_get_item_unsafe l, r0 + r4 = unbox(tuple[int, int, int], r3) + r5 = r4[0] + x = r5 + r6 = r4[1] + y = r6 + r7 = r4[2] + z = r7 L3: - r9 = r0 + 2 - r0 = r9 + r8 = r0 + 1 + r0 = r8 goto L1 L4: - r10 = var_object_size l - r11 = PyList_New(r10) - r12 = 0 + r9 = var_object_size l + r10 = PyList_New(r9) + r11 = 0 L5: - r13 = var_object_size l - r14 = r13 << 1 - r15 = int_lt r12, r14 - if r15 goto L6 else goto L8 :: bool + r12 = var_object_size l + r13 = r11 < r12 :: signed + if r13 goto L6 else goto L8 :: bool L6: - r16 = CPyList_GetItemUnsafe(l, r12) - r17 = unbox(tuple[int, int, int], r16) - r18 = r17[0] - x_2 = r18 - r19 = r17[1] - y_2 = r19 - r20 = r17[2] - z_2 = r20 - r21 = CPyTagged_Add(x_2, y_2) - r22 = CPyTagged_Add(r21, z_2) - r23 = box(int, r22) - r24 = CPyList_SetItemUnsafe(r11, r12, r23) + r14 = list_get_item_unsafe l, r11 + r15 = unbox(tuple[int, int, int], r14) + r16 = r15[0] + x_2 = r16 + r17 = r15[1] + y_2 = r17 + r18 = r15[2] + z_2 = r18 + r19 = CPyTagged_Add(x_2, y_2) + r20 = CPyTagged_Add(r19, z_2) + r21 = box(int, r20) + CPyList_SetItemUnsafe(r10, r11, r21) L7: - r25 = r12 + 2 - r12 = r25 + r22 = r11 + 1 + r11 = r22 goto L5 L8: - return r11 + return r10 [case testProperty] class PropertyHolder: @@ -2147,45 +2163,54 @@ def __top_level__(): r19 :: object r20 :: dict r21 :: str - r22, r23 :: object - r24 :: dict - r25 :: str - r26 :: i32 - r27 :: bit - r28 :: str - r29 :: dict + r22 :: object + r23 :: object[2] + r24 :: object_ptr + r25 :: object + r26 :: dict + r27 :: str + r28 :: i32 + r29 :: bit r30 :: str - r31, r32, r33 :: object - r34 :: tuple - r35 :: dict - r36 :: str - r37 :: i32 - r38 :: bit + r31 :: dict + r32 :: str + r33, r34 :: object + r35 :: object[2] + r36 :: object_ptr + r37 :: object + r38 :: tuple r39 :: dict r40 :: str - r41, r42, r43 :: object - r44 :: dict - r45 :: str - r46 :: i32 - r47 :: bit - r48 :: str - r49 :: dict - r50 :: str - r51 :: object - r52 :: dict - r53 :: str - r54, r55 :: object + r41 :: i32 + r42 :: bit + r43 :: dict + r44 :: str + r45, r46, r47 :: object + r48 :: dict + r49 :: str + r50 :: i32 + r51 :: bit + r52 :: str + r53 :: dict + r54 :: str + r55 :: object r56 :: dict r57 :: str - r58 :: i32 - r59 :: bit - r60 :: list - r61, r62, r63 :: object - r64 :: ptr - r65 :: dict - r66 :: str - r67 :: i32 - r68 :: bit + r58 :: object + r59 :: object[2] + r60 :: object_ptr + r61 :: object + r62 :: dict + r63 :: str + r64 :: i32 + r65 :: bit + r66 :: list + r67, r68, r69 :: object + r70 :: ptr + r71 :: dict + r72 :: str + r73 :: i32 + r74 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -2215,56 +2240,65 @@ L2: r20 = __main__.globals :: static r21 = 'NamedTuple' r22 = CPyDict_GetItem(r20, r21) - r23 = PyObject_CallFunctionObjArgs(r22, r9, r19, 0) - r24 = __main__.globals :: static - r25 = 'Lol' - r26 = CPyDict_SetItem(r24, r25, r23) - r27 = r26 >= 0 :: signed - r28 = '' - r29 = __main__.globals :: static - r30 = 'Lol' - r31 = CPyDict_GetItem(r29, r30) - r32 = object 1 - r33 = PyObject_CallFunctionObjArgs(r31, r32, r28, 0) - r34 = cast(tuple, r33) - r35 = __main__.globals :: static - r36 = 'x' - r37 = CPyDict_SetItem(r35, r36, r34) - r38 = r37 >= 0 :: signed + r23 = [r9, r19] + r24 = load_address r23 + r25 = PyObject_Vectorcall(r22, r24, 2, 0) + keep_alive r9, r19 + r26 = __main__.globals :: static + r27 = 'Lol' + r28 = CPyDict_SetItem(r26, r27, r25) + r29 = r28 >= 0 :: signed + r30 = '' + r31 = __main__.globals :: static + r32 = 'Lol' + r33 = CPyDict_GetItem(r31, r32) + r34 = object 1 + r35 = [r34, r30] + r36 = load_address r35 + r37 = PyObject_Vectorcall(r33, r36, 2, 0) + keep_alive r34, r30 + r38 = cast(tuple, r37) r39 = __main__.globals :: static - r40 = 'List' - r41 = CPyDict_GetItem(r39, r40) - r42 = load_address PyLong_Type - r43 = PyObject_GetItem(r41, r42) - r44 = __main__.globals :: static - r45 = 'Foo' - r46 = CPyDict_SetItem(r44, r45, r43) - r47 = r46 >= 0 :: signed - r48 = 'Bar' - r49 = __main__.globals :: static - r50 = 'Foo' - r51 = CPyDict_GetItem(r49, r50) - r52 = __main__.globals :: static - r53 = 'NewType' - r54 = CPyDict_GetItem(r52, r53) - r55 = PyObject_CallFunctionObjArgs(r54, r48, r51, 0) + r40 = 'x' + r41 = CPyDict_SetItem(r39, r40, r38) + r42 = r41 >= 0 :: signed + r43 = __main__.globals :: static + r44 = 'List' + r45 = CPyDict_GetItem(r43, r44) + r46 = load_address PyLong_Type + r47 = PyObject_GetItem(r45, r46) + r48 = __main__.globals :: static + r49 = 'Foo' + r50 = CPyDict_SetItem(r48, r49, r47) + r51 = r50 >= 0 :: signed + r52 = 'Bar' + r53 = __main__.globals :: static + r54 = 'Foo' + r55 = CPyDict_GetItem(r53, r54) r56 = __main__.globals :: static - r57 = 'Bar' - r58 = CPyDict_SetItem(r56, r57, r55) - r59 = r58 >= 0 :: signed - r60 = PyList_New(3) - r61 = object 1 - r62 = object 2 - r63 = object 3 - r64 = list_items r60 - buf_init_item r64, 0, r61 - buf_init_item r64, 1, r62 - buf_init_item r64, 2, r63 - keep_alive r60 - r65 = __main__.globals :: static - r66 = 'y' - r67 = CPyDict_SetItem(r65, r66, r60) - r68 = r67 >= 0 :: signed + r57 = 'NewType' + r58 = CPyDict_GetItem(r56, r57) + r59 = [r52, r55] + r60 = load_address r59 + r61 = PyObject_Vectorcall(r58, r60, 2, 0) + keep_alive r52, r55 + r62 = __main__.globals :: static + r63 = 'Bar' + r64 = CPyDict_SetItem(r62, r63, r61) + r65 = r64 >= 0 :: signed + r66 = PyList_New(3) + r67 = object 1 + r68 = object 2 + r69 = object 3 + r70 = list_items r66 + buf_init_item r70, 0, r67 + buf_init_item r70, 1, r68 + buf_init_item r70, 2, r69 + keep_alive r66 + r71 = __main__.globals :: static + r72 = 'y' + r73 = CPyDict_SetItem(r71, r72, r66) + r74 = r73 >= 0 :: signed return 1 [case testChainedConditional] @@ -2313,22 +2347,42 @@ def A.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.A rhs, r0, r1 :: object r2 :: bit - r3 :: i32 - r4 :: bit - r5 :: bool + r3 :: object + r4, r5 :: bit r6 :: object + r7 :: bit + r8 :: i32 + r9 :: bit + r10 :: bool + r11 :: object L0: r0 = __mypyc_self__.__eq__(rhs) r1 = load_address _Py_NotImplementedStruct r2 = r0 == r1 - if r2 goto L2 else goto L1 :: bool + if r2 goto L7 else goto L1 :: bool L1: - r3 = PyObject_Not(r0) - r4 = r3 >= 0 :: signed - r5 = truncate r3: i32 to builtins.bool - r6 = box(bool, r5) - return r6 + r3 = load_global Py_True :: static + r4 = r0 == r3 + if r4 goto L2 else goto L3 :: bool L2: + r5 = 0 + goto L6 +L3: + r6 = load_global Py_False :: static + r7 = r0 == r6 + if r7 goto L4 else goto L5 :: bool +L4: + r5 = 1 + goto L6 +L5: + r8 = PyObject_Not(r0) + r9 = r8 >= 0 :: signed + r10 = truncate r8: i32 to builtins.bool + r5 = r10 +L6: + r11 = box(bit, r5) + return r11 +L7: return r1 [case testDecorators_toplevel] @@ -2378,25 +2432,37 @@ def g_a_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5, r6, r7 :: object - r8 :: str - r9 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7, r8, r9 :: object r10 :: str - r11, r12 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = 'Entering' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) - r6 = r0.f - r7 = PyObject_CallFunctionObjArgs(r6, 0) - r8 = 'Exited' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 + r8 = r0.f + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = 'Exited' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 return 1 def a(f): f :: object @@ -2431,25 +2497,37 @@ def g_b_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5, r6, r7 :: object - r8 :: str - r9 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7, r8, r9 :: object r10 :: str - r11, r12 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = '---' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) - r6 = r0.f - r7 = PyObject_CallFunctionObjArgs(r6, 0) - r8 = '---' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 + r8 = r0.f + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = '---' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 return 1 def b(f): f :: object @@ -2484,14 +2562,20 @@ def d_c_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = 'd' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 return 1 def c(): r0 :: __main__.c_env @@ -2499,18 +2583,27 @@ def c(): r2 :: bool r3 :: dict r4 :: str - r5, r6 :: object - r7 :: dict - r8 :: str - r9, r10, d :: object - r11 :: dict - r12 :: str - r13 :: i32 - r14 :: bit - r15 :: str - r16 :: object - r17 :: str - r18, r19, r20 :: object + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9 :: dict + r10 :: str + r11 :: object + r12 :: object[1] + r13 :: object_ptr + r14, d :: object + r15 :: dict + r16 :: str + r17 :: i32 + r18 :: bit + r19 :: str + r20 :: object + r21 :: str + r22 :: object + r23 :: object[1] + r24 :: object_ptr + r25, r26 :: object L0: r0 = c_env() r1 = d_c_obj() @@ -2518,22 +2611,31 @@ L0: r3 = __main__.globals :: static r4 = 'b' r5 = CPyDict_GetItem(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r1, 0) - r7 = __main__.globals :: static - r8 = 'a' - r9 = CPyDict_GetItem(r7, r8) - r10 = PyObject_CallFunctionObjArgs(r9, r6, 0) - d = r10 - r11 = __main__.globals :: static - r12 = 'd' - r13 = CPyDict_SetItem(r11, r12, r10) - r14 = r13 >= 0 :: signed - r15 = 'c' - r16 = builtins :: module - r17 = 'print' - r18 = CPyObject_GetAttr(r16, r17) - r19 = PyObject_CallFunctionObjArgs(r18, r15, 0) - r20 = PyObject_CallFunctionObjArgs(d, 0) + r6 = [r1] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r1 + r9 = __main__.globals :: static + r10 = 'a' + r11 = CPyDict_GetItem(r9, r10) + r12 = [r8] + r13 = load_address r12 + r14 = PyObject_Vectorcall(r11, r13, 1, 0) + keep_alive r8 + d = r14 + r15 = __main__.globals :: static + r16 = 'd' + r17 = PyDict_SetItem(r15, r16, r14) + r18 = r17 >= 0 :: signed + r19 = 'c' + r20 = builtins :: module + r21 = 'print' + r22 = CPyObject_GetAttr(r20, r21) + r23 = [r19] + r24 = load_address r23 + r25 = PyObject_Vectorcall(r22, r24, 1, 0) + keep_alive r19 + r26 = PyObject_Vectorcall(d, 0, 0, 0) return 1 def __top_level__(): r0, r1 :: object @@ -2548,14 +2650,20 @@ def __top_level__(): r11 :: object r12 :: dict r13 :: str - r14, r15 :: object - r16 :: dict - r17 :: str - r18, r19 :: object - r20 :: dict - r21 :: str - r22 :: i32 - r23 :: bit + r14 :: object + r15 :: object[1] + r16 :: object_ptr + r17 :: object + r18 :: dict + r19 :: str + r20 :: object + r21 :: object[1] + r22 :: object_ptr + r23 :: object + r24 :: dict + r25 :: str + r26 :: i32 + r27 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -2577,15 +2685,21 @@ L2: r12 = __main__.globals :: static r13 = 'b' r14 = CPyDict_GetItem(r12, r13) - r15 = PyObject_CallFunctionObjArgs(r14, r11, 0) - r16 = __main__.globals :: static - r17 = 'a' - r18 = CPyDict_GetItem(r16, r17) - r19 = PyObject_CallFunctionObjArgs(r18, r15, 0) - r20 = __main__.globals :: static - r21 = 'c' - r22 = CPyDict_SetItem(r20, r21, r19) - r23 = r22 >= 0 :: signed + r15 = [r11] + r16 = load_address r15 + r17 = PyObject_Vectorcall(r14, r16, 1, 0) + keep_alive r11 + r18 = __main__.globals :: static + r19 = 'a' + r20 = CPyDict_GetItem(r18, r19) + r21 = [r17] + r22 = load_address r21 + r23 = PyObject_Vectorcall(r20, r22, 1, 0) + keep_alive r17 + r24 = __main__.globals :: static + r25 = 'c' + r26 = PyDict_SetItem(r24, r25, r23) + r27 = r26 >= 0 :: signed return 1 [case testDecoratorsSimple_toplevel] @@ -2618,25 +2732,37 @@ def g_a_obj.__call__(__mypyc_self__): r1 :: str r2 :: object r3 :: str - r4, r5, r6, r7 :: object - r8 :: str - r9 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7, r8, r9 :: object r10 :: str - r11, r12 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = 'Entering' r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) - r6 = r0.f - r7 = PyObject_CallFunctionObjArgs(r6, 0) - r8 = 'Exited' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 + r8 = r0.f + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = 'Exited' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 return 1 def a(f): f :: object @@ -2711,7 +2837,7 @@ L4: L5: goto L1 L6: - r5 = CPy_NoErrOccured() + r5 = CPy_NoErrOccurred() L7: L8: return r0 @@ -2740,7 +2866,7 @@ L4: L5: goto L1 L6: - r6 = CPy_NoErrOccured() + r6 = CPy_NoErrOccurred() L7: L8: return r0 @@ -2757,10 +2883,13 @@ def call_sum(l, comparison): r0 :: int r1, r2 :: object r3, x :: int - r4, r5 :: object - r6, r7 :: bool - r8, r9 :: int - r10 :: bit + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8, r9 :: bool + r10, r11 :: int + r12 :: bit L0: r0 = 0 r1 = PyObject_GetIter(l) @@ -2771,16 +2900,19 @@ L2: r3 = unbox(int, r2) x = r3 r4 = box(int, x) - r5 = PyObject_CallFunctionObjArgs(comparison, r4, 0) - r6 = unbox(bool, r5) - r7 = r6 << 1 - r8 = extend r7: builtins.bool to builtins.int - r9 = CPyTagged_Add(r0, r8) - r0 = r9 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(comparison, r6, 1, 0) + keep_alive r4 + r8 = unbox(bool, r7) + r9 = r8 << 1 + r10 = extend r9: builtins.bool to builtins.int + r11 = CPyTagged_Add(r0, r10) + r0 = r11 L3: goto L1 L4: - r10 = CPy_NoErrOccured() + r12 = CPy_NoErrOccurred() L5: return r0 @@ -3060,13 +3192,19 @@ def f(x): x :: int r0 :: object r1 :: str - r2, r3, r4 :: object + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object L0: r0 = builtins :: module r1 = 'reveal_type' r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 return 1 [case testCallCWithStrJoinMethod] @@ -3250,16 +3388,11 @@ def f(x: object) -> bool: return isinstance(x, bool) [out] def f(x): - x, r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool + x :: object + r0 :: bit L0: - r0 = load_address PyBool_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - return r3 + r0 = PyBool_Check(x) + return r0 [case testRangeObject] def range_object() -> None: @@ -3274,34 +3407,40 @@ def range_in_loop() -> None: sum += i [out] def range_object(): - r0, r1, r2, r3, r4 :: object - r5, r :: range + r0, r1, r2, r3 :: object + r4 :: object[3] + r5 :: object_ptr + r6 :: object + r7, r :: range sum :: int - r6, r7 :: object - r8, i, r9 :: int - r10 :: bit + r8, r9 :: object + r10, i, r11 :: int + r12 :: bit L0: r0 = load_address PyRange_Type r1 = object 4 r2 = object 12 r3 = object 2 - r4 = PyObject_CallFunctionObjArgs(r0, r1, r2, r3, 0) - r5 = cast(range, r4) - r = r5 + r4 = [r1, r2, r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r0, r5, 3, 0) + keep_alive r1, r2, r3 + r7 = cast(range, r6) + r = r7 sum = 0 - r6 = PyObject_GetIter(r) + r8 = PyObject_GetIter(r) L1: - r7 = PyIter_Next(r6) - if is_error(r7) goto L4 else goto L2 + r9 = PyIter_Next(r8) + if is_error(r9) goto L4 else goto L2 L2: - r8 = unbox(int, r7) - i = r8 - r9 = CPyTagged_Add(sum, i) - sum = r9 + r10 = unbox(int, r9) + i = r10 + r11 = CPyTagged_Add(sum, i) + sum = r11 L3: goto L1 L4: - r10 = CPy_NoErrOccured() + r12 = CPy_NoErrOccurred() L5: return 1 def range_in_loop(): @@ -3397,3 +3536,266 @@ L3: s = arg r3 = CPyObject_Size(s) return r3 + +[case testUndefinedFunction] +def f(): + non_existent_function() + +[out] +def f(): + r0 :: bool + r1, r2, r3 :: object +L0: + r0 = raise NameError('name "non_existent_function" is not defined') + r1 = box(None, 1) + r2 = PyObject_Vectorcall(r1, 0, 0, 0) + r3 = box(None, 1) + return r3 + +[case testStarArgFastPathTuple] +from typing import Any, Callable +def deco(fn: Callable[..., Any]) -> Callable[..., Any]: + def wrapper(*args: Any) -> Any: + return fn(*args) + return wrapper + +[out] +def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def wrapper_deco_obj.__call__(__mypyc_self__, args): + __mypyc_self__ :: __main__.wrapper_deco_obj + args :: tuple + r0 :: __main__.deco_env + r1, r2 :: object +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = r0.fn + r2 = PyObject_CallObject(r1, args) + return r2 +def deco(fn): + fn :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.wrapper_deco_obj + r3 :: bool + wrapper :: object +L0: + r0 = deco_env() + r0.fn = fn; r1 = is_error + r2 = wrapper_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + wrapper = r2 + return wrapper + +[case testStarArgFastPathList] +from typing import Any, Callable +def deco(fn: Callable[..., Any]) -> Callable[[list[Any]], Any]: + def wrapper(args: list[Any]) -> Any: + return fn(*args) + return wrapper + +[out] +def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def wrapper_deco_obj.__call__(__mypyc_self__, args): + __mypyc_self__ :: __main__.wrapper_deco_obj + args :: list + r0 :: __main__.deco_env + r1 :: object + r2 :: tuple + r3 :: object +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = r0.fn + r2 = PyList_AsTuple(args) + r3 = PyObject_CallObject(r1, r2) + return r3 +def deco(fn): + fn :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.wrapper_deco_obj + r3 :: bool + wrapper :: object +L0: + r0 = deco_env() + r0.fn = fn; r1 = is_error + r2 = wrapper_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + wrapper = r2 + return wrapper + +[case testStarArgFastPathListWithKwargs] +from typing import Any, Callable +def deco(fn: Callable[..., Any]) -> Callable[..., Any]: + def wrapper(lst: list[Any], kwargs: dict[str, Any]) -> Any: + return fn(*lst, **kwargs) + return wrapper + +[out] +def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def wrapper_deco_obj.__call__(__mypyc_self__, lst, kwargs): + __mypyc_self__ :: __main__.wrapper_deco_obj + lst :: list + kwargs :: dict + r0 :: __main__.deco_env + r1 :: object + r2 :: tuple + r3 :: dict + r4 :: object +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = r0.fn + r2 = PyList_AsTuple(lst) + r3 = PyDict_Copy(kwargs) + r4 = PyObject_Call(r1, r2, r3) + return r4 +def deco(fn): + fn :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.wrapper_deco_obj + r3 :: bool + wrapper :: object +L0: + r0 = deco_env() + r0.fn = fn; r1 = is_error + r2 = wrapper_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + wrapper = r2 + return wrapper + +[case testStarArgFastPathSequence] +from typing import Any, Callable +def deco(fn: Callable[[Any], Any]) -> Callable[[Any], Any]: + def wrapper(args: Any) -> Any: + return fn(*args) + return wrapper + +[out] +def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def wrapper_deco_obj.__call__(__mypyc_self__, args): + __mypyc_self__ :: __main__.wrapper_deco_obj + args :: object + r0 :: __main__.deco_env + r1 :: object + r2 :: tuple + r3 :: object +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = r0.fn + r2 = PySequence_Tuple(args) + r3 = PyObject_CallObject(r1, r2) + return r3 +def deco(fn): + fn :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.wrapper_deco_obj + r3 :: bool + wrapper :: object +L0: + r0 = deco_env() + r0.fn = fn; r1 = is_error + r2 = wrapper_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + wrapper = r2 + return wrapper + +[case testStarArgFastPathSequenceWithKwargs] +from typing import Any, Callable +def deco(fn: Callable[[Any], Any]) -> Callable[[Any], Any]: + def wrapper(args: Any, **kwargs: Any) -> Any: + return fn(*args, **kwargs) + return wrapper + +[out] +def wrapper_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def wrapper_deco_obj.__call__(__mypyc_self__, args, kwargs): + __mypyc_self__ :: __main__.wrapper_deco_obj + args :: object + kwargs :: dict + r0 :: __main__.deco_env + r1 :: object + r2 :: tuple + r3 :: dict + r4 :: object +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = r0.fn + r2 = PySequence_Tuple(args) + r3 = PyDict_Copy(kwargs) + r4 = PyObject_Call(r1, r2, r3) + return r4 +def deco(fn): + fn :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.wrapper_deco_obj + r3 :: bool + wrapper :: object +L0: + r0 = deco_env() + r0.fn = fn; r1 = is_error + r2 = wrapper_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + wrapper = r2 + return wrapper diff --git a/mypyc/test-data/irbuild-bool.test b/mypyc/test-data/irbuild-bool.test index 128266e6b1d75..5eac6d8db24f7 100644 --- a/mypyc/test-data/irbuild-bool.test +++ b/mypyc/test-data/irbuild-bool.test @@ -422,3 +422,232 @@ L0: r1 = extend r0: builtins.bool to builtins.int x = r1 return x + +[case testBitToBoolPromotion] +def bitand(x: float, y: float, z: float) -> bool: + b = (x == y) & (x == z) + return b +def bitor(x: float, y: float, z: float) -> bool: + b = (x == y) | (x == z) + return b +def bitxor(x: float, y: float, z: float) -> bool: + b = (x == y) ^ (x == z) + return b +def invert(x: float, y: float) -> bool: + return not(x == y) +[out] +def bitand(x, y, z): + x, y, z :: float + r0, r1 :: bit + r2, b :: bool +L0: + r0 = x == y + r1 = x == z + r2 = r0 & r1 + b = r2 + return b +def bitor(x, y, z): + x, y, z :: float + r0, r1 :: bit + r2, b :: bool +L0: + r0 = x == y + r1 = x == z + r2 = r0 | r1 + b = r2 + return b +def bitxor(x, y, z): + x, y, z :: float + r0, r1 :: bit + r2, b :: bool +L0: + r0 = x == y + r1 = x == z + r2 = r0 ^ r1 + b = r2 + return b +def invert(x, y): + x, y :: float + r0, r1 :: bit +L0: + r0 = x == y + r1 = r0 ^ 1 + return r1 + +[case testUnaryNotWithPrimitiveTypes] +def not_obj(x: object) -> bool: + return not x + +def not_int(x: int) -> bool: + return not x + +def not_str(x: str) -> bool: + return not x + +def not_list(x: list[int]) -> bool: + return not x + +def not_tuple(x: tuple[int, ...]) -> bool: + return not x + +def not_dict(x: dict[str, int]) -> bool: + return not x +[out] +def not_obj(x): + x :: object + r0 :: i32 + r1 :: bit + r2 :: bool +L0: + r0 = PyObject_Not(x) + r1 = r0 >= 0 :: signed + r2 = truncate r0: i32 to builtins.bool + return r2 +def not_int(x): + x :: int + r0 :: bit +L0: + r0 = int_eq x, 0 + return r0 +def not_str(x): + x :: str + r0, r1 :: bit +L0: + r0 = CPyStr_IsTrue(x) + r1 = r0 ^ 1 + return r1 +def not_list(x): + x :: list + r0 :: native_int + r1 :: short_int + r2, r3 :: bit +L0: + r0 = var_object_size x + r1 = r0 << 1 + r2 = int_ne r1, 0 + r3 = r2 ^ 1 + return r3 +def not_tuple(x): + x :: tuple + r0 :: native_int + r1 :: short_int + r2, r3 :: bit +L0: + r0 = var_object_size x + r1 = r0 << 1 + r2 = int_ne r1, 0 + r3 = r2 ^ 1 + return r3 +def not_dict(x): + x :: dict + r0 :: native_int + r1 :: short_int + r2, r3 :: bit +L0: + r0 = PyDict_Size(x) + r1 = r0 << 1 + r2 = int_ne r1, 0 + r3 = r2 ^ 1 + return r3 + +[case testUnaryNotWithNativeClass] +from __future__ import annotations + +class C: + def __bool__(self) -> bool: + return True + +def not_c(x: C) -> bool: + return not x + +def not_c_opt(x: C | None) -> bool: + return not x +[out] +def C.__bool__(self): + self :: __main__.C +L0: + return 1 +def not_c(x): + x :: __main__.C + r0, r1 :: bool +L0: + r0 = x.__bool__() + r1 = r0 ^ 1 + return r1 +def not_c_opt(x): + x :: union[__main__.C, None] + r0 :: object + r1, r2 :: bit + r3 :: __main__.C + r4, r5 :: bool +L0: + r0 = load_address _Py_NoneStruct + r1 = x == r0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = 1 + goto L3 +L2: + r3 = unchecked borrow cast(__main__.C, x) + r4 = r3.__bool__() + r5 = r4 ^ 1 + r2 = r5 +L3: + keep_alive x + return r2 + +[case testUnaryNotWithOptionalPrimitiveTypes] +from __future__ import annotations + +def not_str(x: str | None) -> bool: + return not x + +def not_list(x: list[int] | None) -> bool: + return not x +[out] +def not_str(x): + x :: union[str, None] + r0 :: object + r1, r2 :: bit + r3 :: str + r4, r5 :: bit +L0: + r0 = load_address _Py_NoneStruct + r1 = x == r0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = 1 + goto L3 +L2: + r3 = unchecked borrow cast(str, x) + r4 = CPyStr_IsTrue(r3) + r5 = r4 ^ 1 + r2 = r5 +L3: + keep_alive x + return r2 +def not_list(x): + x :: union[list, None] + r0 :: object + r1, r2 :: bit + r3 :: list + r4 :: native_int + r5 :: short_int + r6, r7 :: bit +L0: + r0 = load_address _Py_NoneStruct + r1 = x == r0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = 1 + goto L3 +L2: + r3 = unchecked borrow cast(list, x) + r4 = var_object_size r3 + r5 = r4 << 1 + r6 = int_ne r5, 0 + r7 = r6 ^ 1 + r2 = r7 +L3: + keep_alive x + return r2 diff --git a/mypyc/test-data/irbuild-bytes.test b/mypyc/test-data/irbuild-bytes.test index b41836d8829f6..8cfefe03ae22c 100644 --- a/mypyc/test-data/irbuild-bytes.test +++ b/mypyc/test-data/irbuild-bytes.test @@ -13,24 +13,30 @@ def f(num, l, d, s): s :: str r0, r1 :: object r2, b1 :: bytes - r3, r4, r5 :: object - r6, b2, r7, b3, r8, b4, r9, b5 :: bytes + r3, r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8, b2, r9, b3, r10, b4, r11, b5 :: bytes L0: r0 = load_address PyBytes_Type - r1 = PyObject_CallFunctionObjArgs(r0, 0) + r1 = PyObject_Vectorcall(r0, 0, 0, 0) r2 = cast(bytes, r1) b1 = r2 r3 = load_address PyBytes_Type r4 = box(int, num) - r5 = PyObject_CallFunctionObjArgs(r3, r4, 0) - r6 = cast(bytes, r5) - b2 = r6 - r7 = PyBytes_FromObject(l) - b3 = r7 - r8 = PyBytes_FromObject(d) - b4 = r8 - r9 = PyBytes_FromObject(s) - b5 = r9 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r3, r6, 1, 0) + keep_alive r4 + r8 = cast(bytes, r7) + b2 = r8 + r9 = PyBytes_FromObject(l) + b3 = r9 + r10 = PyBytes_FromObject(d) + b4 = r10 + r11 = PyBytes_FromObject(s) + b5 = r11 return 1 [case testBytearrayBasics] @@ -53,7 +59,7 @@ L0: r0 = builtins :: module r1 = 'bytearray' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) a = r3 r4 = PyByteArray_FromObject(s) b = r4 @@ -179,3 +185,35 @@ L0: r10 = CPyBytes_Build(2, var, r9) b4 = r10 return 1 + +[case testOptionalBytesEquality] +from typing import Optional + +def non_opt_opt(x: bytes, y: Optional[bytes]) -> bool: + return x != y +[out] +def non_opt_opt(x, y): + x :: bytes + y :: union[bytes, None] + r0 :: object + r1 :: bit + r2 :: bool + r3 :: bytes + r4 :: i32 + r5, r6 :: bit +L0: + r0 = load_address _Py_NoneStruct + r1 = y == r0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = 1 + goto L3 +L2: + r3 = unchecked borrow cast(bytes, y) + r4 = CPyBytes_Compare(r3, x) + r5 = r4 >= 0 :: signed + r6 = r4 != 1 + r2 = r6 +L3: + keep_alive y + return r2 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 2c15f09c9c341..504e6234de243 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -180,7 +180,7 @@ L0: o.x = r1; r2 = is_error return o -[case testSubclass_toplevel] +[case testSubclass_withgil_toplevel] from typing import TypeVar, Generic from mypy_extensions import trait T = TypeVar('T') @@ -209,53 +209,56 @@ def __top_level__(): r13 :: str r14 :: dict r15 :: str - r16, r17 :: object - r18 :: dict - r19 :: str - r20 :: i32 - r21 :: bit - r22 :: object - r23 :: str - r24, r25 :: object - r26 :: bool - r27 :: str - r28 :: tuple - r29 :: i32 - r30 :: bit - r31 :: dict - r32 :: str - r33 :: i32 - r34 :: bit - r35 :: object - r36 :: str - r37, r38 :: object - r39 :: str - r40 :: tuple - r41 :: i32 - r42 :: bit - r43 :: dict - r44 :: str - r45 :: i32 - r46 :: bit - r47, r48 :: object - r49 :: dict - r50 :: str - r51 :: object - r52 :: dict - r53 :: str - r54, r55 :: object - r56 :: tuple - r57 :: str - r58, r59 :: object - r60 :: bool - r61, r62 :: str - r63 :: tuple - r64 :: i32 - r65 :: bit - r66 :: dict - r67 :: str - r68 :: i32 - r69 :: bit + r16 :: object + r17 :: object[1] + r18 :: object_ptr + r19 :: object + r20 :: dict + r21 :: str + r22 :: i32 + r23 :: bit + r24 :: object + r25 :: str + r26, r27 :: object + r28 :: bool + r29 :: str + r30 :: tuple + r31 :: i32 + r32 :: bit + r33 :: dict + r34 :: str + r35 :: i32 + r36 :: bit + r37 :: object + r38 :: str + r39, r40 :: object + r41 :: str + r42 :: tuple + r43 :: i32 + r44 :: bit + r45 :: dict + r46 :: str + r47 :: i32 + r48 :: bit + r49, r50 :: object + r51 :: dict + r52 :: str + r53 :: object + r54 :: dict + r55 :: str + r56, r57 :: object + r58 :: tuple + r59 :: str + r60, r61 :: object + r62 :: bool + r63, r64 :: str + r65 :: tuple + r66 :: i32 + r67 :: bit + r68 :: dict + r69 :: str + r70 :: i32 + r71 :: bit L0: r0 = builtins :: module r1 = load_address _Py_NoneStruct @@ -280,62 +283,65 @@ L2: r14 = __main__.globals :: static r15 = 'TypeVar' r16 = CPyDict_GetItem(r14, r15) - r17 = PyObject_CallFunctionObjArgs(r16, r13, 0) - r18 = __main__.globals :: static - r19 = 'T' - r20 = CPyDict_SetItem(r18, r19, r17) - r21 = r20 >= 0 :: signed - r22 = :: object - r23 = '__main__' - r24 = __main__.C_template :: type - r25 = CPyType_FromTemplate(r24, r22, r23) - r26 = C_trait_vtable_setup() - r27 = '__mypyc_attrs__' - r28 = PyTuple_Pack(0) - r29 = PyObject_SetAttr(r25, r27, r28) - r30 = r29 >= 0 :: signed - __main__.C = r25 :: type - r31 = __main__.globals :: static - r32 = 'C' - r33 = CPyDict_SetItem(r31, r32, r25) - r34 = r33 >= 0 :: signed - r35 = :: object - r36 = '__main__' - r37 = __main__.S_template :: type - r38 = CPyType_FromTemplate(r37, r35, r36) - r39 = '__mypyc_attrs__' - r40 = PyTuple_Pack(0) - r41 = PyObject_SetAttr(r38, r39, r40) - r42 = r41 >= 0 :: signed - __main__.S = r38 :: type - r43 = __main__.globals :: static - r44 = 'S' - r45 = CPyDict_SetItem(r43, r44, r38) - r46 = r45 >= 0 :: signed - r47 = __main__.C :: type - r48 = __main__.S :: type - r49 = __main__.globals :: static - r50 = 'Generic' - r51 = CPyDict_GetItem(r49, r50) - r52 = __main__.globals :: static - r53 = 'T' - r54 = CPyDict_GetItem(r52, r53) - r55 = PyObject_GetItem(r51, r54) - r56 = PyTuple_Pack(3, r47, r48, r55) - r57 = '__main__' - r58 = __main__.D_template :: type - r59 = CPyType_FromTemplate(r58, r56, r57) - r60 = D_trait_vtable_setup() - r61 = '__mypyc_attrs__' - r62 = '__dict__' - r63 = PyTuple_Pack(1, r62) - r64 = PyObject_SetAttr(r59, r61, r63) - r65 = r64 >= 0 :: signed - __main__.D = r59 :: type - r66 = __main__.globals :: static - r67 = 'D' - r68 = CPyDict_SetItem(r66, r67, r59) - r69 = r68 >= 0 :: signed + r17 = [r13] + r18 = load_address r17 + r19 = PyObject_Vectorcall(r16, r18, 1, 0) + keep_alive r13 + r20 = __main__.globals :: static + r21 = 'T' + r22 = CPyDict_SetItem(r20, r21, r19) + r23 = r22 >= 0 :: signed + r24 = :: object + r25 = '__main__' + r26 = __main__.C_template :: type + r27 = CPyType_FromTemplate(r26, r24, r25) + r28 = C_trait_vtable_setup() + r29 = '__mypyc_attrs__' + r30 = CPyTuple_LoadEmptyTupleConstant() + r31 = PyObject_SetAttr(r27, r29, r30) + r32 = r31 >= 0 :: signed + __main__.C = r27 :: type + r33 = __main__.globals :: static + r34 = 'C' + r35 = PyDict_SetItem(r33, r34, r27) + r36 = r35 >= 0 :: signed + r37 = :: object + r38 = '__main__' + r39 = __main__.S_template :: type + r40 = CPyType_FromTemplate(r39, r37, r38) + r41 = '__mypyc_attrs__' + r42 = CPyTuple_LoadEmptyTupleConstant() + r43 = PyObject_SetAttr(r40, r41, r42) + r44 = r43 >= 0 :: signed + __main__.S = r40 :: type + r45 = __main__.globals :: static + r46 = 'S' + r47 = PyDict_SetItem(r45, r46, r40) + r48 = r47 >= 0 :: signed + r49 = __main__.C :: type + r50 = __main__.S :: type + r51 = __main__.globals :: static + r52 = 'Generic' + r53 = CPyDict_GetItem(r51, r52) + r54 = __main__.globals :: static + r55 = 'T' + r56 = CPyDict_GetItem(r54, r55) + r57 = PyObject_GetItem(r53, r56) + r58 = PyTuple_Pack(3, r49, r50, r57) + r59 = '__main__' + r60 = __main__.D_template :: type + r61 = CPyType_FromTemplate(r60, r58, r59) + r62 = D_trait_vtable_setup() + r63 = '__mypyc_attrs__' + r64 = '__dict__' + r65 = PyTuple_Pack(1, r64) + r66 = PyObject_SetAttr(r61, r63, r65) + r67 = r66 >= 0 :: signed + __main__.D = r61 :: type + r68 = __main__.globals :: static + r69 = 'D' + r70 = PyDict_SetItem(r68, r69, r61) + r71 = r70 >= 0 :: signed return 1 [case testIsInstance] @@ -357,7 +363,7 @@ def f(x): L0: r0 = __main__.B :: type r1 = get_element_ptr x ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive x r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -396,7 +402,7 @@ def f(x): L0: r0 = __main__.A :: type r1 = get_element_ptr x ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive x r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -406,7 +412,7 @@ L1: L2: r5 = __main__.B :: type r6 = get_element_ptr x ob_type :: PyObject - r7 = load_mem r6 :: builtins.object* + r7 = borrow load_mem r6 :: builtins.object* keep_alive x r8 = r7 == r5 r4 = r8 @@ -443,7 +449,7 @@ def f(x): L0: r0 = __main__.A :: type r1 = get_element_ptr x ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive x r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -453,7 +459,7 @@ L1: L2: r5 = __main__.R :: type r6 = get_element_ptr x ob_type :: PyObject - r7 = load_mem r6 :: builtins.object* + r7 = borrow load_mem r6 :: builtins.object* keep_alive x r8 = r7 == r5 r4 = r8 @@ -494,7 +500,7 @@ def f(x): L0: r0 = __main__.A :: type r1 = get_element_ptr x ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive x r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -504,7 +510,7 @@ L1: L2: r5 = __main__.C :: type r6 = get_element_ptr x ob_type :: PyObject - r7 = load_mem r6 :: builtins.object* + r7 = borrow load_mem r6 :: builtins.object* keep_alive x r8 = r7 == r5 r4 = r8 @@ -600,7 +606,7 @@ L0: r3 = CPyTagged_Add(r0, r2) return r3 -[case testCallClassMethodViaCls] +[case testCallClassMethodViaCls_64bit] class C: @classmethod def f(cls, x: int) -> int: @@ -613,7 +619,7 @@ class C: class D: @classmethod def f(cls, x: int) -> int: - # TODO: This could aso be optimized, since g is not ever overridden + # TODO: This could also be optimized, since g is not ever overridden return cls.g(x) @classmethod @@ -641,14 +647,20 @@ def D.f(cls, x): cls :: object x :: int r0 :: str - r1, r2 :: object - r3 :: int + r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4 :: object + r5 :: int L0: r0 = 'g' r1 = box(int, x) - r2 = CPyObject_CallMethodObjArgs(cls, r0, r1, 0) - r3 = unbox(int, r2) - return r3 + r2 = [cls, r1] + r3 = load_address r2 + r4 = PyObject_VectorcallMethod(r0, r3, 9223372036854775810, 0) + keep_alive cls, r1 + r5 = unbox(int, r4) + return r5 def D.g(cls, x): cls :: object x :: int @@ -747,18 +759,24 @@ def DictSubclass.__init__(self): self :: dict r0 :: object r1 :: str - r2, r3, r4 :: object - r5 :: str - r6, r7 :: object + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8, r9 :: object L0: r0 = builtins :: module r1 = 'super' r2 = CPyObject_GetAttr(r0, r1) r3 = __main__.DictSubclass :: type - r4 = PyObject_CallFunctionObjArgs(r2, r3, self, 0) - r5 = '__init__' - r6 = CPyObject_GetAttr(r4, r5) - r7 = PyObject_CallFunctionObjArgs(r6, 0) + r4 = [r3, self] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, self + r7 = '__init__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = PyObject_Vectorcall(r8, 0, 0, 0) return 1 [case testClassVariable] @@ -836,22 +854,42 @@ def Base.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.Base rhs, r0, r1 :: object r2 :: bit - r3 :: i32 - r4 :: bit - r5 :: bool + r3 :: object + r4, r5 :: bit r6 :: object + r7 :: bit + r8 :: i32 + r9 :: bit + r10 :: bool + r11 :: object L0: r0 = __mypyc_self__.__eq__(rhs) r1 = load_address _Py_NotImplementedStruct r2 = r0 == r1 - if r2 goto L2 else goto L1 :: bool + if r2 goto L7 else goto L1 :: bool L1: - r3 = PyObject_Not(r0) - r4 = r3 >= 0 :: signed - r5 = truncate r3: i32 to builtins.bool - r6 = box(bool, r5) - return r6 + r3 = load_global Py_True :: static + r4 = r0 == r3 + if r4 goto L2 else goto L3 :: bool L2: + r5 = 0 + goto L6 +L3: + r6 = load_global Py_False :: static + r7 = r0 == r6 + if r7 goto L4 else goto L5 :: bool +L4: + r5 = 1 + goto L6 +L5: + r8 = PyObject_Not(r0) + r9 = r8 >= 0 :: signed + r10 = truncate r8: i32 to builtins.bool + r5 = r10 +L6: + r11 = box(bit, r5) + return r11 +L7: return r1 def Derived.__eq__(self, other): self :: __main__.Derived @@ -892,7 +930,7 @@ L0: r1 = unbox(bool, r0) return r1 -[case testEqDefinedLater] +[case testEqDefinedLater_64bit] def f(a: 'Base', b: 'Base') -> bool: return a == b @@ -939,13 +977,18 @@ L0: def fOpt2(a, b): a, b :: __main__.Derived r0 :: str - r1 :: object - r2 :: bool + r1 :: object[2] + r2 :: object_ptr + r3 :: object + r4 :: bool L0: r0 = '__ne__' - r1 = CPyObject_CallMethodObjArgs(a, r0, b, 0) - r2 = unbox(bool, r1) - return r2 + r1 = [a, b] + r2 = load_address r1 + r3 = PyObject_VectorcallMethod(r0, r2, 9223372036854775810, 0) + keep_alive a, b + r4 = unbox(bool, r3) + return r4 def Derived.__eq__(self, other): self :: __main__.Derived other, r0 :: object @@ -956,22 +999,42 @@ def Derived.__ne__(__mypyc_self__, rhs): __mypyc_self__ :: __main__.Derived rhs, r0, r1 :: object r2 :: bit - r3 :: i32 - r4 :: bit - r5 :: bool + r3 :: object + r4, r5 :: bit r6 :: object + r7 :: bit + r8 :: i32 + r9 :: bit + r10 :: bool + r11 :: object L0: r0 = __mypyc_self__.__eq__(rhs) r1 = load_address _Py_NotImplementedStruct r2 = r0 == r1 - if r2 goto L2 else goto L1 :: bool + if r2 goto L7 else goto L1 :: bool L1: - r3 = PyObject_Not(r0) - r4 = r3 >= 0 :: signed - r5 = truncate r3: i32 to builtins.bool - r6 = box(bool, r5) - return r6 + r3 = load_global Py_True :: static + r4 = r0 == r3 + if r4 goto L2 else goto L3 :: bool L2: + r5 = 0 + goto L6 +L3: + r6 = load_global Py_False :: static + r7 = r0 == r6 + if r7 goto L4 else goto L5 :: bool +L4: + r5 = 1 + goto L6 +L5: + r8 = PyObject_Not(r0) + r9 = r8 >= 0 :: signed + r10 = truncate r8: i32 to builtins.bool + r5 = r10 +L6: + r11 = box(bit, r5) + return r11 +L7: return r1 [case testDefaultVars] @@ -1300,3 +1363,1533 @@ class T: class E(T): y: str # E: Type of "y" is incompatible with definition in trait "T" + + +[case testNestedClasses] +def outer(): + class Inner: # E: Nested class definitions not supported + pass + + return Inner + +if True: + class OtherInner: # E: Nested class definitions not supported + pass + +[case testEnumClassAlias] +from enum import Enum +from typing import Literal, Union + +class SomeEnum(Enum): + AVALUE = "a" + +ALIAS = Literal[SomeEnum.AVALUE] +ALIAS2 = Union[Literal[SomeEnum.AVALUE], None] + +[case testMypycAttrNativeClassErrors] +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class AnnontatedNonExtensionClass: + pass + +@mypyc_attr(native_class=False) +class DerivedExplicitNonNativeClass(AnnontatedNonExtensionClass): + pass + + +def decorator(cls): + return cls + +@mypyc_attr(native_class=True) +@decorator +class NonNativeClassContradiction(): # E: Class is marked as native_class=True but it can't be a native class. Classes that have decorators other than supported decorators can't be native classes. + pass + + +@mypyc_attr(native_class="yes") +class BadUse(): # E: native_class must be used with True or False only + pass + +[case testMypycAttrNativeClassMetaError] +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=True) +class M(type): # E: Inheriting from most builtin types is unimplemented \ + # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \ + # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes + pass + +@mypyc_attr(native_class=True) +class A(metaclass=M): # E: Class is marked as native_class=True but it can't be a native class. Classes with a metaclass other than ABCMeta, TypingMeta or GenericMeta can't be native classes. + pass + +[case testReservedName] +from typing import Any, overload + +def decorator(cls): + return cls + +class TestMethod: + def __mypyc_generator_helper__(self) -> None: # E: Method name "__mypyc_generator_helper__" is reserved for mypyc internal use + pass + +class TestDecorator: + @decorator # E: Method name "__mypyc_generator_helper__" is reserved for mypyc internal use + def __mypyc_generator_helper__(self) -> None: + pass + +class TestOverload: + @overload # E: Method name "__mypyc_generator_helper__" is reserved for mypyc internal use + def __mypyc_generator_helper__(self, x: int) -> int: ... + + @overload + def __mypyc_generator_helper__(self, x: str) -> str: ... + + def __mypyc_generator_helper__(self, x: Any) -> Any: + return x + +[case testNativeBufferFastPath] +from typing import Final +from mypy_extensions import u8 +from librt.internal import ( + WriteBuffer, ReadBuffer, write_bool, read_bool, write_str, read_str, write_float, read_float, + write_int, read_int, write_tag, read_tag, write_bytes, read_bytes, + cache_version, +) + +Tag = u8 +TAG: Final[Tag] = 1 + +def foo() -> None: + b = WriteBuffer() + write_str(b, "foo") + write_bytes(b, b"bar") + write_bool(b, True) + write_float(b, 0.1) + write_int(b, 1) + write_tag(b, TAG) + + rb = ReadBuffer(b.getvalue()) + x = read_str(rb) + xb = read_bytes(rb) + y = read_bool(rb) + z = read_float(rb) + t = read_int(rb) + u = read_tag(rb) + v = cache_version() +[out] +def foo(): + r0, b :: librt.internal.WriteBuffer + r1 :: str + r2 :: None + r3 :: bytes + r4, r5, r6, r7, r8 :: None + r9 :: bytes + r10, rb :: librt.internal.ReadBuffer + r11, x :: str + r12, xb :: bytes + r13, y :: bool + r14, z :: float + r15, t :: int + r16, u, r17, v :: u8 +L0: + r0 = WriteBuffer_internal() + b = r0 + r1 = 'foo' + r2 = write_str_internal(b, r1) + r3 = b'bar' + r4 = write_bytes_internal(b, r3) + r5 = write_bool_internal(b, 1) + r6 = write_float_internal(b, 0.1) + r7 = write_int_internal(b, 2) + r8 = write_tag_internal(b, 1) + r9 = WriteBuffer_getvalue_internal(b) + r10 = ReadBuffer_internal(r9) + rb = r10 + r11 = read_str_internal(rb) + x = r11 + r12 = read_bytes_internal(rb) + xb = r12 + r13 = read_bool_internal(rb) + y = r13 + r14 = read_float_internal(rb) + z = r14 + r15 = read_int_internal(rb) + t = r15 + r16 = read_tag_internal(rb) + u = r16 + r17 = cache_version_internal() + v = r17 + return 1 + +[case testEnumFastPath] +from enum import Enum + +def test(e: E) -> bool: + return e.is_one() + +class E(Enum): + ONE = 1 + TWO = 2 + + def is_one(self) -> bool: + return self == E.ONE +[out] +def test(e): + e :: __main__.E + r0 :: bool +L0: + r0 = e.__mypyc_fast_is_one() + return r0 +def is_one_E_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def is_one_E_obj.__call__(__mypyc_self__, self): + __mypyc_self__ :: __main__.is_one_E_obj + self, r0 :: __main__.E + r1 :: bool + r2 :: bit +L0: + r0 = __main__.E.ONE :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "ONE" was not set') + unreachable +L2: + r2 = self == r0 + return r2 +def E.__mypyc_fast_is_one(self): + self, r0 :: __main__.E + r1 :: bool + r2 :: bit +L0: + r0 = __main__.E.ONE :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "ONE" was not set') + unreachable +L2: + r2 = self == r0 + return r2 + +[case testTypeObjectName_python3_11] +from typing import Any + +class C: pass +class D(C): pass + +def n1(t: type[object]) -> str: + return t.__name__ + +def n2(t: Any) -> str: + return t.__name__ + +def n3() -> str: + return C.__name__ + +def n4(t: type[C]) -> str: + return t.__name__ +[out] +def n1(t): + t, r0 :: object + r1 :: str +L0: + r0 = CPy_GetName(t) + r1 = cast(str, r0) + return r1 +def n2(t): + t, r0 :: object + r1 :: str +L0: + r0 = CPy_GetName(t) + r1 = cast(str, r0) + return r1 +def n3(): + r0, r1 :: object + r2 :: str +L0: + r0 = __main__.C :: type + r1 = CPy_GetName(r0) + r2 = cast(str, r1) + return r2 +def n4(t): + t, r0 :: object + r1 :: str +L0: + r0 = CPy_GetName(t) + r1 = cast(str, r0) + return r1 + +[case testTypeOfObject] +class C: pass +class D(C): pass + +def generic_type(x: object) -> type[object]: + return type(x) + +def generic_class(x: object) -> type[object]: + return x.__class__ + +def native_type(x: C) -> type[object]: + return type(x) + +def native_class(x: C) -> type[object]: + return x.__class__ +[out] +def generic_type(x): + x, r0 :: object +L0: + r0 = CPy_TYPE(x) + return r0 +def generic_class(x): + x :: object + r0 :: str + r1 :: object +L0: + r0 = '__class__' + r1 = CPyObject_GetAttr(x, r0) + return r1 +def native_type(x): + x :: __main__.C + r0 :: object +L0: + r0 = CPy_TYPE(x) + return r0 +def native_class(x): + x :: __main__.C + r0 :: object +L0: + r0 = CPy_TYPE(x) + return r0 + +[case testDunderNew] +from __future__ import annotations +from typing import Any + +class Test: + val: int + + def __new__(cls, val: int) -> Test: + obj = super().__new__(cls) + obj.val = val + return obj + +class Test2: + def __new__(cls) -> Test2: + return super().__new__(cls) + +class Sub(Test2): + pass + +def fn() -> Test: + return Test.__new__(Test, 42) + +class NewClassMethod: + val: int + + @classmethod + def __new__(cls, val: int) -> NewClassMethod: + obj = super().__new__(cls) + obj.val = val + return obj + +def fn2() -> NewClassMethod: + return NewClassMethod.__new__(42) + +class NotTransformed: + def __new__(cls, val: int) -> Any: + return super().__new__(str) + + def factory(cls: Any, val: int) -> Any: + cls = str + return super().__new__(cls) + +[out] +def Test.__new__(cls, val): + cls :: object + val :: int + r0, obj :: __main__.Test + r1 :: bool +L0: + r0 = __mypyc__Test_setup(cls) + obj = r0 + obj.val = val; r1 = is_error + return obj +def Test2.__new__(cls): + cls, r0 :: object + r1 :: __main__.Test2 +L0: + r0 = CPy_SetupObject(cls) + r1 = cast(__main__.Test2, r0) + return r1 +def fn(): + r0 :: object + r1 :: __main__.Test +L0: + r0 = __main__.Test :: type + r1 = Test.__new__(r0, 84) + return r1 +def NewClassMethod.__new__(cls, val): + cls :: object + val :: int + r0, obj :: __main__.NewClassMethod + r1 :: bool +L0: + r0 = __mypyc__NewClassMethod_setup(cls) + obj = r0 + obj.val = val; r1 = is_error + return obj +def fn2(): + r0 :: object + r1 :: __main__.NewClassMethod +L0: + r0 = __main__.NewClassMethod :: type + r1 = NewClassMethod.__new__(r0, 84) + return r1 +def NotTransformed.__new__(cls, val): + cls :: object + val :: int + r0 :: object + r1 :: str + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8, r9 :: object + r10 :: object[1] + r11 :: object_ptr + r12 :: object + r13 :: str +L0: + r0 = builtins :: module + r1 = 'super' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.NotTransformed :: type + r4 = [r3, cls] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, cls + r7 = '__new__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = load_address PyUnicode_Type + r10 = [r9] + r11 = load_address r10 + r12 = PyObject_Vectorcall(r8, r11, 1, 0) + keep_alive r9 + r13 = cast(str, r12) + return r13 +def NotTransformed.factory(cls, val): + cls :: object + val :: int + r0, r1 :: object + r2 :: str + r3, r4 :: object + r5 :: object[2] + r6 :: object_ptr + r7 :: object + r8 :: str + r9 :: object + r10 :: object[1] + r11 :: object_ptr + r12 :: object +L0: + r0 = load_address PyUnicode_Type + cls = r0 + r1 = builtins :: module + r2 = 'super' + r3 = CPyObject_GetAttr(r1, r2) + r4 = __main__.NotTransformed :: type + r5 = [r4, cls] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r3, r6, 2, 0) + keep_alive r4, cls + r8 = '__new__' + r9 = CPyObject_GetAttr(r7, r8) + r10 = [cls] + r11 = load_address r10 + r12 = PyObject_Vectorcall(r9, r11, 1, 0) + keep_alive cls + return r12 + +[case testObjectDunderNew_64bit] +from __future__ import annotations +from mypy_extensions import mypyc_attr +from typing import Any + +class Test: + val: int + + def __new__(cls, val: int) -> Test: + obj = object.__new__(cls) + obj.val = val + return obj + +class Test2: + def __new__(cls) -> Test2: + return object.__new__(cls) + +class Sub(Test2): + pass + +def fn() -> Test: + return Test.__new__(Test, 42) + +class NewClassMethod: + val: int + + @classmethod + def __new__(cls, val: int) -> NewClassMethod: + obj = object.__new__(cls) + obj.val = val + return obj + +def fn2() -> NewClassMethod: + return NewClassMethod.__new__(42) + +class NotTransformed: + def __new__(cls, val: int) -> Any: + return object.__new__(str) + + def factory(cls: Any, val: int) -> Any: + cls = str + return object.__new__(cls) + +@mypyc_attr(native_class=False) +class NonNative: + def __new__(cls: Any) -> Any: + cls = str + return cls("str") + +class InheritsPython(dict): + def __new__(cls: Any) -> Any: + cls = dict + return cls({}) + +class ObjectNewOutsideDunderNew: + def __init__(self) -> None: + object.__new__(ObjectNewOutsideDunderNew) + +def object_new_outside_class() -> None: + object.__new__(Test) + +[out] +def Test.__new__(cls, val): + cls :: object + val :: int + r0, obj :: __main__.Test + r1 :: bool +L0: + r0 = __mypyc__Test_setup(cls) + obj = r0 + obj.val = val; r1 = is_error + return obj +def Test2.__new__(cls): + cls, r0 :: object + r1 :: __main__.Test2 +L0: + r0 = CPy_SetupObject(cls) + r1 = cast(__main__.Test2, r0) + return r1 +def fn(): + r0 :: object + r1 :: __main__.Test +L0: + r0 = __main__.Test :: type + r1 = Test.__new__(r0, 84) + return r1 +def NewClassMethod.__new__(cls, val): + cls :: object + val :: int + r0, obj :: __main__.NewClassMethod + r1 :: bool +L0: + r0 = __mypyc__NewClassMethod_setup(cls) + obj = r0 + obj.val = val; r1 = is_error + return obj +def fn2(): + r0 :: object + r1 :: __main__.NewClassMethod +L0: + r0 = __main__.NewClassMethod :: type + r1 = NewClassMethod.__new__(r0, 84) + return r1 +def NotTransformed.__new__(cls, val): + cls :: object + val :: int + r0 :: object + r1 :: str + r2, r3 :: object + r4 :: str + r5 :: object[2] + r6 :: object_ptr + r7 :: object + r8 :: str +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = load_address PyUnicode_Type + r4 = '__new__' + r5 = [r2, r3] + r6 = load_address r5 + r7 = PyObject_VectorcallMethod(r4, r6, 9223372036854775810, 0) + keep_alive r2, r3 + r8 = cast(str, r7) + return r8 +def NotTransformed.factory(cls, val): + cls :: object + val :: int + r0, r1 :: object + r2 :: str + r3 :: object + r4 :: str + r5 :: object[2] + r6 :: object_ptr + r7 :: object +L0: + r0 = load_address PyUnicode_Type + cls = r0 + r1 = builtins :: module + r2 = 'object' + r3 = CPyObject_GetAttr(r1, r2) + r4 = '__new__' + r5 = [r3, cls] + r6 = load_address r5 + r7 = PyObject_VectorcallMethod(r4, r6, 9223372036854775810, 0) + keep_alive r3, cls + return r7 +def __new___NonNative_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def __new___NonNative_obj.__call__(__mypyc_self__, cls): + __mypyc_self__ :: __main__.__new___NonNative_obj + cls, r0 :: object + r1 :: str + r2 :: object[1] + r3 :: object_ptr + r4 :: object +L0: + r0 = load_address PyUnicode_Type + cls = r0 + r1 = 'str' + r2 = [r1] + r3 = load_address r2 + r4 = PyObject_Vectorcall(cls, r3, 1, 0) + keep_alive r1 + return r4 +def InheritsPython.__new__(cls): + cls, r0 :: object + r1 :: dict + r2 :: object[1] + r3 :: object_ptr + r4 :: object +L0: + r0 = load_address PyDict_Type + cls = r0 + r1 = PyDict_New() + r2 = [r1] + r3 = load_address r2 + r4 = PyObject_Vectorcall(cls, r3, 1, 0) + keep_alive r1 + return r4 +def ObjectNewOutsideDunderNew.__init__(self): + self :: __main__.ObjectNewOutsideDunderNew + r0 :: object + r1 :: str + r2, r3 :: object + r4 :: str + r5 :: object[2] + r6 :: object_ptr + r7 :: object +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.ObjectNewOutsideDunderNew :: type + r4 = '__new__' + r5 = [r2, r3] + r6 = load_address r5 + r7 = PyObject_VectorcallMethod(r4, r6, 9223372036854775810, 0) + keep_alive r2, r3 + return 1 +def object_new_outside_class(): + r0 :: object + r1 :: str + r2, r3 :: object + r4 :: str + r5 :: object[2] + r6 :: object_ptr + r7 :: object +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.Test :: type + r4 = '__new__' + r5 = [r2, r3] + r6 = load_address r5 + r7 = PyObject_VectorcallMethod(r4, r6, 9223372036854775810, 0) + keep_alive r2, r3 + return 1 + +[case testUnsupportedDunderNew] +from __future__ import annotations +from mypy_extensions import mypyc_attr +from typing import Any + +@mypyc_attr(native_class=False) +class NonNative: + def __new__(cls) -> NonNative: + return super().__new__(cls) # E: "object.__new__()" not supported for non-extension classes + +class InheritsPython(dict): + def __new__(cls) -> InheritsPython: + return super().__new__(cls) # E: "object.__new__()" not supported for classes inheriting from non-native classes + +@mypyc_attr(native_class=False) +class NonNativeObjectNew: + def __new__(cls) -> NonNativeObjectNew: + return object.__new__(cls) # E: "object.__new__()" not supported for non-extension classes + +class InheritsPythonObjectNew(dict): + def __new__(cls) -> InheritsPythonObjectNew: + return object.__new__(cls) # E: "object.__new__()" not supported for classes inheriting from non-native classes + +class ClsAssignment: + def __new__(cls: Any) -> Any: + cls = str # E: Assignment to argument "cls" in "__new__" method unsupported + return super().__new__(cls) + +class ClsTupleAssignment: + def __new__(class_i_want: Any, val: int) -> Any: + class_i_want, val = dict, 1 # E: Assignment to argument "class_i_want" in "__new__" method unsupported + return object.__new__(class_i_want) + +class ClsListAssignment: + def __new__(cls: Any, val: str) -> Any: + [cls, val] = [object, "object"] # E: Assignment to argument "cls" in "__new__" method unsupported + return object.__new__(cls) + +class ClsNestedAssignment: + def __new__(cls: Any, val1: str, val2: int) -> Any: + [val1, [val2, cls]] = ["val1", [2, int]] # E: Assignment to argument "cls" in "__new__" method unsupported + return object.__new__(cls) + +class WrongNumberOfArgs: + def __new__(cls): + return super().__new__() # E: "object.__new__()" supported only with 1 argument, got 0 + +class WrongNumberOfArgsObjectNew: + def __new__(cls): + return object.__new__(cls, 1) # E: "object.__new__()" supported only with 1 argument, got 2 + +[case testClassWithFreeList] +from mypy_extensions import mypyc_attr, trait + +@mypyc_attr(free_list_len=1) +class UsesFreeList: + pass + +@mypyc_attr(free_list_len=None) +class NoFreeList: + pass + +@mypyc_attr(free_list_len=2) # E: Unsupported value for "free_list_len": 2 +class FreeListError: + pass + +@trait +@mypyc_attr(free_list_len=1) # E: "free_list_len" can't be used with traits +class NonNative: + pass + +@mypyc_attr(free_list_len=1, allow_interpreted_subclasses=True) # E: "free_list_len" can't be used in a class that allows interpreted subclasses +class InterpSub: + pass + +[case testUnsupportedGetAttr] +from mypy_extensions import mypyc_attr + +@mypyc_attr(allow_interpreted_subclasses=True) +class AllowsInterpreted: + def __getattr__(self, attr: str) -> object: # E: "__getattr__" not supported in class "AllowsInterpreted" because it allows interpreted subclasses + return 0 + +class InheritsInterpreted(dict): + def __getattr__(self, attr: str) -> object: # E: "__getattr__" not supported in class "InheritsInterpreted" because it inherits from a non-native class + return 0 + +@mypyc_attr(native_class=False) +class NonNative: + pass + +class InheritsNonNative(NonNative): + def __getattr__(self, attr: str) -> object: # E: "__getattr__" not supported in class "InheritsNonNative" because it inherits from a non-native class + return 0 + +[case testGetAttr] +from typing import ClassVar + +class GetAttr: + class_var = "x" + class_var_annotated: ClassVar[int] = 99 + + def __init__(self, regular_attr: int): + self.regular_attr = regular_attr + + def __getattr__(self, attr: str) -> object: + return attr + + def method(self) -> int: + return 0 + +def test_getattr() -> list[object]: + i = GetAttr(42) + one = i.one + two = i.regular_attr + three = i.class_var + four = i.class_var_annotated + five = i.method() + return [one, two, three, four, five] + +[typing fixtures/typing-full.pyi] +[out] +def GetAttr.__init__(self, regular_attr): + self :: __main__.GetAttr + regular_attr :: int +L0: + self.regular_attr = regular_attr + return 1 +def GetAttr.__getattr__(self, attr): + self :: __main__.GetAttr + attr :: str +L0: + return attr +def GetAttr.__getattr____wrapper(__mypyc_self__, attr): + __mypyc_self__ :: __main__.GetAttr + attr, r0 :: object + r1 :: bit + r2 :: str + r3 :: object +L0: + r0 = CPyObject_GenericGetAttr(__mypyc_self__, attr) + r1 = r0 != 0 + if r1 goto L1 else goto L2 :: bool +L1: + return r0 +L2: + r2 = cast(str, attr) + r3 = __mypyc_self__.__getattr__(r2) + return r3 +def GetAttr.method(self): + self :: __main__.GetAttr +L0: + return 0 +def GetAttr.__mypyc_defaults_setup(__mypyc_self__): + __mypyc_self__ :: __main__.GetAttr + r0 :: str +L0: + r0 = 'x' + __mypyc_self__.class_var = r0 + return 1 +def test_getattr(): + r0, i :: __main__.GetAttr + r1 :: str + r2, one :: object + r3, two :: int + r4, three, r5 :: str + r6 :: object + r7, four, r8, five :: int + r9 :: list + r10, r11, r12 :: object + r13 :: ptr +L0: + r0 = GetAttr(84) + i = r0 + r1 = 'one' + r2 = CPyObject_GetAttr(i, r1) + one = r2 + r3 = i.regular_attr + two = r3 + r4 = i.class_var + three = r4 + r5 = 'class_var_annotated' + r6 = CPyObject_GetAttr(i, r5) + r7 = unbox(int, r6) + four = r7 + r8 = i.method() + five = r8 + r9 = PyList_New(5) + r10 = box(int, two) + r11 = box(int, four) + r12 = box(int, five) + r13 = list_items r9 + buf_init_item r13, 0, one + buf_init_item r13, 1, r10 + buf_init_item r13, 2, three + buf_init_item r13, 3, r11 + buf_init_item r13, 4, r12 + keep_alive r9 + return r9 + +[case testUnsupportedSetAttr] +from mypy_extensions import mypyc_attr + +@mypyc_attr(allow_interpreted_subclasses=True) +class AllowsInterpreted: + def __setattr__(self, attr: str, val: object) -> None: # E: "__setattr__" not supported in class "AllowsInterpreted" because it allows interpreted subclasses + pass + + def __delattr__(self, attr: str) -> None: + pass + +class InheritsInterpreted(dict): + def __setattr__(self, attr: str, val: object) -> None: # E: "__setattr__" not supported in class "InheritsInterpreted" because it inherits from a non-native class + pass + + def __delattr__(self, attr: str) -> None: + pass + +@mypyc_attr(native_class=False) +class NonNative: + def __setattr__(self, attr: str, val: object) -> None: + pass + +class InheritsNonNative(NonNative): + def __setattr__(self, attr: str, val: object) -> None: # E: "__setattr__" not supported in class "InheritsNonNative" because it inherits from a non-native class + pass + + def __delattr__(self, attr: str) -> None: + pass + +[case testUnsupportedDelAttr] +class SetAttr: + def __setattr__(self, attr: str, val: object) -> None: + pass + +class NoSetAttr: + def __delattr__(self, attr: str) -> None: # E: "__delattr__" supported only in classes that also override "__setattr__", or inherit from a native class that overrides it. + pass + +class InheritedSetAttr(SetAttr): + def __delattr__(self, attr: str) -> None: + pass + +[case testSetAttr] +from typing import ClassVar +class SetAttr: + _attributes: dict[str, object] + regular_attr: int + class_var: ClassVar[str] = "x" + + def __init__(self, regular_attr: int, extra_attrs: dict[str, object], new_attr: str, new_val: object) -> None: + super().__setattr__("_attributes", extra_attrs) + object.__setattr__(self, "regular_attr", regular_attr) + + super().__setattr__(new_attr, new_val) + object.__setattr__(self, new_attr, new_val) + + def __setattr__(self, key: str, val: object) -> None: + if key == "regular_attr": + super().__setattr__("regular_attr", val) + elif key == "class_var": + raise AttributeError() + else: + self._attributes[key] = val + +def test(attr: str, val: object) -> None: + i = SetAttr(99, {}, attr, val) + i.regular_attr = 100 + i.new_attr = 101 + + object.__setattr__(i, "regular_attr", 11) + object.__setattr__(i, attr, val) + +[typing fixtures/typing-full.pyi] +[out] +def SetAttr.__init__(self, regular_attr, extra_attrs, new_attr, new_val): + self :: __main__.SetAttr + regular_attr :: int + extra_attrs :: dict + new_attr :: str + new_val :: object + r0 :: i32 + r1 :: bit + r2 :: i32 + r3 :: bit +L0: + self._attributes = extra_attrs + self.regular_attr = regular_attr + r0 = CPyObject_GenericSetAttr(self, new_attr, new_val) + r1 = r0 >= 0 :: signed + r2 = CPyObject_GenericSetAttr(self, new_attr, new_val) + r3 = r2 >= 0 :: signed + return 1 +def SetAttr.__setattr__(self, key, val): + self :: __main__.SetAttr + key :: str + val :: object + r0 :: str + r1 :: bool + r2 :: int + r3 :: bool + r4 :: str + r5 :: bool + r6 :: object + r7 :: str + r8, r9 :: object + r10 :: dict + r11 :: i32 + r12 :: bit +L0: + r0 = 'regular_attr' + r1 = CPyStr_EqualLiteral(key, r0, 12) + if r1 goto L1 else goto L2 :: bool +L1: + r2 = unbox(int, val) + self.regular_attr = r2; r3 = is_error + goto L6 +L2: + r4 = 'class_var' + r5 = CPyStr_EqualLiteral(key, r4, 9) + if r5 goto L3 else goto L4 :: bool +L3: + r6 = builtins :: module + r7 = 'AttributeError' + r8 = CPyObject_GetAttr(r6, r7) + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + CPy_Raise(r9) + unreachable +L4: + r10 = self._attributes + r11 = CPyDict_SetItem(r10, key, val) + r12 = r11 >= 0 :: signed +L5: +L6: + return 1 +def SetAttr.__setattr____wrapper(__mypyc_self__, attr, value): + __mypyc_self__ :: __main__.SetAttr + attr, value :: object + r0 :: bit + r1 :: i32 + r2 :: bit + r3 :: str + r4 :: None +L0: + r0 = value == 0 + if r0 goto L1 else goto L2 :: bool +L1: + r1 = CPyObject_GenericSetAttr(__mypyc_self__, attr, 0) + r2 = r1 >= 0 :: signed + return 0 +L2: + r3 = cast(str, attr) + r4 = __mypyc_self__.__setattr__(r3, value) + return 0 +def test(attr, val): + attr :: str + val :: object + r0 :: dict + r1, i :: __main__.SetAttr + r2 :: str + r3 :: object + r4 :: None + r5 :: str + r6 :: object + r7 :: i32 + r8 :: bit + r9 :: str + r10 :: object + r11 :: i32 + r12 :: bit + r13 :: i32 + r14 :: bit +L0: + r0 = PyDict_New() + r1 = SetAttr(198, r0, attr, val) + i = r1 + r2 = 'regular_attr' + r3 = object 100 + r4 = i.__setattr__(r2, r3) + r5 = 'new_attr' + r6 = object 101 + r7 = PyObject_SetAttr(i, r5, r6) + r8 = r7 >= 0 :: signed + r9 = 'regular_attr' + r10 = object 11 + r11 = CPyObject_GenericSetAttr(i, r9, r10) + r12 = r11 >= 0 :: signed + r13 = CPyObject_GenericSetAttr(i, attr, val) + r14 = r13 >= 0 :: signed + return 1 + +[case testSetAttrAndDelAttr] +from typing import ClassVar +class SetAttr: + _attributes: dict[str, object] + regular_attr: int + class_var: ClassVar[str] = "x" + + def __init__(self, regular_attr: int, extra_attrs: dict[str, object], new_attr: str, new_val: object) -> None: + super().__setattr__("_attributes", extra_attrs) + object.__setattr__(self, "regular_attr", regular_attr) + + super().__setattr__(new_attr, new_val) + object.__setattr__(self, new_attr, new_val) + + def __setattr__(self, key: str, val: object) -> None: + if key == "regular_attr": + super().__setattr__("regular_attr", val) + elif key == "class_var": + raise AttributeError() + else: + self._attributes[key] = val + + def __delattr__(self, key: str) -> None: + del self._attributes[key] + +[typing fixtures/typing-full.pyi] +[out] +def SetAttr.__init__(self, regular_attr, extra_attrs, new_attr, new_val): + self :: __main__.SetAttr + regular_attr :: int + extra_attrs :: dict + new_attr :: str + new_val :: object + r0 :: i32 + r1 :: bit + r2 :: i32 + r3 :: bit +L0: + self._attributes = extra_attrs + self.regular_attr = regular_attr + r0 = CPyObject_GenericSetAttr(self, new_attr, new_val) + r1 = r0 >= 0 :: signed + r2 = CPyObject_GenericSetAttr(self, new_attr, new_val) + r3 = r2 >= 0 :: signed + return 1 +def SetAttr.__setattr__(self, key, val): + self :: __main__.SetAttr + key :: str + val :: object + r0 :: str + r1 :: bool + r2 :: int + r3 :: bool + r4 :: str + r5 :: bool + r6 :: object + r7 :: str + r8, r9 :: object + r10 :: dict + r11 :: i32 + r12 :: bit +L0: + r0 = 'regular_attr' + r1 = CPyStr_EqualLiteral(key, r0, 12) + if r1 goto L1 else goto L2 :: bool +L1: + r2 = unbox(int, val) + self.regular_attr = r2; r3 = is_error + goto L6 +L2: + r4 = 'class_var' + r5 = CPyStr_EqualLiteral(key, r4, 9) + if r5 goto L3 else goto L4 :: bool +L3: + r6 = builtins :: module + r7 = 'AttributeError' + r8 = CPyObject_GetAttr(r6, r7) + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + CPy_Raise(r9) + unreachable +L4: + r10 = self._attributes + r11 = CPyDict_SetItem(r10, key, val) + r12 = r11 >= 0 :: signed +L5: +L6: + return 1 +def SetAttr.__setattr____wrapper(__mypyc_self__, attr, value): + __mypyc_self__ :: __main__.SetAttr + attr, value :: object + r0 :: bit + r1 :: str + r2 :: None + r3 :: str + r4 :: None +L0: + r0 = value == 0 + if r0 goto L1 else goto L2 :: bool +L1: + r1 = cast(str, attr) + r2 = __mypyc_self__.__delattr__(r1) + return 0 +L2: + r3 = cast(str, attr) + r4 = __mypyc_self__.__setattr__(r3, value) + return 0 +def SetAttr.__delattr__(self, key): + self :: __main__.SetAttr + key :: str + r0 :: dict + r1 :: i32 + r2 :: bit +L0: + r0 = self._attributes + r1 = PyObject_DelItem(r0, key) + r2 = r1 >= 0 :: signed + return 1 + +[case testUntransformedSetAttr_64bit] +from mypy_extensions import mypyc_attr + +class SetAttr: + def super_missing_args(self): + super().__setattr__() + super().__setattr__("attr") + + def object_missing_args(self): + object.__setattr__() + object.__setattr__(self) + object.__setattr__(self, "attr") + +@mypyc_attr(native_class=False) +class NonNative: + def super_setattr(self, key: str, val: object) -> None: + super().__setattr__(key, val) + + def object_setattr(self, key: str, val: object) -> None: + object.__setattr__(self, key, val) + +class InheritsPython(NonNative): + def super_setattr(self, key: str, val: object) -> None: + super().__setattr__(key, val) + + def object_setattr(self, key: str, val: object) -> None: + object.__setattr__(self, key, val) + +class BuiltInBase(dict): + def super_setattr(self, key: str, val: object) -> None: + super().__setattr__(key, val) + + def object_setattr(self, key: str, val: object) -> None: + object.__setattr__(self, key, val) + +[typing fixtures/typing-full.pyi] +[out] +def SetAttr.super_missing_args(self): + self :: __main__.SetAttr + r0 :: object + r1 :: str + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8, r9, r10 :: object + r11 :: str + r12, r13 :: object + r14 :: object[2] + r15 :: object_ptr + r16 :: object + r17 :: str + r18 :: object + r19 :: str + r20 :: object[1] + r21 :: object_ptr + r22, r23 :: object +L0: + r0 = builtins :: module + r1 = 'super' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.SetAttr :: type + r4 = [r3, self] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, self + r7 = '__setattr__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = PyObject_Vectorcall(r8, 0, 0, 0) + r10 = builtins :: module + r11 = 'super' + r12 = CPyObject_GetAttr(r10, r11) + r13 = __main__.SetAttr :: type + r14 = [r13, self] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r12, r15, 2, 0) + keep_alive r13, self + r17 = '__setattr__' + r18 = CPyObject_GetAttr(r16, r17) + r19 = 'attr' + r20 = [r19] + r21 = load_address r20 + r22 = PyObject_Vectorcall(r18, r21, 1, 0) + keep_alive r19 + r23 = box(None, 1) + return r23 +def SetAttr.object_missing_args(self): + self :: __main__.SetAttr + r0 :: object + r1 :: str + r2 :: object + r3 :: str + r4 :: object[1] + r5 :: object_ptr + r6, r7 :: object + r8 :: str + r9 :: object + r10 :: str + r11 :: object[2] + r12 :: object_ptr + r13, r14 :: object + r15 :: str + r16 :: object + r17, r18 :: str + r19 :: object[3] + r20 :: object_ptr + r21, r22 :: object +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = '__setattr__' + r4 = [r2] + r5 = load_address r4 + r6 = PyObject_VectorcallMethod(r3, r5, 9223372036854775809, 0) + keep_alive r2 + r7 = builtins :: module + r8 = 'object' + r9 = CPyObject_GetAttr(r7, r8) + r10 = '__setattr__' + r11 = [r9, self] + r12 = load_address r11 + r13 = PyObject_VectorcallMethod(r10, r12, 9223372036854775810, 0) + keep_alive r9, self + r14 = builtins :: module + r15 = 'object' + r16 = CPyObject_GetAttr(r14, r15) + r17 = 'attr' + r18 = '__setattr__' + r19 = [r16, self, r17] + r20 = load_address r19 + r21 = PyObject_VectorcallMethod(r18, r20, 9223372036854775811, 0) + keep_alive r16, self, r17 + r22 = box(None, 1) + return r22 +def super_setattr_NonNative_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def super_setattr_NonNative_obj.__call__(__mypyc_self__, self, key, val): + __mypyc_self__ :: __main__.super_setattr_NonNative_obj + self :: __main__.NonNative + key :: str + val, r0 :: object + r1 :: str + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8 :: object + r9 :: object[2] + r10 :: object_ptr + r11 :: object +L0: + r0 = builtins :: module + r1 = 'super' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.NonNative :: type + r4 = [r3, self] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, self + r7 = '__setattr__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = [key, val] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 2, 0) + keep_alive key, val + return 1 +def object_setattr_NonNative_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def object_setattr_NonNative_obj.__call__(__mypyc_self__, self, key, val): + __mypyc_self__ :: __main__.object_setattr_NonNative_obj + self :: __main__.NonNative + key :: str + val, r0 :: object + r1 :: str + r2 :: object + r3 :: str + r4 :: object[4] + r5 :: object_ptr + r6 :: object +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = '__setattr__' + r4 = [r2, self, key, val] + r5 = load_address r4 + r6 = PyObject_VectorcallMethod(r3, r5, 9223372036854775812, 0) + keep_alive r2, self, key, val + return 1 +def InheritsPython.super_setattr(self, key, val): + self :: __main__.InheritsPython + key :: str + val, r0 :: object + r1 :: str + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8 :: object + r9 :: object[2] + r10 :: object_ptr + r11 :: object +L0: + r0 = builtins :: module + r1 = 'super' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.InheritsPython :: type + r4 = [r3, self] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, self + r7 = '__setattr__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = [key, val] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 2, 0) + keep_alive key, val + return 1 +def InheritsPython.object_setattr(self, key, val): + self :: __main__.InheritsPython + key :: str + val, r0 :: object + r1 :: str + r2 :: object + r3 :: str + r4 :: object[4] + r5 :: object_ptr + r6 :: object +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = '__setattr__' + r4 = [r2, self, key, val] + r5 = load_address r4 + r6 = PyObject_VectorcallMethod(r3, r5, 9223372036854775812, 0) + keep_alive r2, self, key, val + return 1 +def BuiltInBase.super_setattr(self, key, val): + self :: dict + key :: str + val, r0 :: object + r1 :: str + r2, r3 :: object + r4 :: object[2] + r5 :: object_ptr + r6 :: object + r7 :: str + r8 :: object + r9 :: object[2] + r10 :: object_ptr + r11 :: object +L0: + r0 = builtins :: module + r1 = 'super' + r2 = CPyObject_GetAttr(r0, r1) + r3 = __main__.BuiltInBase :: type + r4 = [r3, self] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 2, 0) + keep_alive r3, self + r7 = '__setattr__' + r8 = CPyObject_GetAttr(r6, r7) + r9 = [key, val] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 2, 0) + keep_alive key, val + return 1 +def BuiltInBase.object_setattr(self, key, val): + self :: dict + key :: str + val, r0 :: object + r1 :: str + r2 :: object + r3 :: str + r4 :: object[4] + r5 :: object_ptr + r6 :: object +L0: + r0 = builtins :: module + r1 = 'object' + r2 = CPyObject_GetAttr(r0, r1) + r3 = '__setattr__' + r4 = [r2, self, key, val] + r5 = load_address r4 + r6 = PyObject_VectorcallMethod(r3, r5, 9223372036854775812, 0) + keep_alive r2, self, key, val + return 1 + +[case testInvalidMypycAttr] +from mypy_extensions import mypyc_attr + +@mypyc_attr("allow_interpreted_subclasses", "invalid_arg") # E: "invalid_arg" is not a supported "mypyc_attr" \ + # N: supported keys: "allow_interpreted_subclasses", "free_list_len", "native_class", "serializable" +class InvalidArg: + pass +@mypyc_attr(invalid_kwarg=True) # E: "invalid_kwarg" is not a supported "mypyc_attr" \ + # N: supported keys: "allow_interpreted_subclasses", "free_list_len", "native_class", "serializable" +class InvalidKwarg: + pass +@mypyc_attr(str()) # E: All "mypyc_attr" positional arguments must be string literals. +class InvalidLiteral: + pass diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 97b13ab337c73..cd953c84c5414 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -187,7 +187,7 @@ L0: return 1 [case testIntConstantFoldingFinal] -from typing_extensions import Final +from typing import Final X: Final = 5 Y: Final = 2 + 4 @@ -203,7 +203,7 @@ L0: return 1 [case testIntConstantFoldingClassFinal] -from typing_extensions import Final +from typing import Final class C: X: Final = 5 @@ -222,7 +222,7 @@ L0: return 1 [case testFloatConstantFolding] -from typing_extensions import Final +from typing import Final N: Final = 1.5 N2: Final = 1.5 * 2 @@ -391,7 +391,7 @@ L2: return 1 [case testStrConstantFolding] -from typing_extensions import Final +from typing import Final S: Final = 'z' N: Final = 2 @@ -416,7 +416,7 @@ L0: return 1 [case testBytesConstantFolding] -from typing_extensions import Final +from typing import Final N: Final = 2 @@ -438,7 +438,7 @@ L0: return 1 [case testComplexConstantFolding] -from typing_extensions import Final +from typing import Final N: Final = 1 FLOAT_N: Final = 1.5 diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 6139a02029b9b..e7a330951ab0d 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -152,41 +152,39 @@ def increment(d): d :: dict r0 :: short_int r1 :: native_int - r2 :: short_int - r3 :: object - r4 :: tuple[bool, short_int, object] - r5 :: short_int - r6 :: bool - r7 :: object - r8, k :: str - r9, r10, r11 :: object - r12 :: i32 - r13, r14, r15 :: bit + r2 :: object + r3 :: tuple[bool, short_int, object] + r4 :: short_int + r5 :: bool + r6 :: object + r7, k :: str + r8, r9, r10 :: object + r11 :: i32 + r12, r13, r14 :: bit L0: r0 = 0 r1 = PyDict_Size(d) - r2 = r1 << 1 - r3 = CPyDict_GetKeysIter(d) + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r0) - r5 = r4[1] - r0 = r5 - r6 = r4[0] - if r6 goto L2 else goto L4 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L4 :: bool L2: - r7 = r4[2] - r8 = cast(str, r7) - k = r8 - r9 = CPyDict_GetItem(d, k) - r10 = object 1 - r11 = PyNumber_InPlaceAdd(r9, r10) - r12 = CPyDict_SetItem(d, k, r11) - r13 = r12 >= 0 :: signed + r6 = r3[2] + r7 = cast(str, r6) + k = r7 + r8 = CPyDict_GetItem(d, k) + r9 = object 1 + r10 = PyNumber_InPlaceAdd(r8, r9) + r11 = CPyDict_SetItem(d, k, r10) + r12 = r11 >= 0 :: signed L3: - r14 = CPyDict_CheckSize(d, r2) + r13 = CPyDict_CheckSize(d, r1) goto L1 L4: - r15 = CPy_NoErrOccured() + r14 = CPy_NoErrOccurred() L5: return d @@ -218,8 +216,7 @@ L0: return r2 [case testDictIterationMethods] -from typing import Dict, Union -from typing_extensions import TypedDict +from typing import Dict, TypedDict, Union class Person(TypedDict): name: str @@ -239,205 +236,191 @@ def typeddict(d: Person) -> None: for k, v in d.items(): if k == "name": name = v +[typing fixtures/typing-full.pyi] [out] def print_dict_methods(d1, d2): d1, d2 :: dict r0 :: short_int r1 :: native_int - r2 :: short_int - r3 :: object - r4 :: tuple[bool, short_int, object] - r5 :: short_int - r6 :: bool - r7 :: object - r8, v :: int - r9 :: object - r10 :: i32 - r11 :: bit - r12 :: bool - r13, r14 :: bit - r15 :: short_int - r16 :: native_int - r17 :: short_int - r18 :: object - r19 :: tuple[bool, short_int, object, object] - r20 :: short_int - r21 :: bool - r22, r23 :: object - r24, r25, k :: int - r26, r27, r28, r29, r30 :: object - r31 :: i32 - r32, r33, r34 :: bit + r2 :: object + r3 :: tuple[bool, short_int, object] + r4 :: short_int + r5 :: bool + r6 :: object + r7, v :: int + r8 :: object + r9 :: i32 + r10 :: bit + r11 :: bool + r12, r13 :: bit + r14 :: short_int + r15 :: native_int + r16 :: object + r17 :: tuple[bool, short_int, object, object] + r18 :: short_int + r19 :: bool + r20, r21 :: object + r22, r23, k :: int + r24, r25, r26, r27, r28 :: object + r29 :: i32 + r30, r31, r32 :: bit L0: r0 = 0 r1 = PyDict_Size(d1) - r2 = r1 << 1 - r3 = CPyDict_GetValuesIter(d1) + r2 = CPyDict_GetValuesIter(d1) L1: - r4 = CPyDict_NextValue(r3, r0) - r5 = r4[1] - r0 = r5 - r6 = r4[0] - if r6 goto L2 else goto L6 :: bool + r3 = CPyDict_NextValue(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r7 = r4[2] - r8 = unbox(int, r7) - v = r8 - r9 = box(int, v) - r10 = PyDict_Contains(d2, r9) - r11 = r10 >= 0 :: signed - r12 = truncate r10: i32 to builtins.bool - if r12 goto L3 else goto L4 :: bool + r6 = r3[2] + r7 = unbox(int, r6) + v = r7 + r8 = box(int, v) + r9 = PyDict_Contains(d2, r8) + r10 = r9 >= 0 :: signed + r11 = truncate r9: i32 to builtins.bool + if r11 goto L3 else goto L4 :: bool L3: return 1 L4: L5: - r13 = CPyDict_CheckSize(d1, r2) + r12 = CPyDict_CheckSize(d1, r1) goto L1 L6: - r14 = CPy_NoErrOccured() + r13 = CPy_NoErrOccurred() L7: - r15 = 0 - r16 = PyDict_Size(d2) - r17 = r16 << 1 - r18 = CPyDict_GetItemsIter(d2) + r14 = 0 + r15 = PyDict_Size(d2) + r16 = CPyDict_GetItemsIter(d2) L8: - r19 = CPyDict_NextItem(r18, r15) - r20 = r19[1] - r15 = r20 - r21 = r19[0] - if r21 goto L9 else goto L11 :: bool + r17 = CPyDict_NextItem(r16, r14) + r18 = r17[1] + r14 = r18 + r19 = r17[0] + if r19 goto L9 else goto L11 :: bool L9: - r22 = r19[2] - r23 = r19[3] - r24 = unbox(int, r22) - r25 = unbox(int, r23) - k = r24 - v = r25 - r26 = box(int, k) - r27 = CPyDict_GetItem(d2, r26) - r28 = box(int, v) - r29 = PyNumber_InPlaceAdd(r27, r28) - r30 = box(int, k) - r31 = CPyDict_SetItem(d2, r30, r29) - r32 = r31 >= 0 :: signed + r20 = r17[2] + r21 = r17[3] + r22 = unbox(int, r20) + r23 = unbox(int, r21) + k = r22 + v = r23 + r24 = box(int, k) + r25 = CPyDict_GetItem(d2, r24) + r26 = box(int, v) + r27 = PyNumber_InPlaceAdd(r25, r26) + r28 = box(int, k) + r29 = CPyDict_SetItem(d2, r28, r27) + r30 = r29 >= 0 :: signed L10: - r33 = CPyDict_CheckSize(d2, r17) + r31 = CPyDict_CheckSize(d2, r15) goto L8 L11: - r34 = CPy_NoErrOccured() + r32 = CPy_NoErrOccurred() L12: return 1 def union_of_dicts(d): d, r0, new :: dict r1 :: short_int r2 :: native_int - r3 :: short_int - r4 :: object - r5 :: tuple[bool, short_int, object, object] - r6 :: short_int - r7 :: bool - r8, r9 :: object - r10 :: str - r11 :: union[int, str] + r3 :: object + r4 :: tuple[bool, short_int, object, object] + r5 :: short_int + r6 :: bool + r7, r8 :: object + r9 :: str + r10 :: union[int, str] k :: str v :: union[int, str] - r12, r13 :: object - r14 :: int - r15 :: object - r16 :: i32 - r17, r18, r19 :: bit + r11 :: object + r12 :: object[1] + r13 :: object_ptr + r14 :: object + r15 :: int + r16 :: object + r17 :: i32 + r18, r19, r20 :: bit L0: r0 = PyDict_New() new = r0 r1 = 0 r2 = PyDict_Size(d) - r3 = r2 << 1 - r4 = CPyDict_GetItemsIter(d) + r3 = CPyDict_GetItemsIter(d) L1: - r5 = CPyDict_NextItem(r4, r1) - r6 = r5[1] - r1 = r6 - r7 = r5[0] - if r7 goto L2 else goto L4 :: bool + r4 = CPyDict_NextItem(r3, r1) + r5 = r4[1] + r1 = r5 + r6 = r4[0] + if r6 goto L2 else goto L4 :: bool L2: - r8 = r5[2] - r9 = r5[3] - r10 = cast(str, r8) - r11 = cast(union[int, str], r9) - k = r10 - v = r11 - r12 = load_address PyLong_Type - r13 = PyObject_CallFunctionObjArgs(r12, v, 0) - r14 = unbox(int, r13) - r15 = box(int, r14) - r16 = CPyDict_SetItem(new, k, r15) - r17 = r16 >= 0 :: signed + r7 = r4[2] + r8 = r4[3] + r9 = cast(str, r7) + r10 = cast(union[int, str], r8) + k = r9 + v = r10 + r11 = load_address PyLong_Type + r12 = [v] + r13 = load_address r12 + r14 = PyObject_Vectorcall(r11, r13, 1, 0) + keep_alive v + r15 = unbox(int, r14) + r16 = box(int, r15) + r17 = CPyDict_SetItem(new, k, r16) + r18 = r17 >= 0 :: signed L3: - r18 = CPyDict_CheckSize(d, r3) + r19 = CPyDict_CheckSize(d, r2) goto L1 L4: - r19 = CPy_NoErrOccured() + r20 = CPy_NoErrOccurred() L5: return 1 def typeddict(d): d :: dict r0 :: short_int r1 :: native_int - r2 :: short_int - r3 :: object - r4 :: tuple[bool, short_int, object, object] - r5 :: short_int - r6 :: bool - r7, r8 :: object - r9, k :: str + r2 :: object + r3 :: tuple[bool, short_int, object, object] + r4 :: short_int + r5 :: bool + r6, r7 :: object + r8, k :: str v :: object - r10 :: str - r11 :: i32 - r12 :: bit - r13 :: object - r14, r15, r16 :: bit + r9 :: str + r10 :: bool name :: object - r17, r18 :: bit + r11, r12 :: bit L0: r0 = 0 r1 = PyDict_Size(d) - r2 = r1 << 1 - r3 = CPyDict_GetItemsIter(d) + r2 = CPyDict_GetItemsIter(d) L1: - r4 = CPyDict_NextItem(r3, r0) - r5 = r4[1] - r0 = r5 - r6 = r4[0] - if r6 goto L2 else goto L9 :: bool + r3 = CPyDict_NextItem(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r7 = r4[2] - r8 = r4[3] - r9 = cast(str, r7) - k = r9 - v = r8 - r10 = 'name' - r11 = PyUnicode_Compare(k, r10) - r12 = r11 == -1 - if r12 goto L3 else goto L5 :: bool + r6 = r3[2] + r7 = r3[3] + r8 = cast(str, r6) + k = r8 + v = r7 + r9 = 'name' + r10 = CPyStr_EqualLiteral(k, r9, 4) + if r10 goto L3 else goto L4 :: bool L3: - r13 = PyErr_Occurred() - r14 = r13 != 0 - if r14 goto L4 else goto L5 :: bool + name = v L4: - r15 = CPy_KeepPropagating() L5: - r16 = r11 == 0 - if r16 goto L6 else goto L7 :: bool + r11 = CPyDict_CheckSize(d, r1) + goto L1 L6: - name = v + r12 = CPy_NoErrOccurred() L7: -L8: - r17 = CPyDict_CheckSize(d, r2) - goto L1 -L9: - r18 = CPy_NoErrOccured() -L10: return 1 [case testDictLoadAddress] diff --git a/mypyc/test-data/irbuild-float.test b/mypyc/test-data/irbuild-float.test index 35e2eff62b863..d0fd32ffbdd70 100644 --- a/mypyc/test-data/irbuild-float.test +++ b/mypyc/test-data/irbuild-float.test @@ -219,7 +219,7 @@ L0: return r0 [case testFloatFinalConstant] -from typing_extensions import Final +from typing import Final X: Final = 123.0 Y: Final = -1.0 diff --git a/mypyc/test-data/irbuild-frozenset.test b/mypyc/test-data/irbuild-frozenset.test new file mode 100644 index 0000000000000..2fa84a2ed055d --- /dev/null +++ b/mypyc/test-data/irbuild-frozenset.test @@ -0,0 +1,115 @@ +[case testNewFrozenSet] +from typing import FrozenSet +def f() -> FrozenSet[int]: + return frozenset({1, 2, 3}) +[out] +def f(): + r0 :: set + r1 :: object + r2 :: i32 + r3 :: bit + r4 :: object + r5 :: i32 + r6 :: bit + r7 :: object + r8 :: i32 + r9 :: bit + r10 :: frozenset +L0: + r0 = PySet_New(0) + r1 = object 1 + r2 = PySet_Add(r0, r1) + r3 = r2 >= 0 :: signed + r4 = object 2 + r5 = PySet_Add(r0, r4) + r6 = r5 >= 0 :: signed + r7 = object 3 + r8 = PySet_Add(r0, r7) + r9 = r8 >= 0 :: signed + r10 = PyFrozenSet_New(r0) + return r10 + +[case testNewEmptyFrozenSet] +from typing import FrozenSet +def f1() -> FrozenSet[int]: + return frozenset() + +def f2() -> FrozenSet[int]: + return frozenset(()) +[out] +def f1(): + r0 :: frozenset +L0: + r0 = PyFrozenSet_New(0) + return r0 +def f2(): + r0 :: tuple[] + r1 :: object + r2 :: frozenset +L0: + r0 = () + r1 = box(tuple[], r0) + r2 = PyFrozenSet_New(r1) + return r2 + +[case testNewFrozenSetFromIterable] +from typing import FrozenSet, List, TypeVar + +T = TypeVar("T") + +def f(l: List[T]) -> FrozenSet[T]: + return frozenset(l) +[out] +def f(l): + l :: list + r0 :: frozenset +L0: + r0 = PyFrozenSet_New(l) + return r0 + +[case testFrozenSetSize] +from typing import FrozenSet +def f() -> int: + return len(frozenset((1, 2, 3))) +[out] +def f(): + r0 :: tuple[int, int, int] + r1 :: object + r2 :: frozenset + r3 :: ptr + r4 :: native_int + r5 :: short_int +L0: + r0 = (2, 4, 6) + r1 = box(tuple[int, int, int], r0) + r2 = PyFrozenSet_New(r1) + r3 = get_element_ptr r2 used :: PySetObject + r4 = load_mem r3 :: native_int* + keep_alive r2 + r5 = r4 << 1 + return r5 + +[case testFrozenSetContains] +from typing import FrozenSet +def f() -> bool: + x = frozenset((3, 4)) + return (5 in x) +[out] +def f(): + r0 :: tuple[int, int] + r1 :: object + r2, x :: frozenset + r3 :: object + r4 :: i32 + r5 :: bit + r6 :: bool +L0: + r0 = (6, 8) + r1 = box(tuple[int, int], r0) + r2 = PyFrozenSet_New(r1) + x = r2 + r3 = object 5 + r4 = PySet_Contains(x, r3) + r5 = r4 >= 0 :: signed + r6 = truncate r4: i32 to builtins.bool + return r6 diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 910148f80ddab..9ec29182e89b6 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -151,7 +151,7 @@ L3: [case testParamSpec] -from typing import Callable, ParamSpec, TypeVar +from typing import Callable, ParamSpec P = ParamSpec("P") @@ -166,25 +166,609 @@ execute(f, 1) def execute(func, args, kwargs): func :: object args :: tuple - kwargs :: dict - r0 :: list + kwargs, r0 :: dict r1 :: object - r2 :: dict - r3 :: i32 - r4 :: bit - r5 :: tuple - r6 :: object - r7 :: int + r2 :: int +L0: + r0 = PyDict_Copy(kwargs) + r1 = PyObject_Call(func, args, r0) + r2 = unbox(int, r1) + return r2 +def f(x): + x :: int +L0: + return x + +[case testTypeVarMappingBound] +# Dicts are special-cased for efficient iteration. +from typing import Dict, TypedDict, TypeVar, Union + +class TD(TypedDict): + foo: int + +M = TypeVar("M", bound=Dict[str, int]) +U = TypeVar("U", bound=Union[Dict[str, int], Dict[str, str]]) +T = TypeVar("T", bound=TD) + +def fn_mapping(m: M) -> None: + [x for x in m] + [x for x in m.values()] + {x for x in m.keys()} + {k: v for k, v in m.items()} + +def fn_union(m: U) -> None: + [x for x in m] + [x for x in m.values()] + {x for x in m.keys()} + {k: v for k, v in m.items()} + +def fn_typeddict(t: T) -> None: + [x for x in t] + [x for x in t.values()] + {x for x in t.keys()} + {k: v for k, v in t.items()} + +[typing fixtures/typing-full.pyi] +[out] +def fn_mapping(m): + m :: dict + r0 :: list + r1 :: short_int + r2 :: native_int + r3 :: object + r4 :: tuple[bool, short_int, object] + r5 :: short_int + r6 :: bool + r7 :: object + r8, x :: str + r9 :: i32 + r10, r11, r12 :: bit + r13 :: list + r14 :: short_int + r15 :: native_int + r16 :: object + r17 :: tuple[bool, short_int, object] + r18 :: short_int + r19 :: bool + r20 :: object + r21, x_2 :: int + r22 :: object + r23 :: i32 + r24, r25, r26 :: bit + r27 :: set + r28 :: short_int + r29 :: native_int + r30 :: object + r31 :: tuple[bool, short_int, object] + r32 :: short_int + r33 :: bool + r34 :: object + r35, x_3 :: str + r36 :: i32 + r37, r38, r39 :: bit + r40 :: dict + r41 :: short_int + r42 :: native_int + r43 :: object + r44 :: tuple[bool, short_int, object, object] + r45 :: short_int + r46 :: bool + r47, r48 :: object + r49 :: str + r50 :: int + k :: str + v :: int + r51 :: object + r52 :: i32 + r53, r54, r55 :: bit +L0: + r0 = PyList_New(0) + r1 = 0 + r2 = PyDict_Size(m) + r3 = CPyDict_GetKeysIter(m) +L1: + r4 = CPyDict_NextKey(r3, r1) + r5 = r4[1] + r1 = r5 + r6 = r4[0] + if r6 goto L2 else goto L4 :: bool +L2: + r7 = r4[2] + r8 = cast(str, r7) + x = r8 + r9 = PyList_Append(r0, x) + r10 = r9 >= 0 :: signed +L3: + r11 = CPyDict_CheckSize(m, r2) + goto L1 +L4: + r12 = CPy_NoErrOccurred() +L5: + r13 = PyList_New(0) + r14 = 0 + r15 = PyDict_Size(m) + r16 = CPyDict_GetValuesIter(m) +L6: + r17 = CPyDict_NextValue(r16, r14) + r18 = r17[1] + r14 = r18 + r19 = r17[0] + if r19 goto L7 else goto L9 :: bool +L7: + r20 = r17[2] + r21 = unbox(int, r20) + x_2 = r21 + r22 = box(int, x_2) + r23 = PyList_Append(r13, r22) + r24 = r23 >= 0 :: signed +L8: + r25 = CPyDict_CheckSize(m, r15) + goto L6 +L9: + r26 = CPy_NoErrOccurred() +L10: + r27 = PySet_New(0) + r28 = 0 + r29 = PyDict_Size(m) + r30 = CPyDict_GetKeysIter(m) +L11: + r31 = CPyDict_NextKey(r30, r28) + r32 = r31[1] + r28 = r32 + r33 = r31[0] + if r33 goto L12 else goto L14 :: bool +L12: + r34 = r31[2] + r35 = cast(str, r34) + x_3 = r35 + r36 = PySet_Add(r27, x_3) + r37 = r36 >= 0 :: signed +L13: + r38 = CPyDict_CheckSize(m, r29) + goto L11 +L14: + r39 = CPy_NoErrOccurred() +L15: + r40 = PyDict_New() + r41 = 0 + r42 = PyDict_Size(m) + r43 = CPyDict_GetItemsIter(m) +L16: + r44 = CPyDict_NextItem(r43, r41) + r45 = r44[1] + r41 = r45 + r46 = r44[0] + if r46 goto L17 else goto L19 :: bool +L17: + r47 = r44[2] + r48 = r44[3] + r49 = cast(str, r47) + r50 = unbox(int, r48) + k = r49 + v = r50 + r51 = box(int, v) + r52 = PyDict_SetItem(r40, k, r51) + r53 = r52 >= 0 :: signed +L18: + r54 = CPyDict_CheckSize(m, r42) + goto L16 +L19: + r55 = CPy_NoErrOccurred() +L20: + return 1 +def fn_union(m): + m :: dict + r0 :: list + r1 :: short_int + r2 :: native_int + r3 :: object + r4 :: tuple[bool, short_int, object] + r5 :: short_int + r6 :: bool + r7 :: object + r8, x :: str + r9 :: i32 + r10, r11, r12 :: bit + r13 :: list + r14 :: short_int + r15 :: native_int + r16 :: object + r17 :: tuple[bool, short_int, object] + r18 :: short_int + r19 :: bool + r20 :: object + r21, x_2 :: union[int, str] + r22 :: i32 + r23, r24, r25 :: bit + r26 :: set + r27 :: short_int + r28 :: native_int + r29 :: object + r30 :: tuple[bool, short_int, object] + r31 :: short_int + r32 :: bool + r33 :: object + r34, x_3 :: str + r35 :: i32 + r36, r37, r38 :: bit + r39 :: dict + r40 :: short_int + r41 :: native_int + r42 :: object + r43 :: tuple[bool, short_int, object, object] + r44 :: short_int + r45 :: bool + r46, r47 :: object + r48 :: str + r49 :: union[int, str] + k :: str + v :: union[int, str] + r50 :: i32 + r51, r52, r53 :: bit L0: r0 = PyList_New(0) - r1 = CPyList_Extend(r0, args) - r2 = PyDict_New() - r3 = CPyDict_UpdateInDisplay(r2, kwargs) - r4 = r3 >= 0 :: signed - r5 = PyList_AsTuple(r0) - r6 = PyObject_Call(func, r5, r2) - r7 = unbox(int, r6) - return r7 + r1 = 0 + r2 = PyDict_Size(m) + r3 = CPyDict_GetKeysIter(m) +L1: + r4 = CPyDict_NextKey(r3, r1) + r5 = r4[1] + r1 = r5 + r6 = r4[0] + if r6 goto L2 else goto L4 :: bool +L2: + r7 = r4[2] + r8 = cast(str, r7) + x = r8 + r9 = PyList_Append(r0, x) + r10 = r9 >= 0 :: signed +L3: + r11 = CPyDict_CheckSize(m, r2) + goto L1 +L4: + r12 = CPy_NoErrOccurred() +L5: + r13 = PyList_New(0) + r14 = 0 + r15 = PyDict_Size(m) + r16 = CPyDict_GetValuesIter(m) +L6: + r17 = CPyDict_NextValue(r16, r14) + r18 = r17[1] + r14 = r18 + r19 = r17[0] + if r19 goto L7 else goto L9 :: bool +L7: + r20 = r17[2] + r21 = cast(union[int, str], r20) + x_2 = r21 + r22 = PyList_Append(r13, x_2) + r23 = r22 >= 0 :: signed +L8: + r24 = CPyDict_CheckSize(m, r15) + goto L6 +L9: + r25 = CPy_NoErrOccurred() +L10: + r26 = PySet_New(0) + r27 = 0 + r28 = PyDict_Size(m) + r29 = CPyDict_GetKeysIter(m) +L11: + r30 = CPyDict_NextKey(r29, r27) + r31 = r30[1] + r27 = r31 + r32 = r30[0] + if r32 goto L12 else goto L14 :: bool +L12: + r33 = r30[2] + r34 = cast(str, r33) + x_3 = r34 + r35 = PySet_Add(r26, x_3) + r36 = r35 >= 0 :: signed +L13: + r37 = CPyDict_CheckSize(m, r28) + goto L11 +L14: + r38 = CPy_NoErrOccurred() +L15: + r39 = PyDict_New() + r40 = 0 + r41 = PyDict_Size(m) + r42 = CPyDict_GetItemsIter(m) +L16: + r43 = CPyDict_NextItem(r42, r40) + r44 = r43[1] + r40 = r44 + r45 = r43[0] + if r45 goto L17 else goto L19 :: bool +L17: + r46 = r43[2] + r47 = r43[3] + r48 = cast(str, r46) + r49 = cast(union[int, str], r47) + k = r48 + v = r49 + r50 = PyDict_SetItem(r39, k, v) + r51 = r50 >= 0 :: signed +L18: + r52 = CPyDict_CheckSize(m, r41) + goto L16 +L19: + r53 = CPy_NoErrOccurred() +L20: + return 1 +def fn_typeddict(t): + t :: dict + r0 :: list + r1 :: short_int + r2 :: native_int + r3 :: object + r4 :: tuple[bool, short_int, object] + r5 :: short_int + r6 :: bool + r7 :: object + r8, x :: str + r9 :: i32 + r10, r11, r12 :: bit + r13 :: list + r14 :: short_int + r15 :: native_int + r16 :: object + r17 :: tuple[bool, short_int, object] + r18 :: short_int + r19 :: bool + r20, x_2 :: object + r21 :: i32 + r22, r23, r24 :: bit + r25 :: set + r26 :: short_int + r27 :: native_int + r28 :: object + r29 :: tuple[bool, short_int, object] + r30 :: short_int + r31 :: bool + r32 :: object + r33, x_3 :: str + r34 :: i32 + r35, r36, r37 :: bit + r38 :: dict + r39 :: short_int + r40 :: native_int + r41 :: object + r42 :: tuple[bool, short_int, object, object] + r43 :: short_int + r44 :: bool + r45, r46 :: object + r47, k :: str + v :: object + r48 :: i32 + r49, r50, r51 :: bit +L0: + r0 = PyList_New(0) + r1 = 0 + r2 = PyDict_Size(t) + r3 = CPyDict_GetKeysIter(t) +L1: + r4 = CPyDict_NextKey(r3, r1) + r5 = r4[1] + r1 = r5 + r6 = r4[0] + if r6 goto L2 else goto L4 :: bool +L2: + r7 = r4[2] + r8 = cast(str, r7) + x = r8 + r9 = PyList_Append(r0, x) + r10 = r9 >= 0 :: signed +L3: + r11 = CPyDict_CheckSize(t, r2) + goto L1 +L4: + r12 = CPy_NoErrOccurred() +L5: + r13 = PyList_New(0) + r14 = 0 + r15 = PyDict_Size(t) + r16 = CPyDict_GetValuesIter(t) +L6: + r17 = CPyDict_NextValue(r16, r14) + r18 = r17[1] + r14 = r18 + r19 = r17[0] + if r19 goto L7 else goto L9 :: bool +L7: + r20 = r17[2] + x_2 = r20 + r21 = PyList_Append(r13, x_2) + r22 = r21 >= 0 :: signed +L8: + r23 = CPyDict_CheckSize(t, r15) + goto L6 +L9: + r24 = CPy_NoErrOccurred() +L10: + r25 = PySet_New(0) + r26 = 0 + r27 = PyDict_Size(t) + r28 = CPyDict_GetKeysIter(t) +L11: + r29 = CPyDict_NextKey(r28, r26) + r30 = r29[1] + r26 = r30 + r31 = r29[0] + if r31 goto L12 else goto L14 :: bool +L12: + r32 = r29[2] + r33 = cast(str, r32) + x_3 = r33 + r34 = PySet_Add(r25, x_3) + r35 = r34 >= 0 :: signed +L13: + r36 = CPyDict_CheckSize(t, r27) + goto L11 +L14: + r37 = CPy_NoErrOccurred() +L15: + r38 = PyDict_New() + r39 = 0 + r40 = PyDict_Size(t) + r41 = CPyDict_GetItemsIter(t) +L16: + r42 = CPyDict_NextItem(r41, r39) + r43 = r42[1] + r39 = r43 + r44 = r42[0] + if r44 goto L17 else goto L19 :: bool +L17: + r45 = r42[2] + r46 = r42[3] + r47 = cast(str, r45) + k = r47 + v = r46 + r48 = PyDict_SetItem(r38, k, v) + r49 = r48 >= 0 :: signed +L18: + r50 = CPyDict_CheckSize(t, r40) + goto L16 +L19: + r51 = CPy_NoErrOccurred() +L20: + return 1 + +[case testParamSpecComponentsAreUsable] +from typing import Callable, ParamSpec + +P = ParamSpec("P") + +def deco(func: Callable[P, int]) -> Callable[P, int]: + def inner(*args: P.args, **kwargs: P.kwargs) -> int: + can_listcomp = [x for x in args] + can_dictcomp = {k: v for k, v in kwargs.items()} + can_iter = list(kwargs) + can_use_keys = list(kwargs.keys()) + can_use_values = list(kwargs.values()) + return func(*args, **kwargs) + + return inner + +@deco +def f(x: int) -> int: + return x + +f(1) +[out] +def inner_deco_obj.__get__(__mypyc_self__, instance, owner): + __mypyc_self__, instance, owner, r0 :: object + r1 :: bit + r2 :: object +L0: + r0 = load_address _Py_NoneStruct + r1 = instance == r0 + if r1 goto L1 else goto L2 :: bool +L1: + return __mypyc_self__ +L2: + r2 = PyMethod_New(__mypyc_self__, instance) + return r2 +def inner_deco_obj.__call__(__mypyc_self__, args, kwargs): + __mypyc_self__ :: __main__.inner_deco_obj + args :: tuple + kwargs :: dict + r0 :: __main__.deco_env + r1 :: native_int + r2 :: list + r3 :: native_int + r4 :: bit + r5, x :: object + r6 :: native_int + can_listcomp :: list + r7 :: dict + r8 :: short_int + r9 :: native_int + r10 :: object + r11 :: tuple[bool, short_int, object, object] + r12 :: short_int + r13 :: bool + r14, r15 :: object + r16, k :: str + v :: object + r17 :: i32 + r18, r19, r20 :: bit + can_dictcomp :: dict + r21, can_iter, r22, can_use_keys, r23, can_use_values :: list + r24 :: object + r25 :: dict + r26 :: object + r27 :: int +L0: + r0 = __mypyc_self__.__mypyc_env__ + r1 = var_object_size args + r2 = PyList_New(r1) + r3 = 0 +L1: + r4 = r3 < r1 :: signed + if r4 goto L2 else goto L4 :: bool +L2: + r5 = CPySequenceTuple_GetItemUnsafe(args, r3) + x = r5 + CPyList_SetItemUnsafe(r2, r3, x) +L3: + r6 = r3 + 1 + r3 = r6 + goto L1 +L4: + can_listcomp = r2 + r7 = PyDict_New() + r8 = 0 + r9 = PyDict_Size(kwargs) + r10 = CPyDict_GetItemsIter(kwargs) +L5: + r11 = CPyDict_NextItem(r10, r8) + r12 = r11[1] + r8 = r12 + r13 = r11[0] + if r13 goto L6 else goto L8 :: bool +L6: + r14 = r11[2] + r15 = r11[3] + r16 = cast(str, r14) + k = r16 + v = r15 + r17 = PyDict_SetItem(r7, k, v) + r18 = r17 >= 0 :: signed +L7: + r19 = CPyDict_CheckSize(kwargs, r9) + goto L5 +L8: + r20 = CPy_NoErrOccurred() +L9: + can_dictcomp = r7 + r21 = PySequence_List(kwargs) + can_iter = r21 + r22 = CPyDict_Keys(kwargs) + can_use_keys = r22 + r23 = CPyDict_Values(kwargs) + can_use_values = r23 + r24 = r0.func + r25 = PyDict_Copy(kwargs) + r26 = PyObject_Call(r24, args, r25) + r27 = unbox(int, r26) + return r27 +def deco(func): + func :: object + r0 :: __main__.deco_env + r1 :: bool + r2 :: __main__.inner_deco_obj + r3 :: bool + inner :: object +L0: + r0 = deco_env() + r0.func = func; r1 = is_error + r2 = inner_deco_obj() + r2.__mypyc_env__ = r0; r3 = is_error + inner = r2 + return inner def f(x): x :: int L0: diff --git a/mypyc/test-data/irbuild-glue-methods.test b/mypyc/test-data/irbuild-glue-methods.test index 3012c79586f25..35e6be1283ebb 100644 --- a/mypyc/test-data/irbuild-glue-methods.test +++ b/mypyc/test-data/irbuild-glue-methods.test @@ -194,18 +194,24 @@ def DerivedProperty.next(self): self :: __main__.DerivedProperty r0 :: object r1 :: int - r2, r3, r4 :: object - r5 :: int - r6 :: __main__.DerivedProperty + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int + r8 :: __main__.DerivedProperty L0: r0 = self._incr_func r1 = self.value r2 = self._incr_func r3 = box(int, r1) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - r6 = DerivedProperty(r0, r5) - return r6 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + r8 = DerivedProperty(r0, r7) + return r8 def DerivedProperty.next__BaseProperty_glue(__mypyc_self__): __mypyc_self__, r0 :: __main__.DerivedProperty L0: @@ -224,24 +230,36 @@ def AgainProperty.next(self): self :: __main__.AgainProperty r0 :: object r1 :: int - r2, r3, r4 :: object - r5 :: int - r6, r7, r8 :: object - r9 :: int - r10 :: __main__.AgainProperty + r2, r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7 :: int + r8, r9 :: object + r10 :: object[1] + r11 :: object_ptr + r12 :: object + r13 :: int + r14 :: __main__.AgainProperty L0: r0 = self._incr_func r1 = self.value r2 = self._incr_func r3 = box(int, r1) - r4 = PyObject_CallFunctionObjArgs(r2, r3, 0) - r5 = unbox(int, r4) - r6 = self._incr_func - r7 = box(int, r5) - r8 = PyObject_CallFunctionObjArgs(r6, r7, 0) - r9 = unbox(int, r8) - r10 = AgainProperty(r0, r9) - return r10 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r2, r5, 1, 0) + keep_alive r3 + r7 = unbox(int, r6) + r8 = self._incr_func + r9 = box(int, r7) + r10 = [r9] + r11 = load_address r10 + r12 = PyObject_Vectorcall(r8, r11, 1, 0) + keep_alive r9 + r13 = unbox(int, r12) + r14 = AgainProperty(r0, r13) + return r14 def AgainProperty.next__DerivedProperty_glue(__mypyc_self__): __mypyc_self__, r0 :: __main__.AgainProperty L0: diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index a52de16f3a6cf..955f8b658f0e6 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -947,6 +947,8 @@ def f(x: i64) -> i64: elif not x: return 6 return 3 +def unary_not(x: i64) -> bool: + return not x [out] def f(x): x :: i64 @@ -964,6 +966,12 @@ L3: L4: L5: return 3 +def unary_not(x): + x :: i64 + r0 :: bit +L0: + r0 = x == 0 + return r0 [case testI64AssignMixed_64bit] from mypy_extensions import i64 @@ -1677,7 +1685,7 @@ L2: return 1 [case testI64FinalConstants] -from typing_extensions import Final +from typing import Final from mypy_extensions import i64 A: Final = -1 @@ -2046,27 +2054,21 @@ L2: return r6 def narrow2(x): x :: union[__main__.C, i64] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4 :: i64 - r5 :: __main__.C - r6 :: i64 + r0 :: bit + r1 :: i64 + r2 :: __main__.C + r3 :: i64 L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyLong_Check(x) + if r0 goto L1 else goto L2 :: bool L1: - r4 = unbox(i64, x) - return r4 + r1 = unbox(i64, x) + return r1 L2: - r5 = borrow cast(__main__.C, x) - r6 = r5.a + r2 = borrow cast(__main__.C, x) + r3 = r2.a keep_alive x - return r6 + return r3 [case testI64ConvertBetweenTuples_64bit] from __future__ import annotations diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index b1a712103e70b..184c66fafb7c3 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -116,7 +116,7 @@ L0: return r0 [case testFinalConstantFolding] -from typing_extensions import Final +from typing import Final X: Final = -1 Y: Final = -(1 + 3*2) @@ -182,13 +182,41 @@ 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 + +[case testIntBitLength] +def f(x: int) -> int: + return x.bit_length() +[out] +def f(x): + x, r0 :: int +L0: + r0 = CPyTagged_BitLength(x) + return r0 diff --git a/mypyc/test-data/irbuild-isinstance.test b/mypyc/test-data/irbuild-isinstance.test index 78da2e9c1e196..36a9300350bd3 100644 --- a/mypyc/test-data/irbuild-isinstance.test +++ b/mypyc/test-data/irbuild-isinstance.test @@ -4,16 +4,11 @@ def is_int(value: object) -> bool: [out] def is_int(value): - value, r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool + value :: object + r0 :: bit L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(value, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - return r3 + r0 = PyLong_Check(value) + return r0 [case testIsinstanceNotBool1] def is_not_bool(value: object) -> bool: @@ -21,17 +16,12 @@ def is_not_bool(value: object) -> bool: [out] def is_not_bool(value): - value, r0 :: object - r1 :: i32 - r2 :: bit - r3, r4 :: bool + value :: object + r0, r1 :: bit L0: - r0 = load_address PyBool_Type - r1 = PyObject_IsInstance(value, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - r4 = r3 ^ 1 - return r4 + r0 = PyBool_Check(value) + r1 = r0 ^ 1 + return r1 [case testIsinstanceIntAndNotBool] # This test is to ensure that 'value' doesn't get coerced to int when we are @@ -41,32 +31,22 @@ def is_not_bool_and_is_int(value: object) -> bool: [out] def is_not_bool_and_is_int(value): - value, r0 :: object - r1 :: i32 - r2 :: bit - r3, r4 :: bool - r5 :: object - r6 :: i32 - r7 :: bit - r8, r9 :: bool + value :: object + r0 :: bit + r1 :: bool + r2, r3 :: bit L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(value, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L2 else goto L1 :: bool + r0 = PyLong_Check(value) + if r0 goto L2 else goto L1 :: bool L1: - r4 = r3 + r1 = r0 goto L3 L2: - r5 = load_address PyBool_Type - r6 = PyObject_IsInstance(value, r5) - r7 = r6 >= 0 :: signed - r8 = truncate r6: i32 to builtins.bool - r9 = r8 ^ 1 - r4 = r9 + r2 = PyBool_Check(value) + r3 = r2 ^ 1 + r1 = r3 L3: - return r4 + return r1 [case testBorrowSpecialCaseWithIsinstance] class C: @@ -97,7 +77,7 @@ L0: x = r0 r1 = __main__.C :: type r2 = get_element_ptr x ob_type :: PyObject - r3 = load_mem r2 :: builtins.object* + r3 = borrow load_mem r2 :: builtins.object* keep_alive x r4 = r3 == r1 if r4 goto L1 else goto L2 :: bool @@ -107,3 +87,133 @@ L1: keep_alive x L2: return 1 + +[case testBytes] +from typing import Any + +def is_bytes(x: Any) -> bool: + return isinstance(x, bytes) + +def is_bytearray(x: Any) -> bool: + return isinstance(x, bytearray) + +[out] +def is_bytes(x): + x :: object + r0 :: bit +L0: + r0 = PyBytes_Check(x) + return r0 +def is_bytearray(x): + x :: object + r0 :: bit +L0: + r0 = PyByteArray_Check(x) + return r0 + +[case testDict] +from typing import Any + +def is_dict(x: Any) -> bool: + return isinstance(x, dict) + +[out] +def is_dict(x): + x :: object + r0 :: bit +L0: + r0 = PyDict_Check(x) + return r0 + +[case testFloat] +from typing import Any + +def is_float(x: Any) -> bool: + return isinstance(x, float) + +[out] +def is_float(x): + x :: object + r0 :: bit +L0: + r0 = PyFloat_Check(x) + return r0 + +[case testSet] +from typing import Any + +def is_set(x: Any) -> bool: + return isinstance(x, set) + +def is_frozenset(x: Any) -> bool: + return isinstance(x, frozenset) + +[out] +def is_set(x): + x :: object + r0 :: bit +L0: + r0 = PySet_Check(x) + return r0 +def is_frozenset(x): + x :: object + r0 :: bit +L0: + r0 = PyFrozenSet_Check(x) + return r0 + +[case testStr] +from typing import Any + +def is_str(x: Any) -> bool: + return isinstance(x, str) + +[out] +def is_str(x): + x :: object + r0 :: bit +L0: + r0 = PyUnicode_Check(x) + return r0 + +[case testTuple] +from typing import Any + +def is_tuple(x: Any) -> bool: + return isinstance(x, tuple) + +[out] +def is_tuple(x): + x :: object + r0 :: bit +L0: + r0 = PyTuple_Check(x) + return r0 + +[case testTupleOfPrimitives] +from typing import Any + +def is_instance(x: Any) -> bool: + return isinstance(x, (str, int, bytes)) + +[out] +def is_instance(x): + x :: object + r0, r1, r2 :: bit + r3 :: bool +L0: + r0 = PyUnicode_Check(x) + if r0 goto L3 else goto L1 :: bool +L1: + r1 = PyLong_Check(x) + if r1 goto L3 else goto L2 :: bool +L2: + r2 = PyBytes_Check(x) + if r2 goto L3 else goto L4 :: bool +L3: + r3 = 1 + goto L5 +L4: + r3 = 0 +L5: + return r3 diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 725f218b686a9..d864bfd19df26 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -145,6 +145,32 @@ L0: x = r10 return 1 +[case testListAdd] +from typing import List +def f(a: List[int], b: List[int]) -> None: + c = a + b +[out] +def f(a, b): + a, b, r0, c :: list +L0: + r0 = PySequence_Concat(a, b) + c = r0 + return 1 + +[case testListIAdd] +from typing import List, Any +def f(a: List[int], b: Any) -> None: + a += b +[out] +def f(a, b): + a :: list + b :: object + r0 :: list +L0: + r0 = PySequence_InPlaceConcat(a, b) + a = r0 + return 1 + [case testListMultiply] from typing import List def f(a: List[int]) -> None: @@ -168,6 +194,18 @@ L0: b = r4 return 1 +[case testListIMultiply] +from typing import List +def f(a: List[int]) -> None: + a *= 2 +[out] +def f(a): + a, r0 :: list +L0: + r0 = CPySequence_InPlaceMultiply(a, 4) + a = r0 + return 1 + [case testListLen] from typing import List def f(a: List[int]) -> int: @@ -182,6 +220,30 @@ L0: r1 = r0 << 1 return r1 +[case testListClear] +from typing import List +def f(l: List[int]) -> None: + return l.clear() +[out] +def f(l): + l :: list + r0 :: bit +L0: + r0 = CPyList_Clear(l) + return 1 + +[case testListCopy] +from typing import List +from typing import Any +def f(a: List[Any]) -> List[Any]: + return a.copy() +[out] +def f(a): + a, r0 :: list +L0: + r0 = CPyList_Copy(a) + return r0 + [case testListAppend] from typing import List def f(a: List[int], x: int) -> None: @@ -309,27 +371,21 @@ def f(source): source :: list r0 :: native_int r1 :: list - r2 :: short_int - r3 :: native_int - r4 :: short_int - r5 :: bit - r6 :: object - r7, x, r8 :: int - r9 :: object - r10 :: bit - r11 :: short_int + r2, r3 :: native_int + r4 :: bit + r5 :: object + r6, x, r7 :: int + r8 :: object + r9 :: native_int a :: list - r12 :: native_int - r13 :: list - r14 :: short_int - r15 :: native_int - r16 :: short_int - r17 :: bit + r10 :: native_int + r11 :: list + r12, r13 :: native_int + r14 :: bit + r15 :: object + r16, x_2, r17 :: int r18 :: object - r19, x_2, r20 :: int - r21 :: object - r22 :: bit - r23 :: short_int + r19 :: native_int b :: list L0: r0 = var_object_size source @@ -337,43 +393,41 @@ L0: r2 = 0 L1: r3 = var_object_size source - r4 = r3 << 1 - r5 = int_lt r2, r4 - if r5 goto L2 else goto L4 :: bool + r4 = r2 < r3 :: signed + if r4 goto L2 else goto L4 :: bool L2: - r6 = CPyList_GetItemUnsafe(source, r2) - r7 = unbox(int, r6) - x = r7 - r8 = CPyTagged_Add(x, 2) - r9 = box(int, r8) - r10 = CPyList_SetItemUnsafe(r1, r2, r9) + r5 = list_get_item_unsafe source, r2 + r6 = unbox(int, r5) + x = r6 + r7 = CPyTagged_Add(x, 2) + r8 = box(int, r7) + CPyList_SetItemUnsafe(r1, r2, r8) L3: - r11 = r2 + 2 - r2 = r11 + r9 = r2 + 1 + r2 = r9 goto L1 L4: a = r1 - r12 = var_object_size source - r13 = PyList_New(r12) - r14 = 0 + r10 = var_object_size source + r11 = PyList_New(r10) + r12 = 0 L5: - r15 = var_object_size source - r16 = r15 << 1 - r17 = int_lt r14, r16 - if r17 goto L6 else goto L8 :: bool + r13 = var_object_size source + r14 = r12 < r13 :: signed + if r14 goto L6 else goto L8 :: bool L6: - r18 = CPyList_GetItemUnsafe(source, r14) - r19 = unbox(int, r18) - x_2 = r19 - r20 = CPyTagged_Add(x_2, 2) - r21 = box(int, r20) - r22 = CPyList_SetItemUnsafe(r13, r14, r21) + r15 = list_get_item_unsafe source, r12 + r16 = unbox(int, r15) + x_2 = r16 + r17 = CPyTagged_Add(x_2, 2) + r18 = box(int, r17) + CPyList_SetItemUnsafe(r11, r12, r18) L7: - r23 = r14 + 2 - r14 = r23 + r19 = r12 + 1 + r12 = r19 goto L5 L8: - b = r13 + b = r11 return 1 [case testGeneratorNext] @@ -384,40 +438,37 @@ def test(x: List[int]) -> None: [out] def test(x): x :: list - r0 :: short_int - r1 :: native_int - r2 :: short_int - r3 :: bit - r4 :: object - r5, i :: int - r6 :: object - r7 :: union[int, None] - r8 :: short_int - r9 :: object + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4, i :: int + r5 :: object + r6 :: union[int, None] + r7 :: native_int + r8 :: object res :: union[int, None] L0: r0 = 0 L1: r1 = var_object_size x - r2 = r1 << 1 - r3 = int_lt r0, r2 - if r3 goto L2 else goto L4 :: bool + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(x, r0) - r5 = unbox(int, r4) - i = r5 - r6 = box(int, i) - r7 = r6 + r3 = list_get_item_unsafe x, r0 + r4 = unbox(int, r3) + i = r4 + r5 = box(int, i) + r6 = r5 goto L5 L3: - r8 = r0 + 2 - r0 = r8 + r7 = r0 + 1 + r0 = r7 goto L1 L4: - r9 = box(None, 1) - r7 = r9 + r8 = box(None, 1) + r6 = r8 L5: - res = r7 + res = r6 return 1 [case testSimplifyListUnion] @@ -436,78 +487,488 @@ def nested_union(a: Union[List[str], List[Optional[str]]]) -> None: [out] def narrow(a): a :: union[list, int] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4 :: list - r5 :: native_int - r6 :: short_int - r7 :: int + r0 :: bit + r1 :: list + r2 :: native_int + r3 :: short_int + r4 :: int L0: - r0 = load_address PyList_Type - r1 = PyObject_IsInstance(a, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyList_Check(a) + if r0 goto L1 else goto L2 :: bool L1: - r4 = borrow cast(list, a) - r5 = var_object_size r4 - r6 = r5 << 1 + r1 = borrow cast(list, a) + r2 = var_object_size r1 + r3 = r2 << 1 keep_alive a - return r6 + return r3 L2: - r7 = unbox(int, a) - return r7 + r4 = unbox(int, a) + return r4 def loop(a): a :: list - r0 :: short_int - r1 :: native_int - r2 :: short_int - r3 :: bit - r4 :: object - r5, x :: union[str, bytes] - r6 :: short_int + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4, x :: union[str, bytes] + r5 :: native_int L0: r0 = 0 L1: r1 = var_object_size a - r2 = r1 << 1 - r3 = int_lt r0, r2 - if r3 goto L2 else goto L4 :: bool + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(a, r0) - r5 = cast(union[str, bytes], r4) - x = r5 + r3 = list_get_item_unsafe a, r0 + r4 = cast(union[str, bytes], r3) + x = r4 L3: - r6 = r0 + 2 - r0 = r6 + r5 = r0 + 1 + r0 = r5 goto L1 L4: return 1 def nested_union(a): a :: list - r0 :: short_int - r1 :: native_int - r2 :: short_int - r3 :: bit - r4 :: object - r5, x :: union[str, None] - r6 :: short_int + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4, x :: union[str, None] + r5 :: native_int L0: r0 = 0 L1: r1 = var_object_size a - r2 = r1 << 1 - r3 = int_lt r0, r2 + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool +L2: + r3 = list_get_item_unsafe a, r0 + r4 = cast(union[str, None], r3) + x = r4 +L3: + r5 = r0 + 1 + r0 = r5 + goto L1 +L4: + return 1 + +[case testSorted] +from typing import List, Any +def list_sort(a: List[int]) -> None: + a.sort() +def sort_iterable(a: Any) -> None: + sorted(a) +[out] +def list_sort(a): + a :: list + r0 :: i32 + r1 :: bit +L0: + r0 = PyList_Sort(a) + r1 = r0 >= 0 :: signed + return 1 +def sort_iterable(a): + a :: object + r0 :: list +L0: + r0 = CPySequence_Sort(a) + return 1 + +[case testListBuiltFromStr] +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + source = "abc" + a = [f2(x) for x in source] +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0, source :: str + r1 :: native_int + r2 :: bit + r3 :: list + r4 :: native_int + r5 :: bit + r6, x, r7 :: str + r8 :: native_int + a :: list +L0: + r0 = 'abc' + source = r0 + r1 = CPyStr_Size_size_t(source) + r2 = r1 >= 0 :: signed + r3 = PyList_New(r1) + r4 = 0 +L1: + r5 = r4 < r1 :: signed + if r5 goto L2 else goto L4 :: bool +L2: + r6 = CPyStr_GetItemUnsafe(source, r4) + x = r6 + r7 = f2(x) + CPyList_SetItemUnsafe(r3, r4, r7) +L3: + r8 = r4 + 1 + r4 = r8 + goto L1 +L4: + a = r3 + return 1 + +[case testListBuiltFromStrExpr] +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = [f2(x) for x in "abc"] +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: list + r2 :: native_int + r3 :: bit + r4, x, r5 :: str + r6 :: native_int + a :: list +L0: + r0 = 'abc' + r1 = PyList_New(3) + r2 = 0 + goto L2 +L1: + r3 = r2 < 3 :: signed if r3 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(a, r0) - r5 = cast(union[str, None], r4) - x = r5 + r4 = CPyStr_GetItemUnsafe(r0, r2) + x = r4 + r5 = f2(x) + CPyList_SetItemUnsafe(r1, r2, r5) +L3: + r6 = r2 + 1 + r2 = r6 + goto L1 +L4: + a = r1 + return 1 + +[case testListBuiltFromFinalStr] +from typing import Final + +source: Final = "abc" + +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = [f2(x) for x in source] +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: list + r2 :: native_int + r3 :: bit + r4, x, r5 :: str + r6 :: native_int + a :: list +L0: + r0 = 'abc' + r1 = PyList_New(3) + r2 = 0 + goto L2 +L1: + r3 = r2 < 3 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = CPyStr_GetItemUnsafe(r0, r2) + x = r4 + r5 = f2(x) + CPyList_SetItemUnsafe(r1, r2, r5) +L3: + r6 = r2 + 1 + r2 = r6 + goto L1 +L4: + a = r1 + return 1 + +[case testListBuiltFromBytes_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + source = b"abc" + a = [f2(x) for x in source] + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0, source :: bytes + r1 :: native_int + r2 :: list + r3 :: native_int + r4, r5, r6 :: bit + r7, r8, r9, r10 :: int + r11 :: object + r12, x, r13 :: int + r14 :: object + r15 :: native_int + a :: list +L0: + r0 = b'abc' + source = r0 + r1 = var_object_size source + r2 = PyList_New(r1) + r3 = 0 +L1: + r4 = r3 < r1 :: signed + if r4 goto L2 else goto L8 :: bool +L2: + r5 = r3 <= 4611686018427387903 :: signed + if r5 goto L3 else goto L4 :: bool +L3: + r6 = r3 >= -4611686018427387904 :: signed + if r6 goto L5 else goto L4 :: bool +L4: + r7 = CPyTagged_FromInt64(r3) + r8 = r7 + goto L6 +L5: + r9 = r3 << 1 + r8 = r9 +L6: + r10 = CPyBytes_GetItem(source, r8) + r11 = box(int, r10) + r12 = unbox(int, r11) + x = r12 + r13 = f2(x) + r14 = box(int, r13) + CPyList_SetItemUnsafe(r2, r3, r14) +L7: + r15 = r3 + 1 + r3 = r15 + goto L1 +L8: + a = r2 + return 1 + +[case testListBuiltFromBytesExpr_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = [f2(x) for x in b"abc"] + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: list + r2 :: native_int + r3, r4, r5 :: bit + r6, r7, r8, r9 :: int + r10 :: object + r11, x, r12 :: int + r13 :: object + r14 :: native_int + a :: list +L0: + r0 = b'abc' + r1 = PyList_New(3) + r2 = 0 + goto L2 +L1: + r3 = r2 < 3 :: signed + if r3 goto L2 else goto L8 :: bool +L2: + r4 = r2 <= 4611686018427387903 :: signed + if r4 goto L3 else goto L4 :: bool +L3: + r5 = r2 >= -4611686018427387904 :: signed + if r5 goto L5 else goto L4 :: bool +L4: + r6 = CPyTagged_FromInt64(r2) + r7 = r6 + goto L6 +L5: + r8 = r2 << 1 + r7 = r8 +L6: + r9 = CPyBytes_GetItem(r0, r7) + r10 = box(int, r9) + r11 = unbox(int, r10) + x = r11 + r12 = f2(x) + r13 = box(int, r12) + CPyList_SetItemUnsafe(r1, r2, r13) +L7: + r14 = r2 + 1 + r2 = r14 + goto L1 +L8: + a = r1 + return 1 + +[case testListBuiltFromFinalBytes_64bit] +from typing import Final + +source: Final = b"abc" + +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = [f2(x) for x in source] + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: bool + r2 :: native_int + r3 :: list + r4 :: native_int + r5, r6, r7 :: bit + r8, r9, r10, r11 :: int + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: native_int + a :: list +L0: + r0 = __main__.source :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "source" was not set') + unreachable +L2: + r2 = var_object_size r0 + r3 = PyList_New(r2) + r4 = 0 +L3: + r5 = r4 < r2 :: signed + if r5 goto L4 else goto L10 :: bool +L4: + r6 = r4 <= 4611686018427387903 :: signed + if r6 goto L5 else goto L6 :: bool +L5: + r7 = r4 >= -4611686018427387904 :: signed + if r7 goto L7 else goto L6 :: bool +L6: + r8 = CPyTagged_FromInt64(r4) + r9 = r8 + goto L8 +L7: + r10 = r4 << 1 + r9 = r10 +L8: + r11 = CPyBytes_GetItem(r0, r9) + r12 = box(int, r11) + r13 = unbox(int, r12) + x = r13 + r14 = f2(x) + r15 = box(int, r14) + CPyList_SetItemUnsafe(r3, r4, r15) +L9: + r16 = r4 + 1 + r4 = r16 + goto L3 +L10: + a = r3 + return 1 + +[case testListBuiltFromStars] +from typing import Final + +abc: Final = "abc" + +def test() -> None: + a = [str(x) for x in [*abc, *"def", *b"ghi", ("j", "k"), *("l", "m", "n")]] + +[out] +def test(): + r0, r1 :: str + r2 :: bytes + r3, r4 :: str + r5 :: tuple[str, str] + r6, r7, r8 :: str + r9 :: tuple[str, str, str] + r10 :: list + r11, r12, r13, r14 :: object + r15 :: i32 + r16 :: bit + r17, r18 :: object + r19 :: list + r20, r21 :: native_int + r22 :: bit + r23, x :: object + r24 :: str + r25 :: native_int + a :: list +L0: + r0 = 'abc' + r1 = 'def' + r2 = b'ghi' + r3 = 'j' + r4 = 'k' + r5 = (r3, r4) + r6 = 'l' + r7 = 'm' + r8 = 'n' + r9 = (r6, r7, r8) + r10 = PyList_New(0) + r11 = CPyList_Extend(r10, r0) + r12 = CPyList_Extend(r10, r1) + r13 = CPyList_Extend(r10, r2) + r14 = box(tuple[str, str], r5) + r15 = PyList_Append(r10, r14) + r16 = r15 >= 0 :: signed + r17 = box(tuple[str, str, str], r9) + r18 = CPyList_Extend(r10, r17) + r19 = PyList_New(13) + r20 = 0 + goto L2 +L1: + r21 = var_object_size r10 + r22 = r20 < r21 :: signed + if r22 goto L2 else goto L4 :: bool +L2: + r23 = list_get_item_unsafe r10, r20 + x = r23 + r24 = PyObject_Str(x) + CPyList_SetItemUnsafe(r19, r20, r24) L3: - r6 = r0 + 2 - r0 = r6 + r25 = r20 + 1 + r20 = r25 goto L1 L4: + a = r19 return 1 diff --git a/mypyc/test-data/irbuild-match.test b/mypyc/test-data/irbuild-match.test index ab5a19624ba68..1e84c385100a7 100644 --- a/mypyc/test-data/irbuild-match.test +++ b/mypyc/test-data/irbuild-match.test @@ -23,7 +23,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L3 L2: @@ -61,7 +61,7 @@ L3: r5 = CPyObject_GetAttr(r3, r4) r6 = [r2] r7 = load_address r6 - r8 = _PyObject_Vectorcall(r5, r7, 1, 0) + r8 = PyObject_Vectorcall(r5, r7, 1, 0) keep_alive r2 goto L5 L4: @@ -105,7 +105,7 @@ L5: r7 = CPyObject_GetAttr(r5, r6) r8 = [r4] r9 = load_address r8 - r10 = _PyObject_Vectorcall(r7, r9, 1, 0) + r10 = PyObject_Vectorcall(r7, r9, 1, 0) keep_alive r4 goto L7 L6: @@ -141,14 +141,14 @@ L1: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L3 L2: L3: r10 = box(None, 1) return r10 -[case testMatchExaustivePattern_python3_10] +[case testMatchExhaustivePattern_python3_10] def f(): match 123: case _: @@ -170,7 +170,7 @@ L1: r3 = CPyObject_GetAttr(r1, r2) r4 = [r0] r5 = load_address r4 - r6 = _PyObject_Vectorcall(r3, r5, 1, 0) + r6 = PyObject_Vectorcall(r3, r5, 1, 0) keep_alive r0 goto L3 L2: @@ -212,7 +212,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L5 L2: @@ -225,7 +225,7 @@ L3: r12 = CPyObject_GetAttr(r10, r11) r13 = [r9] r14 = load_address r13 - r15 = _PyObject_Vectorcall(r12, r14, 1, 0) + r15 = PyObject_Vectorcall(r12, r14, 1, 0) keep_alive r9 goto L5 L4: @@ -278,7 +278,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L9 L2: @@ -296,7 +296,7 @@ L5: r13 = CPyObject_GetAttr(r11, r12) r14 = [r10] r15 = load_address r14 - r16 = _PyObject_Vectorcall(r13, r15, 1, 0) + r16 = PyObject_Vectorcall(r13, r15, 1, 0) keep_alive r10 goto L9 L6: @@ -309,7 +309,7 @@ L7: r21 = CPyObject_GetAttr(r19, r20) r22 = [r18] r23 = load_address r22 - r24 = _PyObject_Vectorcall(r21, r23, 1, 0) + r24 = PyObject_Vectorcall(r21, r23, 1, 0) keep_alive r18 goto L9 L8: @@ -344,7 +344,7 @@ L2: r4 = CPyObject_GetAttr(r2, r3) r5 = [r1] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive r1 goto L4 L3: @@ -400,7 +400,7 @@ L1: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L7 L2: @@ -415,7 +415,7 @@ L3: r16 = CPyObject_GetAttr(r14, r15) r17 = [r13] r18 = load_address r17 - r19 = _PyObject_Vectorcall(r16, r18, 1, 0) + r19 = PyObject_Vectorcall(r16, r18, 1, 0) keep_alive r13 goto L7 L4: @@ -430,7 +430,7 @@ L5: r26 = CPyObject_GetAttr(r24, r25) r27 = [r23] r28 = load_address r27 - r29 = _PyObject_Vectorcall(r26, r28, 1, 0) + r29 = PyObject_Vectorcall(r26, r28, 1, 0) keep_alive r23 goto L7 L6: @@ -471,7 +471,7 @@ L3: r7 = CPyObject_GetAttr(r5, r6) r8 = [r4] r9 = load_address r8 - r10 = _PyObject_Vectorcall(r7, r9, 1, 0) + r10 = PyObject_Vectorcall(r7, r9, 1, 0) keep_alive r4 goto L5 L4: @@ -504,7 +504,7 @@ L1: r4 = CPyObject_GetAttr(r2, r3) r5 = [x] r6 = load_address r5 - r7 = _PyObject_Vectorcall(r4, r6, 1, 0) + r7 = PyObject_Vectorcall(r4, r6, 1, 0) keep_alive x goto L3 L2: @@ -546,7 +546,7 @@ L3: r6 = CPyObject_GetAttr(r4, r5) r7 = [x] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive x goto L5 L4: @@ -563,10 +563,9 @@ def f(): def f(): r0, r1 :: object r2 :: bool - i :: int - r3 :: object - r4 :: str - r5, r6 :: object + r3, i, r4 :: object + r5 :: str + r6 :: object r7 :: object[1] r8 :: object_ptr r9, r10 :: object @@ -576,21 +575,22 @@ L0: r2 = CPy_TypeCheck(r1, r0) if r2 goto L1 else goto L3 :: bool L1: - i = 246 + r3 = object 123 + i = r3 L2: - r3 = builtins :: module - r4 = 'print' - r5 = CPyObject_GetAttr(r3, r4) - r6 = box(int, i) - r7 = [r6] + r4 = builtins :: module + r5 = 'print' + r6 = CPyObject_GetAttr(r4, r5) + r7 = [i] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r5, r8, 1, 0) - keep_alive r6 + r9 = PyObject_Vectorcall(r6, r8, 1, 0) + keep_alive i goto L4 L3: L4: r10 = box(None, 1) return r10 + [case testMatchClassPatternWithPositionalArgs_python3_10] class Position: __match_args__ = ("x", "y", "z") @@ -599,7 +599,7 @@ class Position: y: int z: int -def f(x): +def f(x) -> None: match x: case Position(1, 2, 3): print("matched") @@ -641,7 +641,7 @@ def f(x): r28 :: object r29 :: object[1] r30 :: object_ptr - r31, r32 :: object + r31 :: object L0: r0 = __main__.Position :: type r1 = PyObject_IsInstance(x, r0) @@ -682,13 +682,13 @@ L4: r28 = CPyObject_GetAttr(r26, r27) r29 = [r25] r30 = load_address r29 - r31 = _PyObject_Vectorcall(r28, r30, 1, 0) + r31 = PyObject_Vectorcall(r28, r30, 1, 0) keep_alive r25 goto L6 L5: L6: - r32 = box(None, 1) - return r32 + return 1 + [case testMatchClassPatternWithKeywordPatterns_python3_10] class Position: x: int @@ -767,7 +767,7 @@ L4: r28 = CPyObject_GetAttr(r26, r27) r29 = [r25] r30 = load_address r29 - r31 = _PyObject_Vectorcall(r28, r30, 1, 0) + r31 = PyObject_Vectorcall(r28, r30, 1, 0) keep_alive r25 goto L6 L5: @@ -835,7 +835,7 @@ L4: r19 = CPyObject_GetAttr(r17, r18) r20 = [r16] r21 = load_address r20 - r22 = _PyObject_Vectorcall(r19, r21, 1, 0) + r22 = PyObject_Vectorcall(r19, r21, 1, 0) keep_alive r16 goto L6 L5: @@ -848,7 +848,7 @@ class C: a: int b: int -def f(x): +def f(x) -> None: match x: case C(1, 2) as y: print("matched") @@ -885,7 +885,7 @@ def f(x): r22 :: object r23 :: object[1] r24 :: object_ptr - r25, r26 :: object + r25 :: object L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) @@ -920,13 +920,13 @@ L4: r22 = CPyObject_GetAttr(r20, r21) r23 = [r19] r24 = load_address r23 - r25 = _PyObject_Vectorcall(r22, r24, 1, 0) + r25 = PyObject_Vectorcall(r22, r24, 1, 0) keep_alive r19 goto L6 L5: L6: - r26 = box(None, 1) - return r26 + return 1 + [case testMatchClassPatternPositionalCapture_python3_10] class C: __match_args__ = ("x",) @@ -953,15 +953,14 @@ def f(x): r2 :: bit r3 :: bool r4 :: str - r5 :: object - r6, num :: int - r7 :: str - r8 :: object - r9 :: str - r10 :: object - r11 :: object[1] - r12 :: object_ptr - r13, r14 :: object + r5, num :: object + r6 :: str + r7 :: object + r8 :: str + r9 :: object + r10 :: object[1] + r11 :: object_ptr + r12, r13 :: object L0: r0 = __main__.C :: type r1 = PyObject_IsInstance(x, r0) @@ -971,22 +970,22 @@ L0: L1: r4 = 'x' r5 = CPyObject_GetAttr(x, r4) - r6 = unbox(int, r5) - num = r6 + num = r5 L2: - r7 = 'matched' - r8 = builtins :: module - r9 = 'print' - r10 = CPyObject_GetAttr(r8, r9) - r11 = [r7] - r12 = load_address r11 - r13 = _PyObject_Vectorcall(r10, r12, 1, 0) - keep_alive r7 + r6 = 'matched' + r7 = builtins :: module + r8 = 'print' + r9 = CPyObject_GetAttr(r7, r8) + r10 = [r6] + r11 = load_address r10 + r12 = PyObject_Vectorcall(r9, r11, 1, 0) + keep_alive r6 goto L4 L3: L4: - r14 = box(None, 1) - return r14 + r13 = box(None, 1) + return r13 + [case testMatchMappingEmpty_python3_10] def f(x): match x: @@ -1015,7 +1014,7 @@ L1: r5 = CPyObject_GetAttr(r3, r4) r6 = [r2] r7 = load_address r6 - r8 = _PyObject_Vectorcall(r5, r7, 1, 0) + r8 = PyObject_Vectorcall(r5, r7, 1, 0) keep_alive r2 goto L3 L2: @@ -1072,7 +1071,7 @@ L3: r14 = CPyObject_GetAttr(r12, r13) r15 = [r11] r16 = load_address r15 - r17 = _PyObject_Vectorcall(r14, r16, 1, 0) + r17 = PyObject_Vectorcall(r14, r16, 1, 0) keep_alive r11 goto L5 L4: @@ -1111,7 +1110,7 @@ L2: r6 = CPyObject_GetAttr(r4, r5) r7 = [r3] r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) + r9 = PyObject_Vectorcall(r6, r8, 1, 0) keep_alive r3 goto L4 L3: @@ -1176,7 +1175,7 @@ L4: r17 = CPyObject_GetAttr(r15, r16) r18 = [r14] r19 = load_address r18 - r20 = _PyObject_Vectorcall(r17, r19, 1, 0) + r20 = PyObject_Vectorcall(r17, r19, 1, 0) keep_alive r14 goto L6 L5: @@ -1218,7 +1217,7 @@ L2: r8 = CPyObject_GetAttr(r6, r7) r9 = [r5] r10 = load_address r9 - r11 = _PyObject_Vectorcall(r8, r10, 1, 0) + r11 = PyObject_Vectorcall(r8, r10, 1, 0) keep_alive r5 goto L4 L3: @@ -1284,7 +1283,7 @@ L4: r20 = CPyObject_GetAttr(r18, r19) r21 = [r17] r22 = load_address r21 - r23 = _PyObject_Vectorcall(r20, r22, 1, 0) + r23 = PyObject_Vectorcall(r20, r22, 1, 0) keep_alive r17 goto L6 L5: @@ -1350,7 +1349,7 @@ L4: r20 = CPyObject_GetAttr(r18, r19) r21 = [r17] r22 = load_address r21 - r23 = _PyObject_Vectorcall(r20, r22, 1, 0) + r23 = PyObject_Vectorcall(r20, r22, 1, 0) keep_alive r17 goto L6 L5: @@ -1378,14 +1377,15 @@ def f(x): r15 :: bit r16 :: bool r17 :: native_int - r18, rest :: object - r19 :: str - r20 :: object - r21 :: str - r22 :: object - r23 :: object[1] - r24 :: object_ptr - r25, r26 :: object + r18 :: object + r19, rest :: list + r20 :: str + r21 :: object + r22 :: str + r23 :: object + r24 :: object[1] + r25 :: object_ptr + r26, r27 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1414,21 +1414,23 @@ L3: L4: r17 = r2 - 0 r18 = PySequence_GetSlice(x, 2, r17) - rest = r18 + r19 = cast(list, r18) + rest = r19 L5: - r19 = 'matched' - r20 = builtins :: module - r21 = 'print' - r22 = CPyObject_GetAttr(r20, r21) - r23 = [r19] - r24 = load_address r23 - r25 = _PyObject_Vectorcall(r22, r24, 1, 0) - keep_alive r19 + r20 = 'matched' + r21 = builtins :: module + r22 = 'print' + r23 = CPyObject_GetAttr(r21, r22) + r24 = [r20] + r25 = load_address r24 + r26 = PyObject_Vectorcall(r23, r25, 1, 0) + keep_alive r20 goto L7 L6: L7: - r26 = box(None, 1) - return r26 + r27 = box(None, 1) + return r27 + [case testMatchSequenceWithStarPatternInTheMiddle_python3_10] def f(x): match x: @@ -1455,14 +1457,15 @@ def f(x): r16 :: bit r17 :: bool r18 :: native_int - r19, rest :: object - r20 :: str - r21 :: object - r22 :: str - r23 :: object - r24 :: object[1] - r25 :: object_ptr - r26, r27 :: object + r19 :: object + r20, rest :: list + r21 :: str + r22 :: object + r23 :: str + r24 :: object + r25 :: object[1] + r26 :: object_ptr + r27, r28 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1492,21 +1495,23 @@ L3: L4: r18 = r2 - 1 r19 = PySequence_GetSlice(x, 1, r18) - rest = r19 + r20 = cast(list, r19) + rest = r20 L5: - r20 = 'matched' - r21 = builtins :: module - r22 = 'print' - r23 = CPyObject_GetAttr(r21, r22) - r24 = [r20] - r25 = load_address r24 - r26 = _PyObject_Vectorcall(r23, r25, 1, 0) - keep_alive r20 + r21 = 'matched' + r22 = builtins :: module + r23 = 'print' + r24 = CPyObject_GetAttr(r22, r23) + r25 = [r21] + r26 = load_address r25 + r27 = PyObject_Vectorcall(r24, r26, 1, 0) + keep_alive r21 goto L7 L6: L7: - r27 = box(None, 1) - return r27 + r28 = box(None, 1) + return r28 + [case testMatchSequenceWithStarPatternAtTheStart_python3_10] def f(x): match x: @@ -1530,14 +1535,15 @@ def f(x): r17 :: bit r18 :: bool r19 :: native_int - r20, rest :: object - r21 :: str - r22 :: object - r23 :: str - r24 :: object - r25 :: object[1] - r26 :: object_ptr - r27, r28 :: object + r20 :: object + r21, rest :: list + r22 :: str + r23 :: object + r24 :: str + r25 :: object + r26 :: object[1] + r27 :: object_ptr + r28, r29 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1568,21 +1574,23 @@ L3: L4: r19 = r2 - 2 r20 = PySequence_GetSlice(x, 0, r19) - rest = r20 + r21 = cast(list, r20) + rest = r21 L5: - r21 = 'matched' - r22 = builtins :: module - r23 = 'print' - r24 = CPyObject_GetAttr(r22, r23) - r25 = [r21] - r26 = load_address r25 - r27 = _PyObject_Vectorcall(r24, r26, 1, 0) - keep_alive r21 + r22 = 'matched' + r23 = builtins :: module + r24 = 'print' + r25 = CPyObject_GetAttr(r23, r24) + r26 = [r22] + r27 = load_address r26 + r28 = PyObject_Vectorcall(r25, r27, 1, 0) + keep_alive r22 goto L7 L6: L7: - r28 = box(None, 1) - return r28 + r29 = box(None, 1) + return r29 + [case testMatchBuiltinClassPattern_python3_10] def f(x): match x: @@ -1592,35 +1600,35 @@ def f(x): def f(x): x, r0 :: object r1 :: bool - r2, y :: int - r3 :: str - r4 :: object - r5 :: str - r6 :: object - r7 :: object[1] - r8 :: object_ptr - r9, r10 :: object + y :: object + r2 :: str + r3 :: object + r4 :: str + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8, r9 :: object L0: r0 = load_address PyLong_Type r1 = CPy_TypeCheck(x, r0) if r1 goto L1 else goto L3 :: bool L1: - r2 = unbox(int, x) - y = r2 + y = x L2: - r3 = 'matched' - r4 = builtins :: module - r5 = 'print' - r6 = CPyObject_GetAttr(r4, r5) - r7 = [r3] - r8 = load_address r7 - r9 = _PyObject_Vectorcall(r6, r8, 1, 0) - keep_alive r3 + r2 = 'matched' + r3 = builtins :: module + r4 = 'print' + r5 = CPyObject_GetAttr(r3, r4) + r6 = [r2] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r2 goto L4 L3: L4: - r10 = box(None, 1) - return r10 + r9 = box(None, 1) + return r9 + [case testMatchSequenceCaptureAll_python3_10] def f(x): match x: @@ -1634,14 +1642,15 @@ def f(x): r2 :: native_int r3, r4 :: bit r5 :: native_int - r6, rest :: object - r7 :: str - r8 :: object - r9 :: str - r10 :: object - r11 :: object[1] - r12 :: object_ptr - r13, r14 :: object + r6 :: object + r7, rest :: list + r8 :: str + r9 :: object + r10 :: str + r11 :: object + r12 :: object[1] + r13 :: object_ptr + r14, r15 :: object L0: r0 = CPySequence_Check(x) r1 = r0 != 0 @@ -1654,21 +1663,23 @@ L1: L2: r5 = r2 - 0 r6 = PySequence_GetSlice(x, 0, r5) - rest = r6 + r7 = cast(list, r6) + rest = r7 L3: - r7 = 'matched' - r8 = builtins :: module - r9 = 'print' - r10 = CPyObject_GetAttr(r8, r9) - r11 = [r7] - r12 = load_address r11 - r13 = _PyObject_Vectorcall(r10, r12, 1, 0) - keep_alive r7 + r8 = 'matched' + r9 = builtins :: module + r10 = 'print' + r11 = CPyObject_GetAttr(r9, r10) + r12 = [r8] + r13 = load_address r12 + r14 = PyObject_Vectorcall(r11, r13, 1, 0) + keep_alive r8 goto L5 L4: L5: - r14 = box(None, 1) - return r14 + r15 = box(None, 1) + return r15 + [case testMatchTypeAnnotatedNativeClass_python3_10] class A: a: int @@ -1715,3 +1726,81 @@ L4: L5: L6: unreachable + +[case testMatchLiteralMatchArgs_python3_10] +from typing import Literal + +class Foo: + __match_args__: tuple[Literal["foo"]] = ("foo",) + foo: str + +def f(x: Foo) -> None: + match x: + case Foo(foo): + print("foo") + case _: + assert False, "Unreachable" +[out] +def Foo.__mypyc_defaults_setup(__mypyc_self__): + __mypyc_self__ :: __main__.Foo + r0 :: str + r1 :: tuple[str] +L0: + r0 = 'foo' + r1 = (r0) + __mypyc_self__.__match_args__ = r1 + return 1 +def f(x): + x :: __main__.Foo + r0 :: object + r1 :: i32 + r2 :: bit + r3 :: bool + r4 :: str + r5 :: object + r6, foo, r7 :: str + r8 :: object + r9 :: str + r10 :: object + r11 :: object[1] + r12 :: object_ptr + r13, r14 :: object + r15 :: i32 + r16 :: bit + r17, r18 :: bool +L0: + r0 = __main__.Foo :: type + r1 = PyObject_IsInstance(x, r0) + r2 = r1 >= 0 :: signed + r3 = truncate r1: i32 to builtins.bool + if r3 goto L1 else goto L3 :: bool +L1: + r4 = 'foo' + r5 = CPyObject_GetAttr(x, r4) + r6 = cast(str, r5) + foo = r6 +L2: + r7 = 'foo' + r8 = builtins :: module + r9 = 'print' + r10 = CPyObject_GetAttr(r8, r9) + r11 = [r7] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r10, r12, 1, 0) + keep_alive r7 + goto L8 +L3: +L4: + r14 = box(bool, 0) + r15 = PyObject_IsTrue(r14) + r16 = r15 >= 0 :: signed + r17 = truncate r15: i32 to builtins.bool + if r17 goto L6 else goto L5 :: bool +L5: + r18 = raise AssertionError('Unreachable') + unreachable +L6: + goto L8 +L7: +L8: + return 1 diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 62ae6eb9ee354..1b390e9c35043 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -194,23 +194,33 @@ def d(num): r2 :: bool inner :: object r3 :: str - r4 :: object - r5, a, r6 :: str - r7 :: object - r8, b :: str + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7, a, r8 :: str + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12, b :: str L0: r0 = d_env() r1 = inner_d_obj() r1.__mypyc_env__ = r0; r2 = is_error inner = r1 r3 = 'one' - r4 = PyObject_CallFunctionObjArgs(inner, r3, 0) - r5 = cast(str, r4) - a = r5 - r6 = 'two' - r7 = PyObject_CallFunctionObjArgs(inner, r6, 0) - r8 = cast(str, r7) - b = r8 + r4 = [r3] + r5 = load_address r4 + r6 = PyObject_Vectorcall(inner, r5, 1, 0) + keep_alive r3 + r7 = cast(str, r6) + a = r7 + r8 = 'two' + r9 = [r8] + r10 = load_address r9 + r11 = PyObject_Vectorcall(inner, r10, 1, 0) + keep_alive r8 + r12 = cast(str, r11) + b = r12 return a def inner(): r0 :: str @@ -290,7 +300,7 @@ L0: r2 = inner_a_obj() r2.__mypyc_env__ = r0; r3 = is_error inner = r2 - r4 = PyObject_CallFunctionObjArgs(inner, 0) + r4 = PyObject_Vectorcall(inner, 0, 0, 0) r5 = unbox(int, r4) return r5 def inner_b_obj.__get__(__mypyc_self__, instance, owner): @@ -330,7 +340,7 @@ L0: r2 = inner_b_obj() r2.__mypyc_env__ = r0; r3 = is_error inner = r2 - r4 = PyObject_CallFunctionObjArgs(inner, 0) + r4 = PyObject_Vectorcall(inner, 0, 0, 0) r5 = unbox(int, r4) r6 = r0.num r7 = CPyTagged_Add(r5, r6) @@ -400,7 +410,7 @@ L2: r3.__mypyc_env__ = r0; r4 = is_error inner = r3 L3: - r5 = PyObject_CallFunctionObjArgs(inner, 0) + r5 = PyObject_Vectorcall(inner, 0, 0, 0) r6 = cast(str, r5) return r6 @@ -472,7 +482,7 @@ L0: r6 = c_a_b_obj() r6.__mypyc_env__ = r1; r7 = is_error c = r6 - r8 = PyObject_CallFunctionObjArgs(c, 0) + r8 = PyObject_Vectorcall(c, 0, 0, 0) r9 = unbox(int, r8) return r9 def a(): @@ -488,7 +498,7 @@ L0: r2 = b_a_obj() r2.__mypyc_env__ = r0; r3 = is_error b = r2 - r4 = PyObject_CallFunctionObjArgs(b, 0) + r4 = PyObject_Vectorcall(b, 0, 0, 0) r5 = unbox(int, r4) return r5 @@ -567,7 +577,7 @@ L2: r3.__mypyc_env__ = r0; r4 = is_error inner = r3 L3: - r5 = PyObject_CallFunctionObjArgs(inner, 0) + r5 = PyObject_Vectorcall(inner, 0, 0, 0) r6 = cast(str, r5) return r6 @@ -632,7 +642,7 @@ def bar_f_obj.__call__(__mypyc_self__): L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.foo - r2 = PyObject_CallFunctionObjArgs(r1, 0) + r2 = PyObject_Vectorcall(r1, 0, 0, 0) r3 = unbox(int, r2) return r3 def baz_f_obj.__get__(__mypyc_self__, instance, owner): @@ -654,8 +664,11 @@ def baz_f_obj.__call__(__mypyc_self__, n): r0 :: __main__.f_env r1 :: bit r2 :: int - r3, r4, r5 :: object - r6, r7 :: int + r3, r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object + r8, r9 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = int_eq n, 0 @@ -666,10 +679,13 @@ L2: r2 = CPyTagged_Subtract(n, 2) r3 = r0.baz r4 = box(int, r2) - r5 = PyObject_CallFunctionObjArgs(r3, r4, 0) - r6 = unbox(int, r5) - r7 = CPyTagged_Add(n, r6) - return r7 + r5 = [r4] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r3, r6, 1, 0) + keep_alive r4 + r8 = unbox(int, r7) + r9 = CPyTagged_Add(n, r8) + return r9 def f(a): a :: int r0 :: __main__.f_env @@ -682,8 +698,11 @@ def f(a): r9, r10 :: bool r11, r12 :: object r13, r14 :: int - r15, r16, r17 :: object - r18, r19 :: int + r15, r16 :: object + r17 :: object[1] + r18 :: object_ptr + r19 :: object + r20, r21 :: int L0: r0 = f_env() r0.a = a; r1 = is_error @@ -697,15 +716,18 @@ L0: r8.__mypyc_env__ = r0; r9 = is_error r0.baz = r8; r10 = is_error r11 = r0.bar - r12 = PyObject_CallFunctionObjArgs(r11, 0) + r12 = PyObject_Vectorcall(r11, 0, 0, 0) r13 = unbox(int, r12) r14 = r0.a r15 = r0.baz r16 = box(int, r14) - r17 = PyObject_CallFunctionObjArgs(r15, r16, 0) - r18 = unbox(int, r17) - r19 = CPyTagged_Add(r13, r18) - return r19 + r17 = [r16] + r18 = load_address r17 + r19 = PyObject_Vectorcall(r15, r18, 1, 0) + keep_alive r16 + r20 = unbox(int, r19) + r21 = CPyTagged_Add(r13, r20) + return r21 [case testLambdas] def f(x: int, y: int) -> None: @@ -753,12 +775,18 @@ def __mypyc_lambda__1_f_obj.__call__(__mypyc_self__, a, b): __mypyc_self__ :: __main__.__mypyc_lambda__1_f_obj a, b :: object r0 :: __main__.f_env - r1, r2 :: object + r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.s - r2 = PyObject_CallFunctionObjArgs(r1, a, b, 0) - return r2 + r2 = [a, b] + r3 = load_address r2 + r4 = PyObject_Vectorcall(r1, r3, 2, 0) + keep_alive a, b + return r4 def f(x, y): x, y :: int r0 :: __main__.f_env @@ -766,8 +794,11 @@ def f(x, y): r2, r3 :: bool r4 :: __main__.__mypyc_lambda__1_f_obj r5 :: bool - t, r6, r7, r8 :: object - r9 :: None + t, r6, r7 :: object + r8 :: object[2] + r9 :: object_ptr + r10 :: object + r11 :: None L0: r0 = f_env() r1 = __mypyc_lambda__0_f_obj() @@ -778,9 +809,12 @@ L0: t = r4 r6 = box(int, x) r7 = box(int, y) - r8 = PyObject_CallFunctionObjArgs(t, r6, r7, 0) - r9 = unbox(None, r8) - return r9 + r8 = [r6, r7] + r9 = load_address r8 + r10 = PyObject_Vectorcall(t, r9, 2, 0) + keep_alive r6, r7 + r11 = unbox(None, r10) + return r11 [case testRecursiveFunction] from typing import Callable diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 75c0085869995..fbf7cb148b089 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -251,28 +251,22 @@ def f(x: Union[int, A]) -> int: [out] def f(x): x :: union[int, __main__.A] - r0 :: object - r1 :: i32 - r2 :: bit - r3 :: bool - r4, r5 :: int - r6 :: __main__.A - r7 :: int + r0 :: bit + r1, r2 :: int + r3 :: __main__.A + r4 :: int L0: - r0 = load_address PyLong_Type - r1 = PyObject_IsInstance(x, r0) - r2 = r1 >= 0 :: signed - r3 = truncate r1: i32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = PyLong_Check(x) + if r0 goto L1 else goto L2 :: bool L1: - r4 = unbox(int, x) - r5 = CPyTagged_Add(r4, 2) - return r5 + r1 = unbox(int, x) + r2 = CPyTagged_Add(r1, 2) + return r2 L2: - r6 = borrow cast(__main__.A, x) - r7 = r6.a + r3 = borrow cast(__main__.A, x) + r4 = r3.a keep_alive x - return r7 + return r4 L3: unreachable @@ -317,7 +311,7 @@ def get(o): L0: r0 = __main__.A :: type r1 = get_element_ptr o ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive o r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -396,7 +390,7 @@ def g(o): L0: r0 = __main__.A :: type r1 = get_element_ptr o ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive o r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -409,7 +403,7 @@ L1: L2: r8 = __main__.B :: type r9 = get_element_ptr o ob_type :: PyObject - r10 = load_mem r9 :: builtins.object* + r10 = borrow load_mem r9 :: builtins.object* keep_alive o r11 = r10 == r8 if r11 goto L3 else goto L4 :: bool @@ -462,7 +456,7 @@ def f(o): L0: r0 = __main__.A :: type r1 = get_element_ptr o ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive o r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool @@ -494,7 +488,7 @@ def g(o): L0: r0 = __main__.A :: type r1 = get_element_ptr o ob_type :: PyObject - r2 = load_mem r1 :: builtins.object* + r2 = borrow load_mem r1 :: builtins.object* keep_alive o r3 = r2 == r0 if r3 goto L1 else goto L2 :: bool diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 110801b78a660..5586a2bf4cfb4 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -85,16 +85,14 @@ def test1(): r4 :: ptr tmp_list :: list r5 :: set - r6 :: short_int - r7 :: native_int - r8 :: short_int - r9 :: bit - r10 :: object - r11, x, r12 :: int - r13 :: object - r14 :: i32 - r15 :: bit - r16 :: short_int + r6, r7 :: native_int + r8 :: bit + r9 :: object + r10, x, r11 :: int + r12 :: object + r13 :: i32 + r14 :: bit + r15 :: native_int a :: set L0: r0 = PyList_New(3) @@ -111,20 +109,19 @@ L0: r6 = 0 L1: r7 = var_object_size tmp_list - r8 = r7 << 1 - r9 = int_lt r6, r8 - if r9 goto L2 else goto L4 :: bool + r8 = r6 < r7 :: signed + if r8 goto L2 else goto L4 :: bool L2: - r10 = CPyList_GetItemUnsafe(tmp_list, r6) - r11 = unbox(int, r10) - x = r11 - r12 = f(x) - r13 = box(int, r12) - r14 = PySet_Add(r5, r13) - r15 = r14 >= 0 :: signed + r9 = list_get_item_unsafe tmp_list, r6 + r10 = unbox(int, r9) + x = r10 + r11 = f(x) + r12 = box(int, r11) + r13 = PySet_Add(r5, r12) + r14 = r13 >= 0 :: signed L3: - r16 = r6 + 2 - r6 = r16 + r15 = r6 + 1 + r6 = r15 goto L1 L4: a = r5 @@ -157,7 +154,7 @@ L2: L3: goto L1 L4: - r10 = CPy_NoErrOccured() + r10 = CPy_NoErrOccurred() L5: b = r1 return 1 @@ -168,16 +165,15 @@ def test3(): r7 :: set r8 :: short_int r9 :: native_int - r10 :: short_int - r11 :: object - r12 :: tuple[bool, short_int, object] - r13 :: short_int - r14 :: bool - r15 :: object - r16, x, r17 :: int - r18 :: object - r19 :: i32 - r20, r21, r22 :: bit + r10 :: object + r11 :: tuple[bool, short_int, object] + r12 :: short_int + r13 :: bool + r14 :: object + r15, x, r16 :: int + r17 :: object + r18 :: i32 + r19, r20, r21 :: bit c :: set L0: r0 = '1' @@ -191,27 +187,26 @@ L0: r7 = PySet_New(0) r8 = 0 r9 = PyDict_Size(tmp_dict) - r10 = r9 << 1 - r11 = CPyDict_GetKeysIter(tmp_dict) + r10 = CPyDict_GetKeysIter(tmp_dict) L1: - r12 = CPyDict_NextKey(r11, r8) - r13 = r12[1] - r8 = r13 - r14 = r12[0] - if r14 goto L2 else goto L4 :: bool + r11 = CPyDict_NextKey(r10, r8) + r12 = r11[1] + r8 = r12 + r13 = r11[0] + if r13 goto L2 else goto L4 :: bool L2: - r15 = r12[2] - r16 = unbox(int, r15) - x = r16 - r17 = f(x) - r18 = box(int, r17) - r19 = PySet_Add(r7, r18) - r20 = r19 >= 0 :: signed + r14 = r11[2] + r15 = unbox(int, r14) + x = r15 + r16 = f(x) + r17 = box(int, r16) + r18 = PySet_Add(r7, r17) + r19 = r18 >= 0 :: signed L3: - r21 = CPyDict_CheckSize(tmp_dict, r10) + r20 = CPyDict_CheckSize(tmp_dict, r9) goto L1 L4: - r22 = CPy_NoErrOccured() + r21 = CPy_NoErrOccurred() L5: c = r7 return 1 @@ -313,28 +308,26 @@ def test(): tmp_list :: list r7 :: set r8, r9 :: list - r10 :: short_int - r11 :: native_int - r12 :: short_int - r13 :: bit - r14 :: object - r15, z :: int - r16 :: bit - r17 :: int - r18 :: object - r19 :: i32 - r20 :: bit - r21 :: short_int - r22, r23, r24 :: object - r25, y, r26 :: int - r27 :: object - r28 :: i32 - r29, r30 :: bit - r31, r32, r33 :: object - r34, x, r35 :: int - r36 :: object - r37 :: i32 - r38, r39 :: bit + r10, r11 :: native_int + r12 :: bit + r13 :: object + r14, z :: int + r15 :: bit + r16 :: int + r17 :: object + r18 :: i32 + r19 :: bit + r20 :: native_int + r21, r22, r23 :: object + r24, y, r25 :: int + r26 :: object + r27 :: i32 + r28, r29 :: bit + r30, r31, r32 :: object + r33, x, r34 :: int + r35 :: object + r36 :: i32 + r37, r38 :: bit a :: set L0: r0 = PyList_New(5) @@ -357,60 +350,59 @@ L0: r10 = 0 L1: r11 = var_object_size tmp_list - r12 = r11 << 1 - r13 = int_lt r10, r12 - if r13 goto L2 else goto L6 :: bool + r12 = r10 < r11 :: signed + if r12 goto L2 else goto L6 :: bool L2: - r14 = CPyList_GetItemUnsafe(tmp_list, r10) - r15 = unbox(int, r14) - z = r15 - r16 = int_lt z, 8 - if r16 goto L4 else goto L3 :: bool + r13 = list_get_item_unsafe tmp_list, r10 + r14 = unbox(int, r13) + z = r14 + r15 = int_lt z, 8 + if r15 goto L4 else goto L3 :: bool L3: goto L5 L4: - r17 = f1(z) - r18 = box(int, r17) - r19 = PyList_Append(r9, r18) - r20 = r19 >= 0 :: signed + r16 = f1(z) + r17 = box(int, r16) + r18 = PyList_Append(r9, r17) + r19 = r18 >= 0 :: signed L5: - r21 = r10 + 2 - r10 = r21 + r20 = r10 + 1 + r10 = r20 goto L1 L6: - r22 = PyObject_GetIter(r9) - r23 = PyObject_GetIter(r22) + r21 = PyObject_GetIter(r9) + r22 = PyObject_GetIter(r21) L7: - r24 = PyIter_Next(r23) - if is_error(r24) goto L10 else goto L8 + r23 = PyIter_Next(r22) + if is_error(r23) goto L10 else goto L8 L8: - r25 = unbox(int, r24) - y = r25 - r26 = f2(y) - r27 = box(int, r26) - r28 = PyList_Append(r8, r27) - r29 = r28 >= 0 :: signed + r24 = unbox(int, r23) + y = r24 + r25 = f2(y) + r26 = box(int, r25) + r27 = PyList_Append(r8, r26) + r28 = r27 >= 0 :: signed L9: goto L7 L10: - r30 = CPy_NoErrOccured() + r29 = CPy_NoErrOccurred() L11: - r31 = PyObject_GetIter(r8) - r32 = PyObject_GetIter(r31) + r30 = PyObject_GetIter(r8) + r31 = PyObject_GetIter(r30) L12: - r33 = PyIter_Next(r32) - if is_error(r33) goto L15 else goto L13 + r32 = PyIter_Next(r31) + if is_error(r32) goto L15 else goto L13 L13: - r34 = unbox(int, r33) - x = r34 - r35 = f3(x) - r36 = box(int, r35) - r37 = PySet_Add(r7, r36) - r38 = r37 >= 0 :: signed + r33 = unbox(int, r32) + x = r33 + r34 = f3(x) + r35 = box(int, r34) + r36 = PySet_Add(r7, r35) + r37 = r36 >= 0 :: signed L14: goto L12 L15: - r39 = CPy_NoErrOccured() + r38 = CPy_NoErrOccurred() L16: a = r7 return 1 @@ -628,7 +620,7 @@ L0: return r0 [case testOperatorInSetLiteral] -from typing_extensions import Final +from typing import Final CONST: Final = "daylily" non_const = 10 @@ -686,7 +678,7 @@ def not_precomputed_nested_set(i): r1 :: object r2 :: i32 r3 :: bit - r4 :: object + r4 :: frozenset r5 :: set r6 :: i32 r7 :: bit @@ -716,7 +708,7 @@ L0: return r14 [case testForSetLiteral] -from typing_extensions import Final +from typing import Final CONST: Final = 10 non_const = 20 @@ -752,7 +744,7 @@ L2: L3: goto L1 L4: - r4 = CPy_NoErrOccured() + r4 = CPy_NoErrOccurred() L5: return 1 def precomputed2(): @@ -770,7 +762,7 @@ L2: L3: goto L1 L4: - r3 = CPy_NoErrOccured() + r3 = CPy_NoErrOccurred() L5: return 1 def not_precomputed(): @@ -804,6 +796,6 @@ L2: L3: goto L1 L4: - r11 = CPy_NoErrOccured() + r11 = CPy_NoErrOccurred() L5: return 1 diff --git a/mypyc/test-data/irbuild-singledispatch.test b/mypyc/test-data/irbuild-singledispatch.test index e1053397546f5..1060ee63c57df 100644 --- a/mypyc/test-data/irbuild-singledispatch.test +++ b/mypyc/test-data/irbuild-singledispatch.test @@ -38,22 +38,26 @@ def f_obj.__call__(__mypyc_self__, arg): r8 :: str r9 :: object r10 :: dict - r11 :: object - r12 :: i32 - r13 :: bit - r14 :: object - r15 :: ptr + r11 :: object[2] + r12 :: object_ptr + r13 :: object + r14 :: i32 + r15 :: bit r16 :: object - r17 :: bit - r18 :: int + r17 :: ptr + r18 :: object r19 :: bit r20 :: int - r21 :: bool - r22 :: object + r21 :: bit + r22 :: int r23 :: bool + r24 :: object[1] + r25 :: object_ptr + r26 :: object + r27 :: bool L0: r0 = get_element_ptr arg ob_type :: PyObject - r1 = load_mem r0 :: builtins.object* + r1 = borrow load_mem r0 :: builtins.object* keep_alive arg r2 = __mypyc_self__.dispatch_cache r3 = CPyDict_GetWithNone(r2, r1) @@ -68,31 +72,37 @@ L2: r8 = '_find_impl' r9 = CPyObject_GetAttr(r7, r8) r10 = __mypyc_self__.registry - r11 = PyObject_CallFunctionObjArgs(r9, r1, r10, 0) - r12 = CPyDict_SetItem(r2, r1, r11) - r13 = r12 >= 0 :: signed - r6 = r11 + r11 = [r1, r10] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r9, r12, 2, 0) + keep_alive r1, r10 + r14 = PyDict_SetItem(r2, r1, r13) + r15 = r14 >= 0 :: signed + r6 = r13 L3: - r14 = load_address PyLong_Type - r15 = get_element_ptr r6 ob_type :: PyObject - r16 = load_mem r15 :: builtins.object* + r16 = load_address PyLong_Type + r17 = get_element_ptr r6 ob_type :: PyObject + r18 = borrow load_mem r17 :: builtins.object* keep_alive r6 - r17 = r16 == r14 - if r17 goto L4 else goto L7 :: bool + r19 = r18 == r16 + if r19 goto L4 else goto L7 :: bool L4: - r18 = unbox(int, r6) - r19 = int_eq r18, 0 - if r19 goto L5 else goto L6 :: bool + r20 = unbox(int, r6) + r21 = int_eq r20, 0 + if r21 goto L5 else goto L6 :: bool L5: - r20 = unbox(int, arg) - r21 = g(r20) - return r21 + r22 = unbox(int, arg) + r23 = g(r22) + return r23 L6: unreachable L7: - r22 = PyObject_CallFunctionObjArgs(r6, arg, 0) - r23 = unbox(bool, r22) - return r23 + r24 = [arg] + r25 = load_address r24 + r26 = PyObject_Vectorcall(r6, r25, 1, 0) + keep_alive arg + r27 = unbox(bool, r26) + return r27 def f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -129,7 +139,6 @@ def g(arg): L0: return 1 - [case testCallsToSingledispatchFunctionsAreNative] from functools import singledispatch @@ -170,19 +179,23 @@ def f_obj.__call__(__mypyc_self__, x): r8 :: str r9 :: object r10 :: dict - r11 :: object - r12 :: i32 - r13 :: bit - r14 :: object - r15 :: ptr + r11 :: object[2] + r12 :: object_ptr + r13 :: object + r14 :: i32 + r15 :: bit r16 :: object - r17 :: bit - r18 :: int - r19 :: object - r20 :: None + r17 :: ptr + r18 :: object + r19 :: bit + r20 :: int + r21 :: object[1] + r22 :: object_ptr + r23 :: object + r24 :: None L0: r0 = get_element_ptr x ob_type :: PyObject - r1 = load_mem r0 :: builtins.object* + r1 = borrow load_mem r0 :: builtins.object* keep_alive x r2 = __mypyc_self__.dispatch_cache r3 = CPyDict_GetWithNone(r2, r1) @@ -197,24 +210,30 @@ L2: r8 = '_find_impl' r9 = CPyObject_GetAttr(r7, r8) r10 = __mypyc_self__.registry - r11 = PyObject_CallFunctionObjArgs(r9, r1, r10, 0) - r12 = CPyDict_SetItem(r2, r1, r11) - r13 = r12 >= 0 :: signed - r6 = r11 + r11 = [r1, r10] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r9, r12, 2, 0) + keep_alive r1, r10 + r14 = PyDict_SetItem(r2, r1, r13) + r15 = r14 >= 0 :: signed + r6 = r13 L3: - r14 = load_address PyLong_Type - r15 = get_element_ptr r6 ob_type :: PyObject - r16 = load_mem r15 :: builtins.object* + r16 = load_address PyLong_Type + r17 = get_element_ptr r6 ob_type :: PyObject + r18 = borrow load_mem r17 :: builtins.object* keep_alive r6 - r17 = r16 == r14 - if r17 goto L4 else goto L5 :: bool + r19 = r18 == r16 + if r19 goto L4 else goto L5 :: bool L4: - r18 = unbox(int, r6) + r20 = unbox(int, r6) unreachable L5: - r19 = PyObject_CallFunctionObjArgs(r6, x, 0) - r20 = unbox(None, r19) - return r20 + r21 = [x] + r22 = load_address r21 + r23 = PyObject_Vectorcall(r6, r22, 1, 0) + keep_alive x + r24 = unbox(None, r23) + return r24 def f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bit @@ -255,3 +274,58 @@ L0: r1 = f(r0) r2 = box(None, 1) return r2 + +[case registerNestedFunctionError] +from functools import singledispatch +from typing import Any, overload + +def dec(x: Any) -> Any: + return x + +def f() -> None: + @singledispatch # E: Nested singledispatch functions not supported + def singledispatch_in_func(x: Any) -> None: + pass + +@dec +def g() -> None: + @singledispatch # E: Nested singledispatch functions not supported + def singledispatch_in_decorated(x: Any) -> None: + pass + +@overload +def h(x: int) -> None: + pass +@overload +def h(x: str) -> None: + pass +def h(x: Any) -> None: + @singledispatch # E: Nested singledispatch functions not supported + def singledispatch_in_overload(x: Any) -> None: + pass + +@singledispatch +def outside(x: Any) -> None: + pass + +def i() -> None: + @outside.register # E: Registering nested functions not supported + def register_in_func(x: int) -> None: + pass + +@dec +def j() -> None: + @outside.register # E: Registering nested functions not supported + def register_in_decorated(x: int) -> None: + pass + +@overload +def k(x: int) -> None: + pass +@overload +def k(x: str) -> None: + pass +def k(x: Any) -> None: + @outside.register # E: Registering nested functions not supported + def register_in_overload(x: int) -> None: + pass diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index f9d3354b317c2..48b8e0e318b8b 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -230,30 +230,27 @@ def f(ls: List[int]) -> int: def f(ls): ls :: list y :: int - r0 :: short_int - r1 :: native_int - r2 :: short_int - r3 :: bit - r4 :: object - r5, x, r6 :: int - r7 :: short_int + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4, x, r5 :: int + r6 :: native_int L0: y = 0 r0 = 0 L1: r1 = var_object_size ls - r2 = r1 << 1 - r3 = int_lt r0, r2 - if r3 goto L2 else goto L4 :: bool + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItemUnsafe(ls, r0) - r5 = unbox(int, r4) - x = r5 - r6 = CPyTagged_Add(y, x) - y = r6 + r3 = list_get_item_unsafe ls, r0 + r4 = unbox(int, r3) + x = r4 + r5 = CPyTagged_Add(y, x) + y = r5 L3: - r7 = r0 + 2 - r0 = r7 + r6 = r0 + 1 + r0 = r6 goto L1 L4: return y @@ -269,39 +266,37 @@ def f(d): d :: dict r0 :: short_int r1 :: native_int - r2 :: short_int - r3 :: object - r4 :: tuple[bool, short_int, object] - r5 :: short_int - r6 :: bool - r7 :: object - r8, key :: int - r9, r10 :: object - r11 :: int - r12, r13 :: bit + r2 :: object + r3 :: tuple[bool, short_int, object] + r4 :: short_int + r5 :: bool + r6 :: object + r7, key :: int + r8, r9 :: object + r10 :: int + r11, r12 :: bit L0: r0 = 0 r1 = PyDict_Size(d) - r2 = r1 << 1 - r3 = CPyDict_GetKeysIter(d) + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r0) - r5 = r4[1] - r0 = r5 - r6 = r4[0] - if r6 goto L2 else goto L4 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L4 :: bool L2: - r7 = r4[2] - r8 = unbox(int, r7) - key = r8 - r9 = box(int, key) - r10 = CPyDict_GetItem(d, r9) - r11 = unbox(int, r10) + r6 = r3[2] + r7 = unbox(int, r6) + key = r7 + r8 = box(int, key) + r9 = CPyDict_GetItem(d, r8) + r10 = unbox(int, r9) L3: - r12 = CPyDict_CheckSize(d, r2) + r11 = CPyDict_CheckSize(d, r1) goto L1 L4: - r13 = CPy_NoErrOccured() + r12 = CPy_NoErrOccurred() L5: return 1 @@ -321,54 +316,52 @@ def sum_over_even_values(d): s :: int r0 :: short_int r1 :: native_int - r2 :: short_int - r3 :: object - r4 :: tuple[bool, short_int, object] - r5 :: short_int - r6 :: bool - r7 :: object - r8, key :: int - r9, r10 :: object - r11, r12 :: int - r13 :: bit - r14, r15 :: object - r16, r17 :: int - r18, r19 :: bit + r2 :: object + r3 :: tuple[bool, short_int, object] + r4 :: short_int + r5 :: bool + r6 :: object + r7, key :: int + r8, r9 :: object + r10, r11 :: int + r12 :: bit + r13, r14 :: object + r15, r16 :: int + r17, r18 :: bit L0: s = 0 r0 = 0 r1 = PyDict_Size(d) - r2 = r1 << 1 - r3 = CPyDict_GetKeysIter(d) + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r0) - r5 = r4[1] - r0 = r5 - r6 = r4[0] - if r6 goto L2 else goto L6 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r7 = r4[2] - r8 = unbox(int, r7) - key = r8 - r9 = box(int, key) - r10 = CPyDict_GetItem(d, r9) - r11 = unbox(int, r10) - r12 = CPyTagged_Remainder(r11, 4) - r13 = r12 != 0 - if r13 goto L3 else goto L4 :: bool + r6 = r3[2] + r7 = unbox(int, r6) + key = r7 + r8 = box(int, key) + r9 = CPyDict_GetItem(d, r8) + r10 = unbox(int, r9) + r11 = CPyTagged_Remainder(r10, 4) + r12 = r11 != 0 + if r12 goto L3 else goto L4 :: bool L3: goto L5 L4: - r14 = box(int, key) - r15 = CPyDict_GetItem(d, r14) - r16 = unbox(int, r15) - r17 = CPyTagged_Add(s, r16) - s = r17 + r13 = box(int, key) + r14 = CPyDict_GetItem(d, r13) + r15 = unbox(int, r14) + r16 = CPyTagged_Add(s, r15) + s = r16 L5: - r18 = CPyDict_CheckSize(d, r2) + r17 = CPyDict_CheckSize(d, r1) goto L1 L6: - r19 = CPy_NoErrOccured() + r18 = CPy_NoErrOccurred() L7: return s @@ -492,19 +485,21 @@ def from_any(a: Any) -> None: [out] def from_tuple(t): t :: tuple[int, object] - r0, r1 :: int - r2, x, r3, r4 :: object + r0 :: int + r1 :: object + r2 :: int + r3, x, r4 :: object r5, y :: int L0: r0 = borrow t[0] - r1 = unborrow r0 - r2 = box(int, r1) - x = r2 - r3 = borrow t[1] - r4 = unborrow r3 + r1 = borrow t[1] + keep_alive steal t + r2 = unborrow r0 + r3 = box(int, r2) + x = r3 + r4 = unborrow r1 r5 = unbox(int, r4) y = r5 - keep_alive steal t return 1 def from_any(a): a, r0, r1 :: object @@ -594,17 +589,17 @@ def f(l, t): L0: r0 = CPySequence_CheckUnpackCount(l, 2) r1 = r0 >= 0 :: signed - r2 = CPyList_GetItemUnsafe(l, 0) - r3 = CPyList_GetItemUnsafe(l, 2) + r2 = list_get_item_unsafe l, 0 + r3 = list_get_item_unsafe l, 1 x = r2 r4 = unbox(int, r3) y = r4 r5 = CPySequence_CheckUnpackCount(t, 2) r6 = r5 >= 0 :: signed - r7 = CPySequenceTuple_GetItem(t, 0) - r8 = CPySequenceTuple_GetItem(t, 2) - r9 = unbox(int, r8) + r7 = CPySequenceTuple_GetItemUnsafe(t, 0) + r8 = CPySequenceTuple_GetItemUnsafe(t, 1) x = r7 + r9 = unbox(int, r8) y = r9 return 1 @@ -655,7 +650,10 @@ def complex_msg(x, s): r3 :: bit r4 :: object r5 :: str - r6, r7 :: object + r6 :: object + r7 :: object[1] + r8 :: object_ptr + r9 :: object L0: r0 = load_address _Py_NoneStruct r1 = x != r0 @@ -668,8 +666,11 @@ L2: r4 = builtins :: module r5 = 'AssertionError' r6 = CPyObject_GetAttr(r4, r5) - r7 = PyObject_CallFunctionObjArgs(r6, s, 0) - CPy_Raise(r7) + r7 = [s] + r8 = load_address r7 + r9 = PyObject_Vectorcall(r6, r8, 1, 0) + keep_alive s + CPy_Raise(r9) unreachable L3: return 1 @@ -865,34 +866,31 @@ def g(x: Iterable[int]) -> None: def f(a): a :: list r0 :: short_int + r1, r2 :: native_int + r3 :: bit i :: int - r1 :: short_int - r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, x, r7 :: int - r8, r9 :: short_int + r4 :: object + r5, x, r6 :: int + r7 :: short_int + r8 :: native_int L0: r0 = 0 - i = 0 r1 = 0 L1: r2 = var_object_size a - r3 = r2 << 1 - r4 = int_lt r1, r3 - if r4 goto L2 else goto L4 :: bool + r3 = r1 < r2 :: signed + if r3 goto L2 else goto L4 :: bool L2: - r5 = CPyList_GetItemUnsafe(a, r1) - r6 = unbox(int, r5) - x = r6 - r7 = CPyTagged_Add(i, x) + i = r0 + r4 = list_get_item_unsafe a, r1 + r5 = unbox(int, r4) + x = r5 + r6 = CPyTagged_Add(i, x) L3: - r8 = r0 + 2 - r0 = r8 - i = r8 - r9 = r1 + 2 - r1 = r9 + r7 = r0 + 2 + r0 = r7 + r8 = r1 + 1 + r1 = r8 goto L1 L4: L5: @@ -900,28 +898,26 @@ 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() + r5 = CPy_NoErrOccurred() L5: return 1 @@ -940,66 +936,65 @@ def g(a: Iterable[bool], b: List[int]) -> None: def f(a, b): a :: list b :: object - r0 :: short_int + r0 :: native_int r1 :: object r2 :: native_int - r3 :: short_int - r4 :: bit - r5, r6 :: object - r7, x :: int - r8, y :: bool - r9 :: i32 - r10 :: bit - r11 :: bool - r12 :: short_int - r13 :: bit + r3 :: bit + r4, r5 :: object + r6, x :: int + r7, y :: bool + r8 :: i32 + r9 :: bit + r10 :: bool + r11 :: native_int + r12 :: bit L0: r0 = 0 r1 = PyObject_GetIter(b) L1: r2 = var_object_size a - r3 = r2 << 1 - r4 = int_lt r0, r3 - if r4 goto L2 else goto L7 :: bool + r3 = r0 < r2 :: signed + if r3 goto L2 else goto L7 :: bool L2: - r5 = PyIter_Next(r1) - if is_error(r5) goto L7 else goto L3 + r4 = PyIter_Next(r1) + if is_error(r4) goto L7 else goto L3 L3: - r6 = CPyList_GetItemUnsafe(a, r0) - r7 = unbox(int, r6) - x = r7 - r8 = unbox(bool, r5) - y = r8 - r9 = PyObject_IsTrue(b) - r10 = r9 >= 0 :: signed - r11 = truncate r9: i32 to builtins.bool - if r11 goto L4 else goto L5 :: bool + r5 = list_get_item_unsafe a, r0 + r6 = unbox(int, r5) + x = r6 + r7 = unbox(bool, r4) + y = r7 + r8 = PyObject_IsTrue(b) + r9 = r8 >= 0 :: signed + r10 = truncate r8: i32 to builtins.bool + if r10 goto L4 else goto L5 :: bool L4: x = 2 L5: L6: - r12 = r0 + 2 - r0 = r12 + r11 = r0 + 1 + r0 = r11 goto L1 L7: - r13 = CPy_NoErrOccured() + r12 = CPy_NoErrOccurred() L8: return 1 def g(a, b): a :: object b :: list r0 :: object - r1, r2 :: short_int + r1 :: native_int + r2 :: short_int z :: int r3 :: object r4 :: native_int - r5 :: short_int - r6, r7 :: bit - r8, x :: bool - r9 :: object - r10, y :: int - r11, r12 :: short_int - r13 :: bit + r5, r6 :: bit + r7, x :: bool + r8 :: object + r9, y :: int + r10 :: native_int + r11 :: short_int + r12 :: bit L0: r0 = PyObject_GetIter(a) r1 = 0 @@ -1010,28 +1005,27 @@ L1: if is_error(r3) goto L6 else goto L2 L2: r4 = var_object_size b - r5 = r4 << 1 - r6 = int_lt r1, r5 - if r6 goto L3 else goto L6 :: bool + r5 = r1 < r4 :: signed + if r5 goto L3 else goto L6 :: bool L3: - r7 = int_lt r2, 10 - if r7 goto L4 else goto L6 :: bool + r6 = int_lt r2, 10 + if r6 goto L4 else goto L6 :: bool L4: - r8 = unbox(bool, r3) - x = r8 - r9 = CPyList_GetItemUnsafe(b, r1) - r10 = unbox(int, r9) - y = r10 + r7 = unbox(bool, r3) + x = r7 + r8 = list_get_item_unsafe b, r1 + r9 = unbox(int, r8) + y = r9 x = 0 L5: - r11 = r1 + 2 - r1 = r11 - r12 = r2 + 2 - r2 = r12 - z = r12 + r10 = r1 + 1 + r1 = r10 + r11 = r2 + 2 + r2 = r11 + z = r11 goto L1 L6: - r13 = CPy_NoErrOccured() + r12 = CPy_NoErrOccurred() L7: return 1 diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 771dcc4c0e68c..056f120c7bac0 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -1,13 +1,15 @@ [case testStrSplit] -from typing import Optional, List +from typing import NewType, Optional, List, Union +NewStr = NewType("NewStr", str) -def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: +def do_split(s: Union[str, NewStr], sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: if sep is not None: if max_split is not None: return s.split(sep, max_split) else: return s.split(sep) return s.split() +[typing fixtures/typing-full.pyi] [out] def do_split(s, sep, max_split): s :: str @@ -56,60 +58,40 @@ L9: [case testStrEquality] +from typing import NewType, Union +NewStr = NewType("NewStr", str) def eq(x: str, y: str) -> bool: return x == y -def neq(x: str, y: str) -> bool: +def neq(x: str, y: Union[str, NewStr]) -> bool: return x != y +[typing fixtures/typing-full.pyi] [out] def eq(x, y): x, y :: str - r0 :: i32 - r1 :: bit - r2 :: object - r3, r4, r5 :: bit + r0 :: bool L0: - r0 = PyUnicode_Compare(x, y) - r1 = r0 == -1 - if r1 goto L1 else goto L3 :: bool -L1: - r2 = PyErr_Occurred() - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool -L2: - r4 = CPy_KeepPropagating() -L3: - r5 = r0 == 0 - return r5 + r0 = CPyStr_Equal(x, y) + return r0 def neq(x, y): x, y :: str - r0 :: i32 + r0 :: bool r1 :: bit - r2 :: object - r3, r4, r5 :: bit L0: - r0 = PyUnicode_Compare(x, y) - r1 = r0 == -1 - if r1 goto L1 else goto L3 :: bool -L1: - r2 = PyErr_Occurred() - r3 = r2 != 0 - if r3 goto L2 else goto L3 :: bool -L2: - r4 = CPy_KeepPropagating() -L3: - r5 = r0 != 0 - return r5 + r0 = CPyStr_Equal(x, y) + r1 = r0 == 0 + return r1 [case testStrReplace] -from typing import Optional - -def do_replace(s: str, old_substr: str, new_substr: str, max_count: Optional[int] = None) -> str: +from typing import NewType, Optional, Union +NewStr = NewType("NewStr", str) +def do_replace(s: Union[str, NewStr], old_substr: str, new_substr: str, max_count: Optional[int] = None) -> str: if max_count is not None: return s.replace(old_substr, new_substr, max_count) else: return s.replace(old_substr, new_substr) +[typing fixtures/typing-full.pyi] [out] def do_replace(s, old_substr, new_substr, max_count): s, old_substr, new_substr :: str @@ -137,12 +119,68 @@ L4: L5: unreachable +[case testStrStartswithEndswithTuple] +from typing import NewType, Tuple, Union +NewStr = NewType("NewStr", str) + +def do_startswith(s1: Union[str, NewStr], s2: Tuple[str, ...]) -> bool: + return s1.startswith(s2) + +def do_endswith(s1: Union[str, NewStr], s2: Tuple[str, ...]) -> bool: + return s1.endswith(s2) + +def do_tuple_literal_args(s1: Union[str, NewStr]) -> None: + x = s1.startswith(("a", "b")) + y = s1.endswith(("a", "b")) +[typing fixtures/typing-full.pyi] +[out] +def do_startswith(s1, s2): + s1 :: str + s2 :: tuple + r0 :: bool +L0: + r0 = CPyStr_Startswith(s1, s2) + return r0 +def do_endswith(s1, s2): + s1 :: str + s2 :: tuple + r0 :: bool +L0: + r0 = CPyStr_Endswith(s1, s2) + return r0 +def do_tuple_literal_args(s1): + s1, r0, r1 :: str + r2 :: tuple[str, str] + r3 :: object + r4, x :: bool + r5, r6 :: str + r7 :: tuple[str, str] + r8 :: object + r9, y :: bool +L0: + r0 = 'a' + r1 = 'b' + r2 = (r0, r1) + r3 = box(tuple[str, str], r2) + r4 = CPyStr_Startswith(s1, r3) + x = r4 + r5 = 'a' + r6 = 'b' + r7 = (r5, r6) + r8 = box(tuple[str, str], r7) + r9 = CPyStr_Endswith(s1, r8) + y = r9 + return 1 + [case testStrToBool] -def is_true(x: str) -> bool: +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def is_true(x: Union[str, NewStr]) -> bool: if x: return True else: return False +[typing fixtures/typing-full.pyi] [out] def is_true(x): x :: str @@ -158,11 +196,14 @@ L3: unreachable [case testStringFormatMethod] -def f(s: str, num: int) -> None: +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def f(s: Union[str, NewStr], num: int) -> None: s1 = "Hi! I'm {}, and I'm {} years old.".format(s, num) s2 = ''.format() s3 = 'abc'.format() s4 = '}}{}{{{}}}{{{}'.format(num, num, num) +[typing fixtures/typing-full.pyi] [out] def f(s, num): s :: str @@ -189,22 +230,27 @@ L0: s4 = r13 return 1 -[case testFStrings] -def f(var: str, num: int) -> None: +[case testFStrings_64bit] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def f(var: Union[str, NewStr], num: int) -> None: s1 = f"Hi! I'm {var}. I am {num} years old." s2 = f'Hello {var:>{num}}' s3 = f'' s4 = f'abc' +[typing fixtures/typing-full.pyi] [out] def f(var, num): var :: str num :: int r0, r1, r2, r3, r4, s1, r5, r6, r7, r8, r9, r10, r11 :: str - r12 :: object - r13 :: str - r14 :: list - r15 :: ptr - r16, s2, r17, s3, r18, s4 :: str + r12 :: object[3] + r13 :: object_ptr + r14 :: object + r15 :: str + r16 :: list + r17 :: ptr + r18, s2, r19, s3, r20, s4 :: str L0: r0 = "Hi! I'm " r1 = '. I am ' @@ -219,23 +265,28 @@ L0: r9 = CPyTagged_Str(num) r10 = CPyStr_Build(2, r8, r9) r11 = 'format' - r12 = CPyObject_CallMethodObjArgs(r7, r11, var, r10, 0) - r13 = cast(str, r12) - r14 = PyList_New(2) - r15 = list_items r14 - buf_init_item r15, 0, r6 - buf_init_item r15, 1, r13 - keep_alive r14 - r16 = PyUnicode_Join(r5, r14) - s2 = r16 - r17 = '' - s3 = r17 - r18 = 'abc' - s4 = r18 + r12 = [r7, var, r10] + r13 = load_address r12 + r14 = PyObject_VectorcallMethod(r11, r13, 9223372036854775811, 0) + keep_alive r7, var, r10 + r15 = cast(str, r14) + r16 = PyList_New(2) + r17 = list_items r16 + buf_init_item r17, 0, r6 + buf_init_item r17, 1, r15 + keep_alive r16 + r18 = PyUnicode_Join(r5, r16) + s2 = r18 + r19 = '' + s3 = r19 + r20 = 'abc' + s4 = r20 return 1 [case testStringFormattingCStyle] -def f(var: str, num: int) -> None: +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def f(var: Union[str, NewStr], num: int) -> None: s1 = "Hi! I'm %s." % var s2 = "I am %d years old." % num s3 = "Hi! I'm %s. I am %d years old." % (var, num) @@ -274,39 +325,449 @@ L0: [case testDecode] def f(b: bytes) -> None: b.decode() + b.decode('Utf_8') b.decode('utf-8') + b.decode('UTF8') + b.decode('latin1') + b.decode('Latin-1') + b.decode('ascii') + encoding = 'utf-8' + b.decode(encoding) b.decode('utf-8', 'backslashreplace') +def variants(b: bytes) -> None: + b.decode(encoding="UTF_8") + b.decode("ascii", errors="strict") [out] def f(b): b :: bytes - r0, r1, r2, r3, r4, r5 :: str + r0, r1, r2, r3, r4, r5, r6, r7, encoding, r8, r9, r10, r11 :: str +L0: + r0 = CPy_DecodeUTF8(b) + r1 = CPy_DecodeUTF8(b) + r2 = CPy_DecodeUTF8(b) + r3 = CPy_DecodeUTF8(b) + r4 = CPy_DecodeLatin1(b) + r5 = CPy_DecodeLatin1(b) + r6 = CPy_DecodeASCII(b) + r7 = 'utf-8' + encoding = r7 + r8 = CPy_Decode(b, encoding, 0) + r9 = 'utf-8' + r10 = 'backslashreplace' + r11 = CPy_Decode(b, r9, r10) + return 1 +def variants(b): + b :: bytes + r0, r1 :: str L0: - r0 = CPy_Decode(b, 0, 0) - r1 = 'utf-8' - r2 = CPy_Decode(b, r1, 0) - r3 = 'utf-8' - r4 = 'backslashreplace' - r5 = CPy_Decode(b, r3, r4) + r0 = CPy_DecodeUTF8(b) + r1 = CPy_DecodeASCII(b) return 1 -[case testEncode] -def f(s: str) -> None: +[case testEncode_64bit] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def f(s: Union[str, NewStr]) -> 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') + +[typing fixtures/typing-full.pyi] [out] def f(s): s :: str - r0 :: 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[3] + r20 :: object_ptr + r21, r22 :: object + r23 :: str + r24 :: object[2] + r25 :: object_ptr + r26, r27 :: object + r28 :: str + r29 :: object[3] + r30 :: object_ptr + r31, r32 :: object + r33 :: str + r34 :: bytes +L0: + 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 = [s, r17, errors] + r20 = load_address r19 + r21 = ('errors',) + r22 = PyObject_VectorcallMethod(r18, r20, 9223372036854775810, r21) + keep_alive s, r17, errors + r23 = 'encode' + r24 = [s, errors] + r25 = load_address r24 + r26 = ('errors',) + r27 = PyObject_VectorcallMethod(r23, r25, 9223372036854775809, r26) + keep_alive s, errors + r28 = 'encode' + r29 = [s, encoding, errors] + r30 = load_address r29 + r31 = ('encoding', 'errors') + r32 = PyObject_VectorcallMethod(r28, r30, 9223372036854775809, r31) + keep_alive s, encoding, errors + r33 = 'latin2' + r34 = CPy_Encode(s, r33, 0) + return 1 + +[case testOrd] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def str_ord(x: Union[str, NewStr]) -> 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) +[typing fixtures/typing-full.pyi] +[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 :: bytes - r3, r4 :: str - r5 :: bytes + r2 :: object + r3 :: object[1] + r4 :: object_ptr + r5 :: object + r6 :: int +L0: + r0 = builtins :: module + r1 = 'ord' + r2 = CPyObject_GetAttr(r0, r1) + r3 = [x] + r4 = load_address r3 + r5 = PyObject_Vectorcall(r2, r4, 1, 0) + keep_alive x + r6 = unbox(int, r5) + return r6 + +[case testStrip] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def do_strip(s: Union[str, NewStr]) -> None: + s.lstrip("x") + s.strip("y") + s.rstrip("z") + s.lstrip() + s.strip() + s.rstrip() +[typing fixtures/typing-full.pyi] +[out] +def do_strip(s): + s, r0, r1, r2, r3, r4, r5, r6, r7, r8 :: str +L0: + r0 = 'x' + r1 = CPyStr_LStrip(s, r0) + r2 = 'y' + r3 = CPyStr_Strip(s, r2) + r4 = 'z' + r5 = CPyStr_RStrip(s, r4) + r6 = CPyStr_LStrip(s, 0) + r7 = CPyStr_Strip(s, 0) + r8 = CPyStr_RStrip(s, 0) + return 1 + +[case testCountAll_64bit] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def do_count(s: str) -> int: + return s.count("x") +[typing fixtures/typing-full.pyi] +[out] +def do_count(s): + s, r0 :: str + r1 :: native_int + r2, r3, r4 :: bit + r5, r6, r7 :: int +L0: + r0 = 'x' + r1 = CPyStr_Count(s, r0, 0) + r2 = r1 >= 0 :: signed + r3 = r1 <= 4611686018427387903 :: signed + if r3 goto L1 else goto L2 :: bool +L1: + r4 = r1 >= -4611686018427387904 :: signed + if r4 goto L3 else goto L2 :: bool +L2: + r5 = CPyTagged_FromInt64(r1) + r6 = r5 + goto L4 +L3: + r7 = r1 << 1 + r6 = r7 +L4: + return r6 + +[case testCountStart_64bit] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def do_count(s: str, start: int) -> int: + return s.count("x", start) +[typing fixtures/typing-full.pyi] +[out] +def do_count(s, start): + s :: str + start :: int + r0 :: str + r1 :: native_int + r2, r3, r4 :: bit + r5, r6, r7 :: int +L0: + r0 = 'x' + r1 = CPyStr_Count(s, r0, start) + r2 = r1 >= 0 :: signed + r3 = r1 <= 4611686018427387903 :: signed + if r3 goto L1 else goto L2 :: bool +L1: + r4 = r1 >= -4611686018427387904 :: signed + if r4 goto L3 else goto L2 :: bool +L2: + r5 = CPyTagged_FromInt64(r1) + r6 = r5 + goto L4 +L3: + r7 = r1 << 1 + r6 = r7 +L4: + return r6 + +[case testCountStartEnd_64bit] +from typing import NewType, Union +NewStr = NewType("NewStr", str) +def do_count(s: str, start: int, end: int) -> int: + return s.count("x", start, end) +[typing fixtures/typing-full.pyi] +[out] +def do_count(s, start, end): + s :: str + start, end :: int + r0 :: str + r1 :: native_int + r2, r3, r4 :: bit + r5, r6, r7 :: int +L0: + r0 = 'x' + r1 = CPyStr_CountFull(s, r0, start, end) + r2 = r1 >= 0 :: signed + r3 = r1 <= 4611686018427387903 :: signed + if r3 goto L1 else goto L2 :: bool +L1: + r4 = r1 >= -4611686018427387904 :: signed + if r4 goto L3 else goto L2 :: bool +L2: + r5 = CPyTagged_FromInt64(r1) + r6 = r5 + goto L4 +L3: + r7 = r1 << 1 + r6 = r7 +L4: + return r6 + +[case testFStringFromConstants] +from typing import Final +string: Final = "abc" +integer: Final = 123 +floating: Final = 3.14 +boolean: Final = True + +def test(x: str) -> str: + return f"{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}" +def test2(x: str) -> str: + return f"{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}" +def test3(x: str) -> str: + return f"{x}{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}" + +[out] +def test(x): + x, r0, r1, r2, r3 :: str +L0: + r0 = 'abc1233.14True' + r1 = 'True3.14123abc' + r2 = 'abc1233.14True' + r3 = CPyStr_Build(5, r0, x, r1, x, r2) + return r3 +def test2(x): + x, r0, r1, r2, r3 :: str +L0: + r0 = 'abc1233.14True' + r1 = 'True3.14123abc' + r2 = 'abc1233.14True' + r3 = CPyStr_Build(6, r0, x, r1, x, r2, x) + return r3 +def test3(x): + x, r0, r1, r2, r3 :: str +L0: + r0 = 'abc1233.14True' + r1 = 'True3.14123abc' + r2 = 'abc1233.14True' + r3 = CPyStr_Build(7, x, r0, x, r1, x, r2, x) + return r3 + +[case testOptionalStrEquality1] +from typing import Optional + +def opt_opt(x: Optional[str], y: Optional[str]) -> bool: + return x == y +[out] +def opt_opt(x, y): + x, y :: union[str, None] + r0 :: object + r1 :: bit + r2 :: object + r3 :: bit + r4 :: bool + r5 :: object + r6 :: bit + r7, r8 :: str + r9 :: bool +L0: + r0 = load_address _Py_NoneStruct + r1 = x == r0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = load_address _Py_NoneStruct + r3 = y == r2 + r4 = r3 + goto L5 +L2: + r5 = load_address _Py_NoneStruct + r6 = y == r5 + if r6 goto L3 else goto L4 :: bool +L3: + r4 = 0 + goto L5 +L4: + r7 = unchecked borrow cast(str, x) + r8 = unchecked borrow cast(str, y) + r9 = CPyStr_Equal(r7, r8) + r4 = r9 +L5: + keep_alive x, y + return r4 + +[case testOptionalStrEquality2] +from typing import Optional + +def opt_non_opt(x: Optional[str], y: str) -> bool: + return x == y +[out] +def opt_non_opt(x, y): + x :: union[str, None] + y :: str + r0 :: object + r1 :: bit + r2 :: bool + r3 :: str + r4 :: bool +L0: + r0 = load_address _Py_NoneStruct + r1 = x == r0 + if r1 goto L1 else goto L2 :: bool +L1: + r2 = 0 + goto L3 +L2: + r3 = unchecked borrow cast(str, x) + r4 = CPyStr_Equal(r3, y) + r2 = r4 +L3: + keep_alive x + return r2 + +[case testStrEqLiteral] +from typing import Final +literal: Final = "literal" +def literal_rhs(x: str) -> bool: + return x == literal +def literal_lhs(x: str) -> bool: + return literal == x +def literal_both() -> bool: + return literal == "literal" +[out] +def literal_rhs(x): + x, r0 :: str + r1 :: bool +L0: + r0 = 'literal' + r1 = CPyStr_EqualLiteral(x, r0, 7) + return r1 +def literal_lhs(x): + x, r0 :: str + r1 :: bool +L0: + r0 = 'literal' + r1 = CPyStr_EqualLiteral(x, r0, 7) + return r1 +def literal_both(): + r0, r1 :: str 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 = 'literal' + r1 = 'literal' return 1 diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index a5b7b9a55b868..ec470eae1e88e 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -13,14 +13,17 @@ def g(): r5 :: str r6 :: object r7 :: str - r8, r9 :: object - r10 :: bit + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11 :: object + r12 :: bit L0: L1: r0 = builtins :: module r1 = 'object' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) goto L5 L2: (handler for L1) r4 = CPy_CatchError() @@ -28,13 +31,16 @@ L2: (handler for L1) r6 = builtins :: module r7 = 'print' r8 = CPyObject_GetAttr(r6, r7) - r9 = PyObject_CallFunctionObjArgs(r8, r5, 0) + r9 = [r5] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 1, 0) + keep_alive r5 L3: CPy_RestoreExcInfo(r4) goto L5 L4: (handler for L2) CPy_RestoreExcInfo(r4) - r10 = CPy_KeepPropagating() + r12 = CPy_KeepPropagating() unreachable L5: return 1 @@ -59,8 +65,11 @@ def g(b): r7 :: str r8 :: object r9 :: str - r10, r11 :: object - r12 :: bit + r10 :: object + r11 :: object[1] + r12 :: object_ptr + r13 :: object + r14 :: bit L0: L1: if b goto L2 else goto L3 :: bool @@ -68,7 +77,7 @@ L2: r0 = builtins :: module r1 = 'object' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) goto L4 L3: r4 = 'hi' @@ -81,13 +90,16 @@ L5: (handler for L1, L2, L3, L4) r8 = builtins :: module r9 = 'print' r10 = CPyObject_GetAttr(r8, r9) - r11 = PyObject_CallFunctionObjArgs(r10, r7, 0) + r11 = [r7] + r12 = load_address r11 + r13 = PyObject_Vectorcall(r10, r12, 1, 0) + keep_alive r7 L6: CPy_RestoreExcInfo(r6) goto L8 L7: (handler for L5) CPy_RestoreExcInfo(r6) - r12 = CPy_KeepPropagating() + r14 = CPy_KeepPropagating() unreachable L8: return 1 @@ -107,80 +119,98 @@ def g(): r0 :: str r1 :: object r2 :: str - r3, r4, r5 :: object - r6 :: str - r7, r8 :: object - r9 :: tuple[object, object, object] - r10 :: object - r11 :: str + r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6, r7 :: object + r8 :: str + r9, r10 :: object + r11 :: tuple[object, object, object] r12 :: object - r13 :: bit - r14, e :: object - r15 :: str - r16 :: object + r13 :: str + r14 :: object + r15 :: bit + r16, e :: object r17 :: str - r18, r19 :: object - r20 :: bit - r21 :: tuple[object, object, object] - r22 :: str + r18 :: object + r19 :: str + r20 :: object + r21 :: object[2] + r22 :: object_ptr r23 :: object - r24 :: str - r25, r26 :: object - r27 :: bit + r24 :: bit + r25 :: tuple[object, object, object] + r26 :: str + r27 :: object + r28 :: str + r29 :: object + r30 :: object[1] + r31 :: object_ptr + r32 :: object + r33 :: bit L0: L1: r0 = 'a' r1 = builtins :: module r2 = 'print' r3 = CPyObject_GetAttr(r1, r2) - r4 = PyObject_CallFunctionObjArgs(r3, r0, 0) + r4 = [r0] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r3, r5, 1, 0) + keep_alive r0 L2: - r5 = builtins :: module - r6 = 'object' - r7 = CPyObject_GetAttr(r5, r6) - r8 = PyObject_CallFunctionObjArgs(r7, 0) + r7 = builtins :: module + r8 = 'object' + r9 = CPyObject_GetAttr(r7, r8) + r10 = PyObject_Vectorcall(r9, 0, 0, 0) goto L8 L3: (handler for L2) - r9 = CPy_CatchError() - r10 = builtins :: module - r11 = 'AttributeError' - r12 = CPyObject_GetAttr(r10, r11) - r13 = CPy_ExceptionMatches(r12) - if r13 goto L4 else goto L5 :: bool + r11 = CPy_CatchError() + r12 = builtins :: module + r13 = 'AttributeError' + r14 = CPyObject_GetAttr(r12, r13) + r15 = CPy_ExceptionMatches(r14) + if r15 goto L4 else goto L5 :: bool L4: - r14 = CPy_GetExcValue() - e = r14 - r15 = 'b' - r16 = builtins :: module - r17 = 'print' - r18 = CPyObject_GetAttr(r16, r17) - r19 = PyObject_CallFunctionObjArgs(r18, r15, e, 0) + r16 = CPy_GetExcValue() + e = r16 + r17 = 'b' + r18 = builtins :: module + r19 = 'print' + r20 = CPyObject_GetAttr(r18, r19) + r21 = [r17, e] + r22 = load_address r21 + r23 = PyObject_Vectorcall(r20, r22, 2, 0) + keep_alive r17, e goto L6 L5: CPy_Reraise() unreachable L6: - CPy_RestoreExcInfo(r9) + CPy_RestoreExcInfo(r11) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r9) - r20 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r11) + r24 = CPy_KeepPropagating() unreachable L8: goto L12 L9: (handler for L1, L6, L7, L8) - r21 = CPy_CatchError() - r22 = 'weeee' - r23 = builtins :: module - r24 = 'print' - r25 = CPyObject_GetAttr(r23, r24) - r26 = PyObject_CallFunctionObjArgs(r25, r22, 0) + r25 = CPy_CatchError() + r26 = 'weeee' + r27 = builtins :: module + r28 = 'print' + r29 = CPyObject_GetAttr(r27, r28) + r30 = [r26] + r31 = load_address r30 + r32 = PyObject_Vectorcall(r29, r31, 1, 0) + keep_alive r26 L10: - CPy_RestoreExcInfo(r21) + CPy_RestoreExcInfo(r25) goto L12 L11: (handler for L9) - CPy_RestoreExcInfo(r21) - r27 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r25) + r33 = CPy_KeepPropagating() unreachable L12: return 1 @@ -203,15 +233,21 @@ def g(): r5 :: str r6 :: object r7 :: str - r8, r9, r10 :: object - r11 :: str - r12 :: object - r13 :: bit - r14 :: str - r15 :: object + r8 :: object + r9 :: object[1] + r10 :: object_ptr + r11, r12 :: object + r13 :: str + r14 :: object + r15 :: bit r16 :: str - r17, r18 :: object - r19 :: bit + r17 :: object + r18 :: str + r19 :: object + r20 :: object[1] + r21 :: object_ptr + r22 :: object + r23 :: bit L0: L1: goto L9 @@ -227,20 +263,26 @@ L3: r6 = builtins :: module r7 = 'print' r8 = CPyObject_GetAttr(r6, r7) - r9 = PyObject_CallFunctionObjArgs(r8, r5, 0) + r9 = [r5] + r10 = load_address r9 + r11 = PyObject_Vectorcall(r8, r10, 1, 0) + keep_alive r5 goto L7 L4: - r10 = builtins :: module - r11 = 'IndexError' - r12 = CPyObject_GetAttr(r10, r11) - r13 = CPy_ExceptionMatches(r12) - if r13 goto L5 else goto L6 :: bool + r12 = builtins :: module + r13 = 'IndexError' + r14 = CPyObject_GetAttr(r12, r13) + r15 = CPy_ExceptionMatches(r14) + if r15 goto L5 else goto L6 :: bool L5: - r14 = 'yo' - r15 = builtins :: module - r16 = 'print' - r17 = CPyObject_GetAttr(r15, r16) - r18 = PyObject_CallFunctionObjArgs(r17, r14, 0) + r16 = 'yo' + r17 = builtins :: module + r18 = 'print' + r19 = CPyObject_GetAttr(r17, r18) + r20 = [r16] + r21 = load_address r20 + r22 = PyObject_Vectorcall(r19, r21, 1, 0) + keep_alive r16 goto L7 L6: CPy_Reraise() @@ -250,7 +292,7 @@ L7: goto L9 L8: (handler for L2, L3, L4, L5, L6) CPy_RestoreExcInfo(r0) - r19 = CPy_KeepPropagating() + r23 = CPy_KeepPropagating() unreachable L9: return 1 @@ -268,13 +310,19 @@ def a(b): r0 :: str r1 :: object r2 :: str - r3, r4 :: object - r5, r6, r7 :: tuple[object, object, object] - r8 :: str - r9 :: object + r3 :: object + r4 :: object[1] + r5 :: object_ptr + r6 :: object + r7, r8, r9 :: tuple[object, object, object] r10 :: str - r11, r12 :: object - r13 :: bit + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object + r17 :: bit L0: L1: if b goto L2 else goto L3 :: bool @@ -283,36 +331,42 @@ L2: r1 = builtins :: module r2 = 'Exception' r3 = CPyObject_GetAttr(r1, r2) - r4 = PyObject_CallFunctionObjArgs(r3, r0, 0) - CPy_Raise(r4) + r4 = [r0] + r5 = load_address r4 + r6 = PyObject_Vectorcall(r3, r5, 1, 0) + keep_alive r0 + CPy_Raise(r6) unreachable L3: L4: L5: - r5 = :: tuple[object, object, object] - r6 = r5 + r7 = :: tuple[object, object, object] + r8 = r7 goto L7 L6: (handler for L1, L2, L3) - r7 = CPy_CatchError() - r6 = r7 + r9 = CPy_CatchError() + r8 = r9 L7: - r8 = 'finally' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) - if is_error(r6) goto L9 else goto L8 + r10 = 'finally' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 + if is_error(r8) goto L9 else goto L8 L8: CPy_Reraise() unreachable L9: goto L13 L10: (handler for L7, L8) - if is_error(r6) goto L12 else goto L11 + if is_error(r8) goto L12 else goto L11 L11: - CPy_RestoreExcInfo(r6) + CPy_RestoreExcInfo(r8) L12: - r13 = CPy_KeepPropagating() + r17 = CPy_KeepPropagating() unreachable L13: return 1 @@ -328,90 +382,114 @@ def foo(x): r2 :: str r3 :: object r4 :: str - r5, r6 :: object - r7 :: bool + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9 :: bool y :: object - r8 :: str - r9 :: object r10 :: str - r11, r12 :: object - r13, r14 :: tuple[object, object, object] - r15, r16, r17, r18 :: object - r19 :: i32 - r20 :: bit - r21 :: bool - r22 :: bit - r23, r24, r25 :: tuple[object, object, object] - r26, r27 :: object + r11 :: object + r12 :: str + r13 :: object + r14 :: object[1] + r15 :: object_ptr + r16 :: object + r17, r18 :: tuple[object, object, object] + r19, r20, r21 :: object + r22 :: object[4] + r23 :: object_ptr + r24 :: object + r25 :: i32 + r26 :: bit + r27 :: bool r28 :: bit + r29, r30, r31 :: tuple[object, object, object] + r32 :: object + r33 :: object[4] + r34 :: object_ptr + r35 :: object + r36 :: bit L0: - r0 = PyObject_CallFunctionObjArgs(x, 0) - r1 = PyObject_Type(r0) + r0 = PyObject_Vectorcall(x, 0, 0, 0) + r1 = CPy_TYPE(r0) r2 = '__exit__' r3 = CPyObject_GetAttr(r1, r2) r4 = '__enter__' r5 = CPyObject_GetAttr(r1, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r0, 0) - r7 = 1 + r6 = [r0] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r0 + r9 = 1 L1: L2: - y = r6 - r8 = 'hello' - r9 = builtins :: module - r10 = 'print' - r11 = CPyObject_GetAttr(r9, r10) - r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) + y = r8 + r10 = 'hello' + r11 = builtins :: module + r12 = 'print' + r13 = CPyObject_GetAttr(r11, r12) + r14 = [r10] + r15 = load_address r14 + r16 = PyObject_Vectorcall(r13, r15, 1, 0) + keep_alive r10 goto L8 L3: (handler for L2) - r13 = CPy_CatchError() - r7 = 0 - r14 = CPy_GetExcInfo() - r15 = r14[0] - r16 = r14[1] - r17 = r14[2] - r18 = PyObject_CallFunctionObjArgs(r3, r0, r15, r16, r17, 0) - r19 = PyObject_IsTrue(r18) - r20 = r19 >= 0 :: signed - r21 = truncate r19: i32 to builtins.bool - if r21 goto L5 else goto L4 :: bool + r17 = CPy_CatchError() + r9 = 0 + r18 = CPy_GetExcInfo() + r19 = r18[0] + r20 = r18[1] + r21 = r18[2] + r22 = [r0, r19, r20, r21] + r23 = load_address r22 + r24 = PyObject_Vectorcall(r3, r23, 4, 0) + keep_alive r0, r19, r20, r21 + r25 = PyObject_IsTrue(r24) + r26 = r25 >= 0 :: signed + r27 = truncate r25: i32 to builtins.bool + if r27 goto L5 else goto L4 :: bool L4: CPy_Reraise() unreachable L5: L6: - CPy_RestoreExcInfo(r13) + CPy_RestoreExcInfo(r17) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r13) - r22 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r17) + r28 = CPy_KeepPropagating() unreachable L8: L9: L10: - r23 = :: tuple[object, object, object] - r24 = r23 + r29 = :: tuple[object, object, object] + r30 = r29 goto L12 L11: (handler for L1, L6, L7, L8) - r25 = CPy_CatchError() - r24 = r25 + r31 = CPy_CatchError() + r30 = r31 L12: - if r7 goto L13 else goto L14 :: bool + if r9 goto L13 else goto L14 :: bool L13: - r26 = load_address _Py_NoneStruct - r27 = PyObject_CallFunctionObjArgs(r3, r0, r26, r26, r26, 0) + r32 = load_address _Py_NoneStruct + r33 = [r0, r32, r32, r32] + r34 = load_address r33 + r35 = PyObject_Vectorcall(r3, r34, 4, 0) + keep_alive r0, r32, r32, r32 L14: - if is_error(r24) goto L16 else goto L15 + if is_error(r30) goto L16 else goto L15 L15: CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r24) goto L19 else goto L18 + if is_error(r30) goto L19 else goto L18 L18: - CPy_RestoreExcInfo(r24) + CPy_RestoreExcInfo(r30) L19: - r28 = CPy_KeepPropagating() + r36 = CPy_KeepPropagating() unreachable L20: return 1 @@ -443,19 +521,22 @@ def foo(x): r2 :: str r3 :: object r4 :: str - r5, r6 :: object - r7, r8 :: tuple[object, object, object] - r9, r10, r11 :: object - r12 :: None - r13 :: object - r14 :: i32 - r15 :: bit - r16 :: bool + r5 :: object + r6 :: object[1] + r7 :: object_ptr + r8 :: object + r9, r10 :: tuple[object, object, object] + r11, r12, r13 :: object + r14 :: None + r15 :: object + r16 :: i32 r17 :: bit - r18, r19, r20 :: tuple[object, object, object] - r21 :: object - r22 :: None - r23 :: bit + r18 :: bool + r19 :: bit + r20, r21, r22 :: tuple[object, object, object] + r23 :: object + r24 :: None + r25 :: bit L0: r0 = x.__enter__() r1 = 1 @@ -465,59 +546,62 @@ L2: r3 = builtins :: module r4 = 'print' r5 = CPyObject_GetAttr(r3, r4) - r6 = PyObject_CallFunctionObjArgs(r5, r2, 0) + r6 = [r2] + r7 = load_address r6 + r8 = PyObject_Vectorcall(r5, r7, 1, 0) + keep_alive r2 goto L8 L3: (handler for L2) - r7 = CPy_CatchError() + r9 = CPy_CatchError() r1 = 0 - r8 = CPy_GetExcInfo() - r9 = r8[0] - r10 = r8[1] - r11 = r8[2] - r12 = x.__exit__(r9, r10, r11) - r13 = box(None, r12) - r14 = PyObject_IsTrue(r13) - r15 = r14 >= 0 :: signed - r16 = truncate r14: i32 to builtins.bool - if r16 goto L5 else goto L4 :: bool + r10 = CPy_GetExcInfo() + r11 = r10[0] + r12 = r10[1] + r13 = r10[2] + r14 = x.__exit__(r11, r12, r13) + r15 = box(None, r14) + r16 = PyObject_IsTrue(r15) + r17 = r16 >= 0 :: signed + r18 = truncate r16: i32 to builtins.bool + if r18 goto L5 else goto L4 :: bool L4: CPy_Reraise() unreachable L5: L6: - CPy_RestoreExcInfo(r7) + CPy_RestoreExcInfo(r9) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r7) - r17 = CPy_KeepPropagating() + CPy_RestoreExcInfo(r9) + r19 = CPy_KeepPropagating() unreachable L8: L9: L10: - r18 = :: tuple[object, object, object] - r19 = r18 + r20 = :: tuple[object, object, object] + r21 = r20 goto L12 L11: (handler for L1, L6, L7, L8) - r20 = CPy_CatchError() - r19 = r20 + r22 = CPy_CatchError() + r21 = r22 L12: if r1 goto L13 else goto L14 :: bool L13: - r21 = load_address _Py_NoneStruct - r22 = x.__exit__(r21, r21, r21) + r23 = load_address _Py_NoneStruct + r24 = x.__exit__(r23, r23, r23) L14: - if is_error(r19) goto L16 else goto L15 + if is_error(r21) goto L16 else goto L15 L15: CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r19) goto L19 else goto L18 + if is_error(r21) goto L19 else goto L18 L18: - CPy_RestoreExcInfo(r19) + CPy_RestoreExcInfo(r21) L19: - r23 = CPy_KeepPropagating() + r25 = CPy_KeepPropagating() unreachable L20: return 1 diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index a6813de4ee442..0fdd8e87a1542 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -127,27 +127,24 @@ def f(xs: Tuple[str, ...]) -> None: [out] def f(xs): xs :: tuple - r0 :: short_int - r1 :: native_int - r2 :: short_int - r3 :: bit - r4 :: object - r5, x :: str - r6 :: short_int + r0, r1 :: native_int + r2 :: bit + r3 :: object + r4, x :: str + r5 :: native_int L0: - r0 = 0 + r0 = var_object_size xs + r1 = 0 L1: - r1 = var_object_size xs - r2 = r1 << 1 - r3 = int_lt r0, r2 - if r3 goto L2 else goto L4 :: bool + r2 = r1 < r0 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPySequenceTuple_GetItem(xs, r0) - r5 = cast(str, r4) - x = r5 + r3 = CPySequenceTuple_GetItemUnsafe(xs, r1) + r4 = cast(str, r3) + x = r4 L3: - r6 = r0 + 2 - r0 = r6 + r5 = r1 + 1 + r1 = r5 goto L1 L4: return 1 @@ -187,30 +184,102 @@ def f(i: int) -> bool: [out] def f(i): i :: int - r0 :: bit - r1 :: bool - r2 :: bit + r0, r1, r2 :: bit r3 :: bool - r4 :: bit L0: r0 = int_eq i, 2 - if r0 goto L1 else goto L2 :: bool + if r0 goto L4 else goto L1 :: bool L1: - r1 = r0 - goto L3 + r1 = int_eq i, 4 + if r1 goto L4 else goto L2 :: bool +L2: + r2 = int_eq i, 6 + if r2 goto L4 else goto L3 :: bool +L3: + r3 = 0 + goto L5 +L4: + r3 = 1 +L5: + return r3 + +[case testTupleOperatorNotIn] +def x() -> int: + return 1 +def y() -> int: + return 2 +def z() -> int: + return 3 + +def f() -> bool: + return z() not in (x(), y()) +[out] +def x(): +L0: + return 2 +def y(): +L0: + return 4 +def z(): +L0: + return 6 +def f(): + r0, r1, r2 :: int + r3, r4 :: bit + r5 :: bool +L0: + r0 = z() + r1 = x() + r2 = y() + r3 = int_ne r0, r1 + if r3 goto L1 else goto L3 :: bool +L1: + r4 = int_ne r0, r2 + if r4 goto L2 else goto L3 :: bool +L2: + r5 = 1 + goto L4 +L3: + r5 = 0 +L4: + return r5 + +[case testTupleOperatorInFinalTuple] +from typing import Final + +tt: Final = (1, 2) + +def f(x: int) -> bool: + return x in tt +[out] +def f(x): + x :: int + r0 :: tuple[int, int] + r1 :: bool + r2, r3 :: int + r4, r5 :: bit + r6 :: bool +L0: + r0 = __main__.tt :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "tt" was not set') + unreachable L2: - r2 = int_eq i, 4 - r1 = r2 + r2 = r0[0] + r3 = r0[1] + r4 = int_eq x, r2 + if r4 goto L5 else goto L3 :: bool L3: - if r1 goto L4 else goto L5 :: bool + r5 = int_eq x, r3 + if r5 goto L5 else goto L4 :: bool L4: - r3 = r1 + r6 = 0 goto L6 L5: - r4 = int_eq i, 6 - r3 = r4 + r6 = 1 L6: - return r3 + return r6 [case testTupleBuiltFromList] def f(val: int) -> bool: @@ -234,16 +303,13 @@ def test(): source :: list r5 :: native_int r6 :: tuple - r7 :: short_int - r8 :: native_int - r9 :: short_int - r10 :: bit - r11 :: object - r12, x :: int - r13 :: bool - r14 :: object - r15 :: bit - r16 :: short_int + r7, r8 :: native_int + r9 :: bit + r10 :: object + r11, x :: int + r12 :: bool + r13 :: object + r14 :: native_int a :: tuple L0: r0 = PyList_New(3) @@ -261,19 +327,18 @@ L0: r7 = 0 L1: r8 = var_object_size source - r9 = r8 << 1 - r10 = int_lt r7, r9 - if r10 goto L2 else goto L4 :: bool + r9 = r7 < r8 :: signed + if r9 goto L2 else goto L4 :: bool L2: - r11 = CPyList_GetItemUnsafe(source, r7) - r12 = unbox(int, r11) - x = r12 - r13 = f(x) - r14 = box(bool, r13) - r15 = CPySequenceTuple_SetItemUnsafe(r6, r7, r14) + r10 = list_get_item_unsafe source, r7 + r11 = unbox(int, r10) + x = r11 + r12 = f(x) + r13 = box(bool, r12) + CPySequenceTuple_SetItemUnsafe(r6, r7, r13) L3: - r16 = r7 + 2 - r7 = r16 + r14 = r7 + 1 + r7 = r14 goto L1 L4: a = r6 @@ -298,14 +363,10 @@ def test(): r1 :: native_int r2 :: bit r3 :: tuple - r4 :: short_int - r5 :: native_int - r6 :: bit - r7 :: short_int - r8 :: bit - r9, x, r10 :: str - r11 :: bit - r12 :: short_int + r4 :: native_int + r5 :: bit + r6, x, r7 :: str + r8 :: native_int a :: tuple L0: r0 = 'abc' @@ -315,24 +376,433 @@ L0: r3 = PyTuple_New(r1) r4 = 0 L1: - r5 = CPyStr_Size_size_t(source) - r6 = r5 >= 0 :: signed - r7 = r5 << 1 - r8 = int_lt r4, r7 - if r8 goto L2 else goto L4 :: bool + r5 = r4 < r1 :: signed + if r5 goto L2 else goto L4 :: bool +L2: + r6 = CPyStr_GetItemUnsafe(source, r4) + x = r6 + r7 = f2(x) + CPySequenceTuple_SetItemUnsafe(r3, r4, r7) +L3: + r8 = r4 + 1 + r4 = r8 + goto L1 +L4: + a = r3 + return 1 + +[case testTupleBuiltFromStrExpr] +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = tuple(f2(x) for x in "abc") + +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: tuple + r2 :: native_int + r3 :: bit + r4, x, r5 :: str + r6 :: native_int + a :: tuple +L0: + r0 = 'abc' + r1 = PyTuple_New(3) + r2 = 0 + goto L2 +L1: + r3 = r2 < 3 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = CPyStr_GetItemUnsafe(r0, r2) + x = r4 + r5 = f2(x) + CPySequenceTuple_SetItemUnsafe(r1, r2, r5) +L3: + r6 = r2 + 1 + r2 = r6 + goto L1 +L4: + a = r1 + return 1 + +[case testTupleBuiltFromFinalStr] +from typing import Final + +source: Final = "abc" + +def f2(val: str) -> str: + return val + "f2" + +def test() -> None: + a = tuple(f2(x) for x in source) +[out] +def f2(val): + val, r0, r1 :: str +L0: + r0 = 'f2' + r1 = PyUnicode_Concat(val, r0) + return r1 +def test(): + r0 :: str + r1 :: tuple + r2 :: native_int + r3 :: bit + r4, x, r5 :: str + r6 :: native_int + a :: tuple +L0: + r0 = 'abc' + r1 = PyTuple_New(3) + r2 = 0 + goto L2 +L1: + r3 = r2 < 3 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = CPyStr_GetItemUnsafe(r0, r2) + x = r4 + r5 = f2(x) + CPySequenceTuple_SetItemUnsafe(r1, r2, r5) +L3: + r6 = r2 + 1 + r2 = r6 + goto L1 +L4: + a = r1 + return 1 + +[case testTupleBuiltFromBytes_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + source = b"abc" + a = tuple(f2(x) for x in source) + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0, source :: bytes + r1 :: native_int + r2 :: tuple + r3 :: native_int + r4, r5, r6 :: bit + r7, r8, r9, r10 :: int + r11 :: object + r12, x, r13 :: int + r14 :: object + r15 :: native_int + a :: tuple +L0: + r0 = b'abc' + source = r0 + r1 = var_object_size source + r2 = PyTuple_New(r1) + r3 = 0 +L1: + r4 = r3 < r1 :: signed + if r4 goto L2 else goto L8 :: bool +L2: + r5 = r3 <= 4611686018427387903 :: signed + if r5 goto L3 else goto L4 :: bool +L3: + r6 = r3 >= -4611686018427387904 :: signed + if r6 goto L5 else goto L4 :: bool +L4: + r7 = CPyTagged_FromInt64(r3) + r8 = r7 + goto L6 +L5: + r9 = r3 << 1 + r8 = r9 +L6: + r10 = CPyBytes_GetItem(source, r8) + r11 = box(int, r10) + r12 = unbox(int, r11) + x = r12 + r13 = f2(x) + r14 = box(int, r13) + CPySequenceTuple_SetItemUnsafe(r2, r3, r14) +L7: + r15 = r3 + 1 + r3 = r15 + goto L1 +L8: + a = r2 + return 1 + +[case testTupleBuiltFromBytesExpr_64bit] +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = tuple(f2(x) for x in b"abc") + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: tuple + r2 :: native_int + r3, r4, r5 :: bit + r6, r7, r8, r9 :: int + r10 :: object + r11, x, r12 :: int + r13 :: object + r14 :: native_int + a :: tuple +L0: + r0 = b'abc' + r1 = PyTuple_New(3) + r2 = 0 + goto L2 +L1: + r3 = r2 < 3 :: signed + if r3 goto L2 else goto L8 :: bool L2: - r9 = CPyStr_GetItem(source, r4) - x = r9 - r10 = f2(x) - r11 = CPySequenceTuple_SetItemUnsafe(r3, r4, r10) + r4 = r2 <= 4611686018427387903 :: signed + if r4 goto L3 else goto L4 :: bool L3: - r12 = r4 + 2 - r4 = r12 + r5 = r2 >= -4611686018427387904 :: signed + if r5 goto L5 else goto L4 :: bool +L4: + r6 = CPyTagged_FromInt64(r2) + r7 = r6 + goto L6 +L5: + r8 = r2 << 1 + r7 = r8 +L6: + r9 = CPyBytes_GetItem(r0, r7) + r10 = box(int, r9) + r11 = unbox(int, r10) + x = r11 + r12 = f2(x) + r13 = box(int, r12) + CPySequenceTuple_SetItemUnsafe(r1, r2, r13) +L7: + r14 = r2 + 1 + r2 = r14 goto L1 +L8: + a = r1 + return 1 + +[case testTupleBuiltFromFinalBytes_64bit] +from typing import Final + +source: Final = b"abc" + +def f2(val: int) -> int: + return val + 2 + +def test() -> None: + a = tuple(f2(x) for x in source) + +[out] +def f2(val): + val, r0 :: int +L0: + r0 = CPyTagged_Add(val, 4) + return r0 +def test(): + r0 :: bytes + r1 :: bool + r2 :: native_int + r3 :: tuple + r4 :: native_int + r5, r6, r7 :: bit + r8, r9, r10, r11 :: int + r12 :: object + r13, x, r14 :: int + r15 :: object + r16 :: native_int + a :: tuple +L0: + r0 = __main__.source :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "source" was not set') + unreachable +L2: + r2 = var_object_size r0 + r3 = PyTuple_New(r2) + r4 = 0 +L3: + r5 = r4 < r2 :: signed + if r5 goto L4 else goto L10 :: bool L4: + r6 = r4 <= 4611686018427387903 :: signed + if r6 goto L5 else goto L6 :: bool +L5: + r7 = r4 >= -4611686018427387904 :: signed + if r7 goto L7 else goto L6 :: bool +L6: + r8 = CPyTagged_FromInt64(r4) + r9 = r8 + goto L8 +L7: + r10 = r4 << 1 + r9 = r10 +L8: + r11 = CPyBytes_GetItem(r0, r9) + r12 = box(int, r11) + r13 = unbox(int, r12) + x = r13 + r14 = f2(x) + r15 = box(int, r14) + CPySequenceTuple_SetItemUnsafe(r3, r4, r15) +L9: + r16 = r4 + 1 + r4 = r16 + goto L3 +L10: a = r3 return 1 +[case testTupleBuiltFromFixedLengthTuple] +def f(val: int) -> bool: + return val % 2 == 0 + +def test() -> None: + source = (1, 2, 3) + a = tuple(f(x) for x in source) +[out] +def f(val): + val, r0 :: int + r1 :: bit +L0: + r0 = CPyTagged_Remainder(val, 4) + r1 = int_eq r0, 0 + return r1 +def test(): + r0, source :: tuple[int, int, int] + r1, r2, r3 :: int + r4, r5, r6 :: object + r7, r8 :: tuple + r9 :: native_int + r10 :: bit + r11 :: object + r12, x :: int + r13 :: bool + r14 :: object + r15 :: native_int + a :: tuple +L0: + r0 = (2, 4, 6) + source = r0 + r1 = source[0] + r2 = source[1] + r3 = source[2] + r4 = box(int, r1) + r5 = box(int, r2) + r6 = box(int, r3) + r7 = PyTuple_Pack(3, r4, r5, r6) + r8 = PyTuple_New(3) + r9 = 0 + goto L2 +L1: + r10 = r9 < 3 :: signed + if r10 goto L2 else goto L4 :: bool +L2: + r11 = CPySequenceTuple_GetItemUnsafe(r7, r9) + r12 = unbox(int, r11) + x = r12 + r13 = f(x) + r14 = box(bool, r13) + CPySequenceTuple_SetItemUnsafe(r8, r9, r14) +L3: + r15 = r9 + 1 + r9 = r15 + goto L1 +L4: + a = r8 + return 1 + +[case testTupleBuiltFromFinalFixedLengthTuple] +from typing import Final + +source: Final = (1, 2, 3) + +def f(val: int) -> bool: + return val % 2 == 0 + +def test() -> None: + a = tuple(f(x) for x in source) +[out] +def f(val): + val, r0 :: int + r1 :: bit +L0: + r0 = CPyTagged_Remainder(val, 4) + r1 = int_eq r0, 0 + return r1 +def test(): + r0 :: tuple[int, int, int] + r1 :: bool + r2, r3, r4 :: int + r5, r6, r7 :: object + r8, r9 :: tuple + r10 :: native_int + r11 :: bit + r12 :: object + r13, x :: int + r14 :: bool + r15 :: object + r16 :: native_int + a :: tuple +L0: + r0 = __main__.source :: static + if is_error(r0) goto L1 else goto L2 +L1: + r1 = raise NameError('value for final name "source" was not set') + unreachable +L2: + r2 = r0[0] + r3 = r0[1] + r4 = r0[2] + r5 = box(int, r2) + r6 = box(int, r3) + r7 = box(int, r4) + r8 = PyTuple_Pack(3, r5, r6, r7) + r9 = PyTuple_New(3) + r10 = 0 + goto L4 +L3: + r11 = r10 < 3 :: signed + if r11 goto L4 else goto L6 :: bool +L4: + r12 = CPySequenceTuple_GetItemUnsafe(r8, r10) + r13 = unbox(int, r12) + x = r13 + r14 = f(x) + r15 = box(bool, r14) + CPySequenceTuple_SetItemUnsafe(r9, r10, r15) +L5: + r16 = r10 + 1 + r10 = r16 + goto L3 +L6: + a = r9 + return 1 + [case testTupleBuiltFromVariableLengthTuple] from typing import Tuple @@ -351,36 +821,191 @@ def test(source): source :: tuple r0 :: native_int r1 :: tuple - r2 :: short_int - r3 :: native_int - r4 :: short_int - r5 :: bit - r6 :: object - r7, x, r8 :: bool - r9 :: object - r10 :: bit - r11 :: short_int + r2 :: native_int + r3 :: bit + r4 :: object + r5, x, r6 :: bool + r7 :: object + r8 :: native_int a :: tuple L0: r0 = var_object_size source r1 = PyTuple_New(r0) r2 = 0 L1: - r3 = var_object_size source - r4 = r3 << 1 - r5 = int_lt r2, r4 - if r5 goto L2 else goto L4 :: bool + r3 = r2 < r0 :: signed + if r3 goto L2 else goto L4 :: bool L2: - r6 = CPySequenceTuple_GetItem(source, r2) - r7 = unbox(bool, r6) - x = r7 - r8 = f(x) - r9 = box(bool, r8) - r10 = CPySequenceTuple_SetItemUnsafe(r1, r2, r9) + r4 = CPySequenceTuple_GetItemUnsafe(source, r2) + r5 = unbox(bool, r4) + x = r5 + r6 = f(x) + r7 = box(bool, r6) + CPySequenceTuple_SetItemUnsafe(r1, r2, r7) L3: - r11 = r2 + 2 - r2 = r11 + r8 = r2 + 1 + r2 = r8 goto L1 L4: a = r1 return 1 + +[case testTupleBuiltFromStars] +from typing import Final + +abc: Final = "abc" + +def test() -> None: + a = tuple(str(x) for x in [*abc, *"def", *b"ghi", ("j", "k"), *("l", "m", "n")]) + +[out] +def test(): + r0, r1 :: str + r2 :: bytes + r3, r4 :: str + r5 :: tuple[str, str] + r6, r7, r8 :: str + r9 :: tuple[str, str, str] + r10 :: list + r11, r12, r13, r14 :: object + r15 :: i32 + r16 :: bit + r17, r18 :: object + r19 :: tuple + r20, r21 :: native_int + r22 :: bit + r23, x :: object + r24 :: str + r25 :: native_int + a :: tuple +L0: + r0 = 'abc' + r1 = 'def' + r2 = b'ghi' + r3 = 'j' + r4 = 'k' + r5 = (r3, r4) + r6 = 'l' + r7 = 'm' + r8 = 'n' + r9 = (r6, r7, r8) + r10 = PyList_New(0) + r11 = CPyList_Extend(r10, r0) + r12 = CPyList_Extend(r10, r1) + r13 = CPyList_Extend(r10, r2) + r14 = box(tuple[str, str], r5) + r15 = PyList_Append(r10, r14) + r16 = r15 >= 0 :: signed + r17 = box(tuple[str, str, str], r9) + r18 = CPyList_Extend(r10, r17) + r19 = PyTuple_New(13) + r20 = 0 + goto L2 +L1: + r21 = var_object_size r10 + r22 = r20 < r21 :: signed + if r22 goto L2 else goto L4 :: bool +L2: + r23 = list_get_item_unsafe r10, r20 + x = r23 + r24 = PyObject_Str(x) + CPySequenceTuple_SetItemUnsafe(r19, r20, r24) +L3: + r25 = r20 + 1 + r20 = r25 + goto L1 +L4: + a = r19 + return 1 + +[case testTupleAdd] +from typing import Tuple +def f(a: Tuple[int, ...], b: Tuple[int, ...]) -> None: + c = a + b + d = a + (1, 2) +def g(a: Tuple[int, int], b: Tuple[int, int]) -> None: + c = a + b +[out] +def f(a, b): + a, b, r0, c :: tuple + r1 :: tuple[int, int] + r2 :: object + r3, d :: tuple +L0: + r0 = PySequence_Concat(a, b) + c = r0 + r1 = (2, 4) + r2 = box(tuple[int, int], r1) + r3 = PySequence_Concat(a, r2) + d = r3 + return 1 +def g(a, b): + a, b :: tuple[int, int] + r0, r1 :: object + r2 :: tuple + r3, c :: tuple[int, int, int, int] +L0: + r0 = box(tuple[int, int], a) + r1 = box(tuple[int, int], b) + r2 = PySequence_Concat(r0, r1) + r3 = unbox(tuple[int, int, int, int], r2) + c = r3 + return 1 + +[case testTupleMultiply] +from typing import Tuple +def f(a: Tuple[int]) -> None: + b = a * 2 + c = 3 * (2,) +def g(a: Tuple[int, ...]) -> None: + b = a * 2 +[out] +def f(a): + a :: tuple[int] + r0 :: object + r1 :: tuple + r2, b :: tuple[int, int] + r3 :: tuple[int] + r4 :: object + r5 :: tuple + r6, c :: tuple[int, int, int] +L0: + r0 = box(tuple[int], a) + r1 = CPySequence_Multiply(r0, 4) + r2 = unbox(tuple[int, int], r1) + b = r2 + r3 = (4) + r4 = box(tuple[int], r3) + r5 = CPySequence_RMultiply(6, r4) + r6 = unbox(tuple[int, int, int], r5) + c = r6 + return 1 +def g(a): + a, r0, b :: tuple +L0: + r0 = CPySequence_Multiply(a, 4) + b = r0 + return 1 + +[case testTupleFloatElementComparison] +def f(x: tuple[float], y: tuple[float]) -> bool: + return x == y + +[out] +def f(x, y): + x, y :: tuple[float] + r0, r1 :: float + r2 :: bit + r3 :: bool +L0: + r0 = x[0] + r1 = y[0] + r2 = r0 == r1 + if not r2 goto L1 else goto L2 :: bool +L1: + r3 = 0 + goto L3 +L2: + r3 = 1 +L3: + return r3 diff --git a/mypyc/test-data/irbuild-unreachable.test b/mypyc/test-data/irbuild-unreachable.test index b5188c91ac580..8eafede66b56e 100644 --- a/mypyc/test-data/irbuild-unreachable.test +++ b/mypyc/test-data/irbuild-unreachable.test @@ -11,50 +11,27 @@ def f(): r1 :: str r2 :: object r3, r4 :: str - r5 :: i32 - r6 :: bit - r7 :: object - r8, r9, r10 :: bit - r11, r12 :: bool - r13 :: object - r14 :: str - r15 :: object - r16 :: tuple[int, int] - r17, r18 :: object - r19, y :: bool + r5, r6, r7 :: bool + r8 :: object + r9, y :: bool L0: r0 = sys :: module r1 = 'platform' r2 = CPyObject_GetAttr(r0, r1) r3 = cast(str, r2) r4 = 'x' - r5 = PyUnicode_Compare(r3, r4) - r6 = r5 == -1 - if r6 goto L1 else goto L3 :: bool + r5 = CPyStr_EqualLiteral(r3, r4, 1) + if r5 goto L2 else goto L1 :: bool L1: - r7 = PyErr_Occurred() - r8 = r7 != 0 - if r8 goto L2 else goto L3 :: bool + r6 = r5 + goto L3 L2: - r9 = CPy_KeepPropagating() + r7 = raise RuntimeError('mypyc internal error: should be unreachable') + r8 = box(None, 1) + r9 = unbox(bool, r8) + r6 = r9 L3: - r10 = r5 == 0 - if r10 goto L5 else goto L4 :: bool -L4: - r11 = r10 - goto L6 -L5: - r12 = raise RuntimeError('mypyc internal error: should be unreachable') - r13 = box(None, 1) - r14 = 'version_info' - r15 = CPyObject_GetAttr(r13, r14) - r16 = (6, 10) - r17 = box(tuple[int, int], r16) - r18 = PyObject_RichCompare(r15, r17, 4) - r19 = unbox(bool, r18) - r11 = r19 -L6: - y = r11 + y = r6 return 1 [case testUnreachableNameExpr] @@ -68,41 +45,27 @@ def f(): r1 :: str r2 :: object r3, r4 :: str - r5 :: i32 - r6 :: bit - r7 :: object - r8, r9, r10 :: bit - r11, r12 :: bool - r13 :: object - r14, y :: bool + r5, r6, r7 :: bool + r8 :: object + r9, y :: bool L0: r0 = sys :: module r1 = 'platform' r2 = CPyObject_GetAttr(r0, r1) r3 = cast(str, r2) r4 = 'x' - r5 = PyUnicode_Compare(r3, r4) - r6 = r5 == -1 - if r6 goto L1 else goto L3 :: bool + r5 = CPyStr_EqualLiteral(r3, r4, 1) + if r5 goto L2 else goto L1 :: bool L1: - r7 = PyErr_Occurred() - r8 = r7 != 0 - if r8 goto L2 else goto L3 :: bool + r6 = r5 + goto L3 L2: - r9 = CPy_KeepPropagating() + r7 = raise RuntimeError('mypyc internal error: should be unreachable') + r8 = box(None, 1) + r9 = unbox(bool, r8) + r6 = r9 L3: - r10 = r5 == 0 - if r10 goto L5 else goto L4 :: bool -L4: - r11 = r10 - goto L6 -L5: - r12 = raise RuntimeError('mypyc internal error: should be unreachable') - r13 = box(None, 1) - r14 = unbox(bool, r13) - r11 = r14 -L6: - y = r11 + y = r6 return 1 [case testUnreachableStatementAfterReturn] @@ -205,7 +168,7 @@ L1: r0 = builtins :: module r1 = 'ValueError' r2 = CPyObject_GetAttr(r0, r1) - r3 = PyObject_CallFunctionObjArgs(r2, 0) + r3 = PyObject_Vectorcall(r2, 0, 0, 0) CPy_Raise(r3) unreachable L2: @@ -223,7 +186,10 @@ def f(x): r1 :: str r2 :: object r3 :: str - r4, r5 :: object + r4 :: object + r5 :: object[1] + r6 :: object_ptr + r7 :: object L0: if x goto L1 else goto L4 :: bool L1: @@ -236,6 +202,9 @@ L3: r2 = builtins :: module r3 = 'print' r4 = CPyObject_GetAttr(r2, r3) - r5 = PyObject_CallFunctionObjArgs(r4, r1, 0) + r5 = [r1] + r6 = load_address r5 + r7 = PyObject_Vectorcall(r4, r6, 1, 0) + keep_alive r1 L4: return 4 diff --git a/mypyc/test-data/irbuild-vectorcall.test b/mypyc/test-data/irbuild-vectorcall.test index 1ba08efc25019..15e717191ff05 100644 --- a/mypyc/test-data/irbuild-vectorcall.test +++ b/mypyc/test-data/irbuild-vectorcall.test @@ -3,7 +3,7 @@ -- Vectorcalls are faster than the legacy API, especially with keyword arguments, -- since there is no need to allocate a temporary dictionary for keyword args. -[case testeVectorcallBasic_python3_8] +[case testeVectorcallBasic] from typing import Any def f(c: Any) -> None: @@ -17,16 +17,16 @@ def f(c): r4 :: object_ptr r5 :: object L0: - r0 = _PyObject_Vectorcall(c, 0, 0, 0) + r0 = PyObject_Vectorcall(c, 0, 0, 0) r1 = 'x' r2 = 'y' r3 = [r1, r2] r4 = load_address r3 - r5 = _PyObject_Vectorcall(c, r4, 2, 0) + r5 = PyObject_Vectorcall(c, r4, 2, 0) keep_alive r1, r2 return 1 -[case testVectorcallKeywords_python3_8] +[case testVectorcallKeywords] from typing import Any def f(c: Any) -> None: @@ -48,7 +48,7 @@ L0: r1 = [r0] r2 = load_address r1 r3 = ('x',) - r4 = _PyObject_Vectorcall(c, r2, 0, r3) + r4 = PyObject_Vectorcall(c, r2, 0, r3) keep_alive r0 r5 = 'x' r6 = 'y' @@ -56,43 +56,11 @@ L0: r8 = [r5, r6, r7] r9 = load_address r8 r10 = ('a', 'b') - r11 = _PyObject_Vectorcall(c, r9, 1, r10) + r11 = PyObject_Vectorcall(c, r9, 1, r10) keep_alive r5, r6, r7 return 1 -[case testVectorcallMethod_python3_8] -from typing import Any - -def f(o: Any) -> None: - # On Python 3.8 vectorcalls are only faster with keyword args - o.m('x') - o.m('x', a='y') -[out] -def f(o): - o :: object - r0, r1 :: str - r2 :: object - r3, r4, r5 :: str - r6 :: object - r7 :: object[2] - r8 :: object_ptr - r9, r10 :: object -L0: - r0 = 'x' - r1 = 'm' - r2 = CPyObject_CallMethodObjArgs(o, r1, r0, 0) - r3 = 'x' - r4 = 'y' - r5 = 'm' - r6 = CPyObject_GetAttr(o, r5) - r7 = [r3, r4] - r8 = load_address r7 - r9 = ('a',) - r10 = _PyObject_Vectorcall(r6, r8, 1, r9) - keep_alive r3, r4 - return 1 - -[case testVectorcallMethod_python3_9_64bit] +[case testVectorcallMethod_64bit] from typing import Any def f(o: Any) -> None: @@ -128,7 +96,7 @@ L0: keep_alive o, r5, r6, r7 return 1 -[case testVectorcallMethod_python3_9_32bit] +[case testVectorcallMethod_32bit] from typing import Any def f(o: Any) -> None: diff --git a/mypyc/test-data/irbuild-weakref.test b/mypyc/test-data/irbuild-weakref.test new file mode 100644 index 0000000000000..2180b1e747aab --- /dev/null +++ b/mypyc/test-data/irbuild-weakref.test @@ -0,0 +1,103 @@ +[case testWeakrefRef] +import weakref +from typing import Any, Callable +def f(x: object) -> object: + return weakref.ref(x) + +[out] +def f(x): + x, r0 :: object +L0: + r0 = PyWeakref_NewRef(x, 0) + return r0 + +[case testWeakrefRefCallback] +import weakref +from typing import Any, Callable +def f(x: object, cb: Callable[[object], Any]) -> object: + return weakref.ref(x, cb) + +[out] +def f(x, cb): + x, cb, r0 :: object +L0: + r0 = PyWeakref_NewRef(x, cb) + return r0 + +[case testFromWeakrefRef] +from typing import Any, Callable +from weakref import ref +def f(x: object) -> object: + return ref(x) + +[out] +def f(x): + x, r0 :: object +L0: + r0 = PyWeakref_NewRef(x, 0) + return r0 + +[case testFromWeakrefRefCallback] +from typing import Any, Callable +from weakref import ref +def f(x: object, cb: Callable[[object], Any]) -> object: + return ref(x, cb) + +[out] +def f(x, cb): + x, cb, r0 :: object +L0: + r0 = PyWeakref_NewRef(x, cb) + return r0 + +[case testWeakrefProxy] +import weakref +from typing import Any, Callable +def f(x: object) -> object: + return weakref.proxy(x) + +[out] +def f(x): + x, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, 0) + return r0 + +[case testWeakrefProxyCallback] +import weakref +from typing import Any, Callable +def f(x: object, cb: Callable[[object], Any]) -> object: + return weakref.proxy(x, cb) + +[out] +def f(x, cb): + x, cb, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, cb) + return r0 + +[case testFromWeakrefProxy] +from typing import Any, Callable +from weakref import proxy +def f(x: object) -> object: + return proxy(x) + +[out] +def f(x): + x, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, 0) + return r0 + +[case testFromWeakrefProxyCallback] +from typing import Any, Callable +from weakref import proxy +def f(x: object, cb: Callable[[object], Any]) -> object: + return proxy(x, cb) + +[out] +def f(x, cb): + x, cb, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, cb) + return r0 diff --git a/mypyc/test-data/lowering-int.test b/mypyc/test-data/lowering-int.test index e7df944c44581..c2bcba54e444d 100644 --- a/mypyc/test-data/lowering-int.test +++ b/mypyc/test-data/lowering-int.test @@ -332,7 +332,7 @@ L4: L5: return 4 -[case testLowerIntForLoop] +[case testLowerIntForLoop_64bit] from __future__ import annotations def f(l: list[int]) -> None: @@ -341,37 +341,42 @@ def f(l: list[int]) -> None: [out] def f(l): l :: list - r0 :: short_int + r0 :: native_int r1 :: ptr r2 :: native_int - r3 :: short_int - r4 :: bit - r5 :: object - r6, x :: int - r7 :: short_int - r8 :: None + r3 :: bit + r4, r5 :: ptr + r6 :: native_int + r7 :: ptr + r8 :: object + r9, x :: int + r10 :: native_int + r11 :: None L0: r0 = 0 L1: r1 = get_element_ptr l ob_size :: PyVarObject r2 = load_mem r1 :: native_int* - r3 = r2 << 1 - r4 = r0 < r3 :: signed - if r4 goto L2 else goto L5 :: bool + r3 = r0 < r2 :: signed + if r3 goto L2 else goto L5 :: bool L2: - r5 = CPyList_GetItemUnsafe(l, r0) - r6 = unbox(int, r5) - dec_ref r5 - if is_error(r6) goto L6 (error at f:4) else goto L3 + r4 = get_element_ptr l ob_item :: PyListObject + r5 = load_mem r4 :: ptr* + r6 = r0 * 8 + r7 = r5 + r6 + r8 = load_mem r7 :: builtins.object* + r9 = unbox(int, r8) + dec_ref r8 + if is_error(r9) goto L6 (error at f:4) else goto L3 L3: - x = r6 + x = r9 dec_ref x :: int L4: - r7 = r0 + 2 - r0 = r7 + r10 = r0 + 1 + r0 = r10 goto L1 L5: return 1 L6: - r8 = :: None - return r8 + r11 = :: None + return r11 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index e719ecb2afe14..a71c53041cf7e 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -642,15 +642,15 @@ def g() -> Tuple[C, C]: [out] def f(): r0 :: tuple[__main__.C, __main__.C] - r1, r2, x, r3, r4, y :: __main__.C + r1, r2, r3, x, r4, y :: __main__.C r5, r6, r7 :: int L0: r0 = g() r1 = borrow r0[0] - r2 = unborrow r1 - x = r2 - r3 = borrow r0[1] - r4 = unborrow r3 + r2 = borrow r0[1] + r3 = unborrow r1 + x = r3 + r4 = unborrow r2 y = r4 r5 = borrow x.a r6 = borrow y.a @@ -684,22 +684,18 @@ def g(x: str) -> int: [out] def g(x): x :: str - r0 :: object - r1 :: str - r2 :: tuple - r3 :: object - r4 :: dict - r5 :: object + r0, r1 :: object + r2 :: object[2] + r3 :: object_ptr + r4, r5 :: object r6 :: int L0: r0 = load_address PyLong_Type - r1 = 'base' - r2 = PyTuple_Pack(1, x) - r3 = object 2 - r4 = CPyDict_Build(1, r1, r3) - r5 = PyObject_Call(r0, r2, r4) - dec_ref r2 - dec_ref r4 + r1 = object 2 + r2 = [x, r1] + r3 = load_address r2 + r4 = ('base',) + r5 = PyObject_Vectorcall(r0, r3, 1, r4) r6 = unbox(int, r5) dec_ref r5 return r6 @@ -734,49 +730,47 @@ def f(d): d :: dict r0 :: short_int r1 :: native_int - r2 :: short_int - r3 :: object - r4 :: tuple[bool, short_int, object] - r5 :: short_int - r6 :: bool - r7 :: object - r8, key :: int - r9, r10 :: object - r11 :: int - r12, r13 :: bit + r2 :: object + r3 :: tuple[bool, short_int, object] + r4 :: short_int + r5 :: bool + r6 :: object + r7, key :: int + r8, r9 :: object + r10 :: int + r11, r12 :: bit L0: r0 = 0 r1 = PyDict_Size(d) - r2 = r1 << 1 - r3 = CPyDict_GetKeysIter(d) + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r0) - r5 = r4[1] - r0 = r5 - r6 = r4[0] - if r6 goto L2 else goto L6 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r7 = r4[2] - dec_ref r4 - r8 = unbox(int, r7) - dec_ref r7 - key = r8 - r9 = box(int, key) - r10 = CPyDict_GetItem(d, r9) + r6 = r3[2] + dec_ref r3 + r7 = unbox(int, r6) + dec_ref r6 + key = r7 + r8 = box(int, key) + r9 = CPyDict_GetItem(d, r8) + dec_ref r8 + r10 = unbox(int, r9) dec_ref r9 - r11 = unbox(int, r10) - dec_ref r10 - dec_ref r11 :: int + dec_ref r10 :: int L3: - r12 = CPyDict_CheckSize(d, r2) + r11 = CPyDict_CheckSize(d, r1) goto L1 L4: - r13 = CPy_NoErrOccured() + r12 = CPy_NoErrOccurred() L5: return 1 L6: + dec_ref r2 dec_ref r3 - dec_ref r4 goto L4 [case testBorrowRefs] @@ -804,6 +798,31 @@ L2: L3: return 1 +[case testTupleUnpackUnused] +from typing import Tuple + +def f(x: Tuple[str, int]) -> int: + a, xi = x + return 0 +[out] +def f(x): + x :: tuple[str, int] + r0 :: str + r1 :: int + r2, a :: str + r3, xi :: int +L0: + r0 = borrow x[0] + r1 = borrow x[1] + inc_ref x + r2 = unborrow r0 + a = r2 + dec_ref a + r3 = unborrow r1 + xi = r3 + dec_ref xi :: int + return 0 + [case testGetElementPtrLifeTime] from typing import List @@ -875,7 +894,7 @@ L11: xdec_ref y :: int goto L6 -[case testVectorcall_python3_8] +[case testVectorcall] from typing import Any def call(f: Any, x: int) -> int: @@ -894,13 +913,13 @@ L0: r0 = box(int, x) r1 = [r0] r2 = load_address r1 - r3 = _PyObject_Vectorcall(f, r2, 1, 0) + r3 = PyObject_Vectorcall(f, r2, 1, 0) dec_ref r0 r4 = unbox(int, r3) dec_ref r3 return r4 -[case testVectorcallMethod_python3_9_64bit] +[case testVectorcallMethod_64bit] from typing import Any def call(o: Any, x: int) -> int: @@ -1098,7 +1117,7 @@ L0: r0 = borrow x.a r1 = __main__.D :: type r2 = get_element_ptr r0 ob_type :: PyObject - r3 = load_mem r2 :: builtins.object* + r3 = borrow load_mem r2 :: builtins.object* r4 = r3 == r1 if r4 goto L1 else goto L2 :: bool L1: @@ -1343,7 +1362,7 @@ L0: return r2 [case testBorrowIntCompareFinal] -from typing_extensions import Final +from typing import Final X: Final = 10 diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index 8488632e6574f..39410e00a0247 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -1,7 +1,10 @@ # async test cases (compile and run) -[case testAsync] +[case testRunAsyncBasics] import asyncio +from typing import Callable, Awaitable + +from testutil import assertRaises async def h() -> int: return 1 @@ -11,19 +14,217 @@ async def g() -> int: return await h() async def f() -> int: - return await g() + return await g() + 2 + +async def f2() -> int: + x = 0 + for i in range(2): + x += i + await f() + await g() + return x + +async def test_simple_call() -> None: + result = await f() + assert result == 3 + +async def test_multiple_awaits_in_expression() -> None: + result = await f2() + assert result == 9 + +class MyError(Exception): + pass + +async def exc1() -> None: + await asyncio.sleep(0) + raise MyError() + +async def exc2() -> None: + await asyncio.sleep(0) + raise MyError() + +async def exc3() -> None: + await exc1() + +async def exc4() -> None: + await exc2() + +async def exc5() -> int: + try: + await exc1() + except MyError: + return 3 + return 4 + +async def exc6() -> int: + try: + await exc4() + except MyError: + return 3 + return 4 + +async def test_exception() -> None: + with assertRaises(MyError): + await exc1() + with assertRaises(MyError): + await exc2() + with assertRaises(MyError): + await exc3() + with assertRaises(MyError): + await exc4() + assert await exc5() == 3 + assert await exc6() == 3 + +async def indirect_call(x: int, c: Callable[[int], Awaitable[int]]) -> int: + return await c(x) + +async def indirect_call_2(a: Awaitable[None]) -> None: + await a + +async def indirect_call_3(a: Awaitable[float]) -> float: + return (await a) + 1.0 + +async def inc(x: int) -> int: + await asyncio.sleep(0) + return x + 1 + +async def ident(x: float, err: bool = False) -> float: + await asyncio.sleep(0.0) + if err: + raise MyError() + return x + float("0.0") + +async def test_indirect_call() -> None: + assert await indirect_call(3, inc) == 4 + + with assertRaises(MyError): + await indirect_call_2(exc1()) + + assert await indirect_call_3(ident(2.0)) == 3.0 + assert await indirect_call_3(ident(-113.0)) == -112.0 + assert await indirect_call_3(ident(-114.0)) == -113.0 + + with assertRaises(MyError): + await indirect_call_3(ident(1.0, True)) + with assertRaises(MyError): + await indirect_call_3(ident(-113.0, True)) + +class C: + def __init__(self, n: int) -> None: + self.n = n + + async def add(self, x: int, err: bool = False) -> int: + await asyncio.sleep(0) + if err: + raise MyError() + return x + self.n + +async def method_call(x: int) -> int: + c = C(5) + return await c.add(x) + +async def method_call_exception() -> int: + c = C(5) + return await c.add(3, err=True) + +async def test_async_method_call() -> None: + assert await method_call(3) == 8 + with assertRaises(MyError): + await method_call_exception() [file asyncio/__init__.pyi] async def sleep(t: float) -> None: ... [typing fixtures/typing-full.pyi] -[file driver.py] -from native import f +[case testRunAsyncAwaitInVariousPositions] +from typing import cast, Any + import asyncio -result = asyncio.run(f()) -assert result == 1 +async def one() -> int: + await asyncio.sleep(0.0) + return int() + 1 + +async def true() -> bool: + return bool(int() + await one()) + +async def branch_await() -> int: + if bool(int() + 1) == await true(): + return 3 + return 2 + +async def branch_await_not() -> int: + if bool(int() + 1) == (not await true()): + return 3 + return 2 + +async def test_branch() -> None: + assert await branch_await() == 3 + assert await branch_await_not() == 2 + +async def assign_multi() -> int: + _, x = int(), await one() + return x + 1 + +async def test_assign_multi() -> None: + assert await assign_multi() == 2 + +class C: + def __init__(self, s: str) -> None: + self.s = s + + def concat(self, s: str) -> str: + return self.s + s + +async def make_c(s: str) -> C: + await one() + return C(s) + +async def concat(s: str, t: str) -> str: + await one() + return s + t + +async def set_attr(s: str) -> None: + (await make_c("xyz")).s = await concat(s, "!") + +async def test_set_attr() -> None: + await set_attr("foo") # Just check that it compiles and runs + +def concat2(x: str, y: str) -> str: + return x + y + +async def call1(s: str) -> str: + return concat2(str(int()), await concat(s, "a")) + +async def call2(s: str) -> str: + return await concat(str(int()), await concat(s, "b")) + +async def test_call() -> None: + assert await call1("foo") == "0fooa" + assert await call2("foo") == "0foob" + +async def method_call(s: str) -> str: + return C("<").concat(await concat(s, ">")) + +async def test_method_call() -> None: + assert await method_call("foo") == "" + +class D: + def __init__(self, a: str, b: str) -> None: + self.a = a + self.b = b + +async def construct(s: str) -> str: + c = D(await concat(s, "!"), await concat(s, "?")) + return c.a + c.b + +async def test_construct() -> None: + assert await construct("foo") == "foo!foo?" + +[file asyncio/__init__.pyi] +async def sleep(t: float) -> None: ... + +[typing fixtures/typing-full.pyi] + [case testAsyncWith] from testutil import async_val @@ -68,7 +269,6 @@ yields, val = run_generator(async_return()) assert yields == ('foo',) assert val == 'test', val - [case testAsyncFor] from typing import AsyncIterable, List, Set, Dict @@ -157,7 +357,7 @@ class ConManB: async def __aexit__(self, *exc: object): pass -async def x() -> None: +async def test_x() -> None: value = 2 async with ConMan() as f: value += f @@ -166,8 +366,1036 @@ async def x() -> None: value += f assert value == 5, value +[case testRunAsyncSpecialCases] +import asyncio + +async def t() -> tuple[int, str, str]: + return (1, "x", "y") + +async def f() -> tuple[int, str, str]: + return await t() + +async def test_tuple_return() -> None: + result = await f() + assert result == (1, "x", "y") + +async def e() -> ValueError: + return ValueError("foo") + +async def g() -> ValueError: + return await e() + +async def test_exception_return() -> None: + result = await g() + assert isinstance(result, ValueError) + +[file asyncio/__init__.pyi] +async def sleep(t: float) -> None: ... + [typing fixtures/typing-full.pyi] -[file driver.py] + +[case testRunAsyncRefCounting] +import asyncio +import gc + +async def assert_no_leaks(fn, max_new): + # Warm-up, in case asyncio allocates something on first use + await fn() + + gc.collect() + old_objs = gc.get_objects() + + for i in range(10): + await fn() + + gc.collect() + new_objs = gc.get_objects() + + delta = len(new_objs) - len(old_objs) + # Often a few persistent objects get allocated, which may be unavoidable. + # The main thing we care about is that each iteration does not leak an + # additional object. + assert delta <= max_new, delta + +async def concat_one(x: str) -> str: + return x + "1" + +async def foo(n: int) -> str: + s = "" + while len(s) < n: + s = await concat_one(s) + return s + +async def test_trivial() -> None: + await assert_no_leaks(lambda: foo(1000), 5) + +async def make_list(a: list[int]) -> list[int]: + await concat_one("foobar") + return [a[0]] + +async def spill() -> list[int]: + a: list[int] = [] + for i in range(5): + await asyncio.sleep(0.0001) + a = (await make_list(a + [1])) + a + (await make_list(a + [2])) + return a + +async def bar(n: int) -> None: + for i in range(n): + await spill() + +async def test_spilled() -> None: + await assert_no_leaks(lambda: bar(80), 2) + +async def raise_deep(n: int) -> str: + if n == 0: + await asyncio.sleep(0.0001) + raise TypeError(str(n)) + else: + if n == 2: + await asyncio.sleep(0.0001) + return await raise_deep(n - 1) + +async def maybe_raise(n: int) -> str: + if n % 3 == 0: + await raise_deep(5) + elif n % 29 == 0: + await asyncio.sleep(0.0001) + return str(n) + +async def exc(n: int) -> list[str]: + a = [] + for i in range(n): + try: + a.append(str(int()) + await maybe_raise(n)) + except TypeError: + a.append(str(int() + 5)) + return a + +async def test_exception() -> None: + await assert_no_leaks(lambda: exc(50), 2) + +class C: + def __init__(self, s: str) -> None: + self.s = s + +async def id(c: C) -> C: + return c + +async def stolen_helper(c: C, s: str) -> str: + await asyncio.sleep(0.0001) + (await id(c)).s = await concat_one(s) + await asyncio.sleep(0.0001) + return c.s + +async def stolen(n: int) -> int: + for i in range(n): + c = C(str(i)) + s = await stolen_helper(c, str(i + 2)) + assert s == str(i + 2) + "1" + return n + +async def test_stolen() -> None: + await assert_no_leaks(lambda: stolen(200), 2) + +[file asyncio/__init__.pyi] +async def sleep(t: float) -> None: ... + +[case testRunAsyncMiscTypesInEnvironment] +# Here we test that values of various kinds of types can be spilled to the +# environment. In particular, types with "overlapping error values" such as +# i64 can be tricky, since they require extra work to support undefined +# attribute values (which raise AttributeError when accessed). For these, +# the object struct has a bitfield which keeps track of whether certain +# attributes have an assigned value. +# +# In practice we mark these attributes as "always defined", which causes these +# checks to be skipped on attribute access, and thus we don't require the +# bitfield to exist. +# +# See the comment of RType.error_overlap for more information. + +import asyncio + +from mypy_extensions import i64, i32, i16, u8 + +async def inc_float(x: float) -> float: + return x + 1.0 + +async def inc_i64(x: i64) -> i64: + return x + 1 + +async def inc_i32(x: i32) -> i32: + return x + 1 + +async def inc_i16(x: i16) -> i16: + return x + 1 + +async def inc_u8(x: u8) -> u8: + return x + 1 + +async def inc_tuple(x: tuple[i64, float]) -> tuple[i64, float]: + return x[0] + 1, x[1] + 1.5 + +async def neg_bool(b: bool) -> bool: + return not b + +async def float_ops(x: float) -> float: + n = x + n = await inc_float(n) + n = float("0.5") + await inc_float(n) + return n + +async def test_float() -> None: + assert await float_ops(2.5) == 5.0 + +async def i64_ops(x: i64) -> i64: + n = x + n = await inc_i64(n) + n = i64("1") + await inc_i64(n) + return n + +async def test_i64() -> None: + assert await i64_ops(2) == 5 + +async def i32_ops(x: i32) -> i32: + n = x + n = await inc_i32(n) + n = i32("1") + await inc_i32(n) + return n + +async def test_i32() -> None: + assert await i32_ops(3) == 6 + +async def i16_ops(x: i16) -> i16: + n = x + n = await inc_i16(n) + n = i16("1") + await inc_i16(n) + return n + +async def test_i16() -> None: + assert await i16_ops(4) == 7 + +async def u8_ops(x: u8) -> u8: + n = x + n = await inc_u8(n) + n = u8("1") + await inc_u8(n) + return n + +async def test_u8() -> None: + assert await u8_ops(5) == 8 + +async def tuple_ops(x: tuple[i64, float]) -> tuple[i64, float]: + n = x + n = await inc_tuple(n) + m = ((i64("1"), float("0.5")), await inc_tuple(n)) + return m[1] + +async def test_tuple() -> None: + assert await tuple_ops((1, 2.5)) == (3, 5.5) + +async def bool_ops(x: bool) -> bool: + n = x + n = await neg_bool(n) + m = (bool("1"), await neg_bool(n)) + return m[0] and m[1] + +async def test_bool() -> None: + assert await bool_ops(True) is True + assert await bool_ops(False) is False + +[file asyncio/__init__.pyi] +def run(x: object) -> object: ... + +[case testRunAsyncNestedFunctions] +from __future__ import annotations + import asyncio -import native -asyncio.run(native.x()) +from typing import cast, Iterator, overload, Awaitable, Any, TypeVar + +from testutil import assertRaises + +def normal_contains_async_def(x: int) -> int: + async def f(y: int) -> int: + return x + y + + return 5 + cast(int, asyncio.run(f(6))) + +def test_def_contains_async_def() -> None: + assert normal_contains_async_def(3) == 14 + +async def inc(x: int) -> int: + return x + 1 + +async def async_def_contains_normal(x: int) -> int: + def nested(y: int, z: int) -> int: + return x + y + z + + a = x + a += nested((await inc(3)), (await inc(4))) + return a + +async def test_async_def_contains_normal() -> None: + assert await async_def_contains_normal(2) == (2 + 2 + 4 + 5) + +async def async_def_contains_async_def(x: int) -> int: + async def f(y: int) -> int: + return (await inc(x)) + (await inc(y)) + + return (await f(1)) + (await f(2)) + +async def test_async_def_contains_async_def() -> None: + assert await async_def_contains_async_def(3) == (3 + 1 + 1 + 1) + (3 + 1 + 2 + 1) + +async def async_def_contains_generator(x: int) -> tuple[int, int, int]: + def gen(y: int) -> Iterator[int]: + yield x + 1 + yield x + y + + it = gen(4) + res = x + 10, next(it), next(it) + + with assertRaises(StopIteration): + next(it) + + return res + +async def test_async_def_contains_generator() -> None: + assert await async_def_contains_generator(3) == (13, 4, 7) + +def generator_contains_async_def(x: int) -> Iterator[int]: + async def f(y: int) -> int: + return (await inc(x)) + (await inc(y)) + + yield cast(int, asyncio.run(f(2))) + yield cast(int, asyncio.run(f(3))) + yield x + 10 + +def test_generator_contains_async_def() -> None: + assert list(generator_contains_async_def(5)) == [6 + 3, 6 + 4, 15] + +async def async_def_contains_two_nested_functions(x: int, y: int) -> tuple[int, int]: + def f(a: int) -> int: + return x + a + + def g(b: int, c: int) -> int: + return y + b + c + + return (await inc(f(3))), (await inc(g(4, 10))) + +async def test_async_def_contains_two_nested_functions() -> None: + assert await async_def_contains_two_nested_functions(5, 7) == ( + (5 + 3 + 1), (7 + 4 + 10 + 1) + ) + +async def async_def_contains_overloaded_async_def(n: int) -> int: + @overload + async def f(x: int) -> int: ... + + @overload + async def f(x: str) -> str: ... + + async def f(x: int | str) -> Any: + return x + + return (await f(n)) + 1 + + +async def test_async_def_contains_overloaded_async_def() -> None: + assert await async_def_contains_overloaded_async_def(5) == 6 + +T = TypeVar("T") + +def deco(f: T) -> T: + return f + +async def async_def_contains_decorated_async_def(n: int) -> int: + @deco + async def f(x: int) -> int: + return x + 2 + + return (await f(n)) + 1 + + +async def test_async_def_contains_decorated_async_def() -> None: + assert await async_def_contains_decorated_async_def(7) == 10 + +[file asyncio/__init__.pyi] +def run(x: object) -> object: ... + +[case testAsyncTryFinallyMixedReturn] +# This used to raise an AttributeError, when: +# - the try block contains multiple paths +# - at least one of those explicitly returns +# - at least one of those does not explicitly return +# - the non-returning path is taken at runtime + +async def mixed_return(b: bool) -> bool: + try: + if b: + return b + finally: + pass + return b + + +async def test_async_try_finally_mixed_return() -> None: + # Test return path + result1 = await mixed_return(True) + assert result1 == True + + # Test non-return path + result2 = await mixed_return(False) + assert result2 == False + +[case testAsyncWithMixedReturn] +# This used to raise an AttributeError, related to +# testAsyncTryFinallyMixedReturn, this is essentially +# a far more extensive version of that test surfacing +# more edge cases + +from typing import Optional, Type, Literal + + +class AsyncContextManager: + async def __aenter__(self) -> "AsyncContextManager": + return self + + async def __aexit__( + self, + t: Optional[Type[BaseException]], + v: Optional[BaseException], + tb: object, + ) -> Literal[False]: + return False + + +# Simple async functions (generator class) +async def gen_1(b: bool) -> bool: + async with AsyncContextManager(): + if b: + return b + return b + + +async def gen_2(b: bool) -> bool: + async with AsyncContextManager(): + if b: + return b + else: + return b + + +async def gen_3(b: bool) -> bool: + async with AsyncContextManager(): + if b: + return b + else: + pass + return b + + +async def gen_4(b: bool) -> bool: + ret: bool + async with AsyncContextManager(): + if b: + ret = b + else: + ret = b + return ret + + +async def gen_5(i: int) -> int: + async with AsyncContextManager(): + if i == 1: + return i + elif i == 2: + pass + elif i == 3: + return i + return i + + +async def gen_6(i: int) -> int: + async with AsyncContextManager(): + if i == 1: + return i + elif i == 2: + return i + elif i == 3: + return i + return i + + +async def gen_7(i: int) -> int: + async with AsyncContextManager(): + if i == 1: + return i + elif i == 2: + return i + elif i == 3: + return i + else: + return i + + +# Async functions with nested functions (environment class) +async def env_1(b: bool) -> bool: + def helper() -> bool: + return True + + async with AsyncContextManager(): + if b: + return helper() + return b + + +async def env_2(b: bool) -> bool: + def helper() -> bool: + return True + + async with AsyncContextManager(): + if b: + return helper() + else: + return b + + +async def env_3(b: bool) -> bool: + def helper() -> bool: + return True + + async with AsyncContextManager(): + if b: + return helper() + else: + pass + return b + + +async def env_4(b: bool) -> bool: + def helper() -> bool: + return True + + ret: bool + async with AsyncContextManager(): + if b: + ret = helper() + else: + ret = b + return ret + + +async def env_5(i: int) -> int: + def helper() -> int: + return 1 + + async with AsyncContextManager(): + if i == 1: + return helper() + elif i == 2: + pass + elif i == 3: + return i + return i + + +async def env_6(i: int) -> int: + def helper() -> int: + return 1 + + async with AsyncContextManager(): + if i == 1: + return helper() + elif i == 2: + return i + elif i == 3: + return i + return i + + +async def env_7(i: int) -> int: + def helper() -> int: + return 1 + + async with AsyncContextManager(): + if i == 1: + return helper() + elif i == 2: + return i + elif i == 3: + return i + else: + return i + + +async def test_async_with_mixed_return() -> None: + # Test simple async functions (generator class) + # env_1: mixed return/no-return + assert await gen_1(True) is True + assert await gen_1(False) is False + + # gen_2: all branches return + assert await gen_2(True) is True + assert await gen_2(False) is False + + # gen_3: mixed return/pass + assert await gen_3(True) is True + assert await gen_3(False) is False + + # gen_4: no returns in async with + assert await gen_4(True) is True + assert await gen_4(False) is False + + # gen_5: multiple branches, some return + assert await gen_5(0) == 0 + assert await gen_5(1) == 1 + assert await gen_5(2) == 2 + assert await gen_5(3) == 3 + + # gen_6: all explicit branches return, implicit fallthrough + assert await gen_6(0) == 0 + assert await gen_6(1) == 1 + assert await gen_6(2) == 2 + assert await gen_6(3) == 3 + + # gen_7: all branches return including else + assert await gen_7(0) == 0 + assert await gen_7(1) == 1 + assert await gen_7(2) == 2 + assert await gen_7(3) == 3 + + # Test async functions with nested functions (environment class) + # env_1: mixed return/no-return + assert await env_1(True) is True + assert await env_1(False) is False + + # env_2: all branches return + assert await env_2(True) is True + assert await env_2(False) is False + + # env_3: mixed return/pass + assert await env_3(True) is True + assert await env_3(False) is False + + # env_4: no returns in async with + assert await env_4(True) is True + assert await env_4(False) is False + + # env_5: multiple branches, some return + assert await env_5(0) == 0 + assert await env_5(1) == 1 + assert await env_5(2) == 2 + assert await env_5(3) == 3 + + # env_6: all explicit branches return, implicit fallthrough + assert await env_6(0) == 0 + assert await env_6(1) == 1 + assert await env_6(2) == 2 + assert await env_6(3) == 3 + + # env_7: all branches return including else + assert await env_7(0) == 0 + assert await env_7(1) == 1 + assert await env_7(2) == 2 + assert await env_7(3) == 3 + +[case testAsyncTryExceptFinallyAwait] +import asyncio +from testutil import assertRaises + +class TestError(Exception): + pass + +# Test 0: Simplest case - just try/finally with raise and await +async def simple_try_finally_await() -> None: + try: + raise ValueError("simple error") + finally: + await asyncio.sleep(0) + +# Test 1: Raise inside try, catch in except, don't re-raise +async def async_try_except_no_reraise() -> int: + try: + raise ValueError("test error") + return 1 # Never reached + except ValueError: + return 2 # Should return this + finally: + await asyncio.sleep(0) + return 3 # Should not reach this + +# Test 2: Raise inside try, catch in except, re-raise +async def async_try_except_reraise() -> int: + try: + raise ValueError("test error") + return 1 # Never reached + except ValueError: + raise # Re-raise the exception + finally: + await asyncio.sleep(0) + return 2 # Should not reach this + +# Test 3: Raise inside try, catch in except, raise different error +async def async_try_except_raise_different() -> int: + try: + raise ValueError("original error") + return 1 # Never reached + except ValueError: + raise RuntimeError("different error") + finally: + await asyncio.sleep(0) + return 2 # Should not reach this + +# Test 4: Another try/except block inside finally +async def async_try_except_inside_finally() -> int: + try: + raise ValueError("outer error") + return 1 # Never reached + finally: + await asyncio.sleep(0) + try: + raise RuntimeError("inner error") + except RuntimeError: + pass # Catch inner error + return 2 # What happens after finally with inner exception handled? + +# Test 5: Another try/finally block inside finally +async def async_try_finally_inside_finally() -> int: + try: + raise ValueError("outer error") + return 1 # Never reached + finally: + await asyncio.sleep(0) + try: + raise RuntimeError("inner error") + finally: + await asyncio.sleep(0) + return 2 # Should not reach this + +# Control case: No await in finally - should work correctly +async def async_exception_no_await_in_finally() -> None: + """Control case: This works correctly - exception propagates""" + try: + raise TestError("This exception will propagate!") + finally: + pass # No await here + +# Test function with no exception to check normal flow +async def async_no_exception_with_await_in_finally() -> int: + try: + return 1 # Normal return + finally: + await asyncio.sleep(0) + return 2 # Should not reach this + +async def test_async_try_except_finally_await() -> None: + # Test 0: Simplest case - just try/finally with exception + # Expected: ValueError propagates + with assertRaises(ValueError): + await simple_try_finally_await() + + # Test 1: Exception caught, not re-raised + # Expected: return 2 (from except block) + result = await async_try_except_no_reraise() + assert result == 2, f"Expected 2, got {result}" + + # Test 2: Exception caught and re-raised + # Expected: ValueError propagates + with assertRaises(ValueError): + await async_try_except_reraise() + + # Test 3: Exception caught, different exception raised + # Expected: RuntimeError propagates + with assertRaises(RuntimeError): + await async_try_except_raise_different() + + # Test 4: Try/except inside finally + # Expected: ValueError propagates (outer exception) + with assertRaises(ValueError): + await async_try_except_inside_finally() + + # Test 5: Try/finally inside finally + # Expected: RuntimeError propagates (inner error) + with assertRaises(RuntimeError): + await async_try_finally_inside_finally() + + # Control case: No await in finally (should work correctly) + with assertRaises(TestError): + await async_exception_no_await_in_finally() + + # Test normal flow (no exception) + # Expected: return 1 + result = await async_no_exception_with_await_in_finally() + assert result == 1, f"Expected 1, got {result}" + +[file asyncio/__init__.pyi] +async def sleep(t: float) -> None: ... + +[case testAsyncContextManagerExceptionHandling] +import asyncio +from typing import Optional, Type +from testutil import assertRaises + +# Test 1: Basic async context manager that doesn't suppress exceptions +class AsyncContextManager: + async def __aenter__(self) -> 'AsyncContextManager': + return self + + async def __aexit__(self, exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: object) -> None: + # This await in __aexit__ is like await in finally + await asyncio.sleep(0) + # Don't suppress the exception (return None/False) + +async def func_with_async_context_manager() -> str: + async with AsyncContextManager(): + raise ValueError("Exception inside async with") + return "should not reach" # Never reached + return "should not reach either" # Never reached + +async def test_basic_exception() -> str: + try: + await func_with_async_context_manager() + return "func_a returned normally - bug!" + except ValueError: + return "caught ValueError - correct!" + except Exception as e: + return f"caught different exception: {type(e).__name__}" + +# Test 2: Async context manager that raises a different exception in __aexit__ +class AsyncContextManagerRaisesInExit: + async def __aenter__(self) -> 'AsyncContextManagerRaisesInExit': + return self + + async def __aexit__(self, exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: object) -> None: + # This await in __aexit__ is like await in finally + await asyncio.sleep(0) + # Raise a different exception - this should replace the original exception + raise RuntimeError("Exception in __aexit__") + +async def func_with_raising_context_manager() -> str: + async with AsyncContextManagerRaisesInExit(): + raise ValueError("Original exception") + return "should not reach" # Never reached + return "should not reach either" # Never reached + +async def test_exception_in_aexit() -> str: + try: + await func_with_raising_context_manager() + return "func returned normally - unexpected!" + except RuntimeError: + return "caught RuntimeError - correct!" + except ValueError: + return "caught ValueError - original exception not replaced!" + except Exception as e: + return f"caught different exception: {type(e).__name__}" + +async def test_async_context_manager_exception_handling() -> None: + # Test 1: Basic exception propagation + result = await test_basic_exception() + # Expected: "caught ValueError - correct!" + assert result == "caught ValueError - correct!", f"Expected exception to propagate, got: {result}" + + # Test 2: Exception raised in __aexit__ replaces original exception + result = await test_exception_in_aexit() + # Expected: "caught RuntimeError - correct!" + # (The RuntimeError from __aexit__ should replace the ValueError) + assert result == "caught RuntimeError - correct!", f"Expected RuntimeError from __aexit__, got: {result}" + +[file asyncio/__init__.pyi] +async def sleep(t: float) -> None: ... + +[case testCallableArgWithSameNameAsHelperMethod] +import asyncio +from typing import Awaitable, Callable + + +MyCallable = Callable[[int, int], Awaitable[int]] + +async def add(a: int, b: int) -> int: + return a + b + +async def await_send(send: MyCallable) -> int: + return await send(1, 2) + +async def await_throw(throw: MyCallable) -> int: + return await throw(3, 4) + +async def tests() -> None: + assert await await_send(add) == 3 + assert await await_throw(add) == 7 + +def test_callable_arg_same_name_as_helper() -> None: + asyncio.run(tests()) + +[file asyncio/__init__.pyi] +def run(x: object) -> object: ... + +[case testRunAsyncCancelFinallySpecialCase] +import asyncio + +from testutil import assertRaises + +# Greatly simplified from asyncio.Condition +class Condition: + async def acquire(self) -> None: pass + + async def wait(self) -> bool: + l = asyncio.get_running_loop() + fut = l.create_future() + a = [] + try: + try: + a.append(fut) + try: + await fut + return True + finally: + a.pop() + finally: + err = None + while True: + try: + await self.acquire() + break + except asyncio.CancelledError as e: + err = e + + if err is not None: + try: + raise err + finally: + err = None + except BaseException: + raise + +async def do_cancel() -> None: + cond = Condition() + wait = asyncio.create_task(cond.wait()) + asyncio.get_running_loop().call_soon(wait.cancel) + with assertRaises(asyncio.CancelledError): + await wait + +def test_cancel_special_case() -> None: + asyncio.run(do_cancel()) + +[file asyncio/__init__.pyi] +from typing import Any + +class CancelledError(Exception): ... + +def run(x: object) -> object: ... +def get_running_loop() -> Any: ... +def create_task(x: object) -> Any: ... + +[case testAsyncInheritance1] +from typing import final, Coroutine, Any, TypeVar + +import asyncio + +from mypy_extensions import trait + +class Base1: + async def foo(self) -> int: + return 1 + +class Derived1(Base1): + async def foo(self) -> int: + return await super().foo() + 1 + +async def base1_foo(b: Base1) -> int: + return await b.foo() + +async def derived1_foo(b: Derived1) -> int: + return await b.foo() + +def test_async_inheritance() -> None: + assert asyncio.run(base1_foo(Base1())) == 1 + assert asyncio.run(base1_foo(Derived1())) == 2 + assert asyncio.run(derived1_foo(Derived1())) == 2 + +@final +class FinalClass: + async def foo(self) -> int: + return 3 + +async def final_class_foo(b: FinalClass) -> int: + return await b.foo() + +def test_final_class() -> None: + assert asyncio.run(final_class_foo(FinalClass())) == 3 + +class Base2: + async def foo(self) -> int: + return 4 + + async def bar(self) -> int: + return 5 + +class Derived2(Base2): + # Does not override "foo" + async def bar(self) -> int: + return 6 + +async def base2_foo(b: Base2) -> int: + return await b.foo() + +def test_no_override() -> None: + assert asyncio.run(base2_foo(Base2())) == 4 + assert asyncio.run(base2_foo(Derived2())) == 4 + +class Base3: + async def foo(self) -> int: + return 7 + +class Derived3(Base3): + def foo(self) -> Coroutine[Any, Any, int]: + async def inner() -> int: + return 8 + return inner() + +async def base3_foo(b: Base3) -> int: + return await b.foo() + +def test_override_non_async() -> None: + assert asyncio.run(base3_foo(Base3())) == 7 + assert asyncio.run(base3_foo(Derived3())) == 8 + +class Base4: pass + +@trait +class TraitBase: + async def foo(self, value: int) -> int: + raise NotImplementedError() + +class DerivedFromTrait(Base4, TraitBase): + async def foo(self, value: int) -> int: + return value + 3 + +async def trait_foo(o: TraitBase, x: int) -> int: + return await o.foo(x) + +def test_override_trait() -> None: + assert asyncio.run(trait_foo(DerivedFromTrait(), 7)) == 10 + +class Base5: + def __init__(self) -> None: + self._name = "test" + + async def foo(self, x: int) -> int: + assert self._name == "test" + return x + 11 + +class Derived5(Base5): + async def foo(self, x: int) -> int: + return await super().foo(x) + 22 + +def test_call_using_super() -> None: + assert asyncio.run(Derived5().foo(5)) == 38 + +[file asyncio/__init__.pyi] +def run(x: object) -> object: ... diff --git a/mypyc/test-data/run-base64.test b/mypyc/test-data/run-base64.test new file mode 100644 index 0000000000000..8d7eb7c13482d --- /dev/null +++ b/mypyc/test-data/run-base64.test @@ -0,0 +1,167 @@ +[case testAllBase64Features_librt_experimental] +from typing import Any +import base64 +import binascii + +from librt.base64 import b64encode, b64decode + +from testutil import assertRaises + +def test_encode_basic() -> None: + assert b64encode(b"x") == b"eA==" + + with assertRaises(TypeError): + b64encode(bytearray(b"x")) + +def check_encode(b: bytes) -> None: + assert b64encode(b) == getattr(base64, "b64encode")(b) + +def test_encode_different_strings() -> None: + for i in range(256): + check_encode(bytes([i])) + check_encode(bytes([i]) + b"x") + check_encode(bytes([i]) + b"xy") + check_encode(bytes([i]) + b"xyz") + check_encode(bytes([i]) + b"xyza") + check_encode(b"x" + bytes([i])) + check_encode(b"xy" + bytes([i])) + check_encode(b"xyz" + bytes([i])) + check_encode(b"xyza" + bytes([i])) + + b = b"a\x00\xb7" * 1000 + for i in range(1000): + check_encode(b[:i]) + + for b in b"", b"ab", b"bac", b"1234", b"xyz88", b"abc" * 200: + check_encode(b) + +def test_encode_wrapper() -> None: + enc: Any = b64encode + assert enc(b"x") == b"eA==" + + with assertRaises(TypeError): + enc() + + with assertRaises(TypeError): + enc(b"x", b"y") + +def test_decode_basic() -> None: + assert b64decode(b"eA==") == b"x" + + with assertRaises(TypeError): + b64decode(bytearray(b"eA==")) + + for non_ascii in "\x80", "foo\u100bar", "foo\ua1234bar": + with assertRaises(ValueError): + b64decode(non_ascii) + +def check_decode(b: bytes, encoded: bool = False) -> None: + if encoded: + enc = b + else: + enc = b64encode(b) + assert b64decode(enc) == getattr(base64, "b64decode")(enc) + if getattr(enc, "isascii")(): # Test stub has no "isascii" + enc_str = enc.decode("ascii") + assert b64decode(enc_str) == getattr(base64, "b64decode")(enc_str) + +def test_decode_different_strings() -> None: + for i in range(256): + check_decode(bytes([i])) + check_decode(bytes([i]) + b"x") + check_decode(bytes([i]) + b"xy") + check_decode(bytes([i]) + b"xyz") + check_decode(bytes([i]) + b"xyza") + check_decode(b"x" + bytes([i])) + check_decode(b"xy" + bytes([i])) + check_decode(b"xyz" + bytes([i])) + check_decode(b"xyza" + bytes([i])) + + b = b"a\x00\xb7" * 1000 + for i in range(1000): + check_decode(b[:i]) + + for b in b"", b"ab", b"bac", b"1234", b"xyz88", b"abc" * 200: + check_decode(b) + +def is_base64_char(x: int) -> bool: + c = chr(x) + return ('a' <= c <= 'z') or ('A' <= c <= 'Z') or ('0' <= c <= '9') or c in '+/=' + +def test_decode_with_non_base64_chars() -> None: + # For stdlib compatibility, non-base64 characters should be ignored. + + # Invalid characters as a suffix use a fast path. + check_decode(b"eA== ", encoded=True) + check_decode(b"eA==\n", encoded=True) + check_decode(b"eA== \t\n", encoded=True) + check_decode(b"\n", encoded=True) + + check_decode(b" e A = = ", encoded=True) + + # Special case: Two different encodings of the same data + check_decode(b"eAa=", encoded=True) + check_decode(b"eAY=", encoded=True) + + for x in range(256): + if not is_base64_char(x): + b = bytes([x]) + check_decode(b, encoded=True) + check_decode(b"eA==" + b, encoded=True) + check_decode(b"e" + b + b"A==", encoded=True) + check_decode(b"eA=" + b + b"=", encoded=True) + +def check_decode_error(b: bytes, ignore_stdlib: bool = False) -> None: + if not ignore_stdlib: + with assertRaises(binascii.Error): + getattr(base64, "b64decode")(b) + + # The raised error is different, since librt shouldn't depend on binascii + with assertRaises(ValueError): + b64decode(b) + +def test_decode_with_invalid_padding() -> None: + check_decode_error(b"eA") + check_decode_error(b"eA=") + check_decode_error(b"eHk") + check_decode_error(b"eA = ") + + # Here stdlib behavior seems nonsensical, so we don't try to duplicate it + check_decode_error(b"eA=a=", ignore_stdlib=True) + +def test_decode_with_extra_data_after_padding() -> None: + check_decode(b"=", encoded=True) + check_decode(b"==", encoded=True) + check_decode(b"===", encoded=True) + check_decode(b"====", encoded=True) + check_decode(b"eA===", encoded=True) + check_decode(b"eHk==", encoded=True) + check_decode(b"eA==x", encoded=True) + check_decode(b"eHk=x", encoded=True) + check_decode(b"eA==abc=======efg", encoded=True) + +def test_decode_wrapper() -> None: + dec: Any = b64decode + assert dec(b"eA==") == b"x" + + with assertRaises(TypeError): + dec() + + with assertRaises(TypeError): + dec(b"x", b"y") + +[case testBase64FeaturesNotAvailableInNonExperimentalBuild_librt_base64] +# This also ensures librt.base64 can be built without experimental features +import librt.base64 + +def test_b64encode_not_available() -> None: + assert not hasattr(librt.base64, "b64encode") + +[case testBase64UsedAtTopLevelOnly_librt_experimental] +from librt.base64 import b64encode + +# The only reference to b64encode is at module top level +encoded = b64encode(b"x") + +def test_top_level_only_encode() -> None: + assert encoded == b"eA==" diff --git a/mypyc/test-data/run-bools.test b/mypyc/test-data/run-bools.test index d7a2aa37ade76..45bf861e71e32 100644 --- a/mypyc/test-data/run-bools.test +++ b/mypyc/test-data/run-bools.test @@ -15,6 +15,8 @@ True False [case testBoolOps] +from __future__ import annotations + from typing import Optional, Any MYPY = False if MYPY: @@ -31,7 +33,7 @@ def test_if() -> None: assert f(False) is True def test_bitwise_and() -> None: - # Use eval() to avoid constand folding + # Use eval() to avoid constant folding t: bool = eval('True') f: bool = eval('False') assert t & t == True @@ -44,7 +46,7 @@ def test_bitwise_and() -> None: assert t == False def test_bitwise_or() -> None: - # Use eval() to avoid constand folding + # Use eval() to avoid constant folding t: bool = eval('True') f: bool = eval('False') assert t | t == True @@ -57,7 +59,7 @@ def test_bitwise_or() -> None: assert f == True def test_bitwise_xor() -> None: - # Use eval() to avoid constand folding + # Use eval() to avoid constant folding t: bool = eval('True') f: bool = eval('False') assert t ^ t == False @@ -117,6 +119,29 @@ def test_optional_to_bool() -> None: assert not optional_to_bool3(F(False)) assert optional_to_bool3(F(True)) +def not_c(c: C) -> bool: + return not c + +def not_c_opt(c: C | None) -> bool: + return not c + +def not_d(d: D) -> bool: + return not d + +def not_d_opt(d: D | None) -> bool: + return not d + +def test_not_instance() -> None: + assert not not_c(C()) + assert not_c_opt(None) + assert not not_c_opt(C()) + + assert not_d(D(False)) + assert not not_d(D(True)) + assert not_d_opt(D(False)) + assert not_d_opt(None) + assert not not_d_opt(D(True)) + def test_any_to_bool() -> None: a: Any = int() b: Any = a + 1 @@ -222,8 +247,122 @@ def test_mixed_comparisons_i64() -> None: assert lt_mixed_i64(x, n) == (int(x) < n) assert gt_mixed_i64(n, x) == (n > int(x)) +def not_object(x: object) -> bool: + return not x + +def not_str(x: str) -> bool: + return not x + +def not_int(x: int) -> bool: + return not x + +def not_list(x: list[int]) -> bool: + return not x + +def not_tuple(x: tuple[int, ...]) -> bool: + return not x + +def not_dict(x: dict[str, int]) -> bool: + return not x + +def test_not_object() -> None: + assert not_object(None) + assert not_object([]) + assert not_object(0) + assert not not_object(1) + assert not not_object([1]) + +def test_not_str() -> None: + assert not_str(str()) + assert not not_str('x' + str()) + +def test_not_int() -> None: + assert not_int(int('0')) + assert not not_int(int('1')) + assert not not_int(int('-1')) + +def test_not_list() -> None: + assert not_list([]) + assert not not_list([1]) + +def test_not_tuple() -> None: + assert not_tuple(()) + assert not not_tuple((1,)) + +def test_not_dict() -> None: + assert not_dict({}) + assert not not_dict({'x': 1}) + +def not_str_opt(x: str | None) -> bool: + return not x + +def not_int_opt(x: int | None) -> bool: + return not x + +def not_list_opt(x: list[int] | None) -> bool: + return not x + +def not_tuple_opt(x: tuple[int, ...] | None) -> bool: + return not x + +def not_dict_opt(x: dict[str, int] | None) -> bool: + return not x + +def test_not_str_opt() -> None: + assert not_str_opt(str()) + assert not_str_opt(None) + assert not not_str_opt('x' + str()) + +def test_not_int_opt() -> None: + assert not_int_opt(int('0')) + assert not_int_opt(None) + assert not not_int_opt(int('1')) + assert not not_int_opt(int('-1')) + +def test_not_list_opt() -> None: + assert not_list_opt([]) + assert not_list_opt(None) + assert not not_list_opt([1]) + +def test_not_tuple_opt() -> None: + assert not_tuple_opt(()) + assert not_tuple_opt(None) + assert not not_tuple_opt((1,)) + +def test_not_dict_opt() -> None: + assert not_dict_opt({}) + assert not_dict_opt(None) + assert not not_dict_opt({'x': 1}) + [case testBoolMixInt] -y = False -print((y or 0) and True) +def test_mix() -> None: + y = False + print((y or 0) and True) [out] 0 + +[case testIsInstance] +from typing import Any +def test_built_in() -> None: + true: Any = True + false: Any = False + assert isinstance(true, bool) + assert isinstance(false, bool) + + assert not isinstance(set(), bool) + assert not isinstance((), bool) + assert not isinstance((True, False), bool) + assert not isinstance({False, True}, bool) + assert not isinstance(int() + 1, bool) + assert not isinstance(str() + 'False', bool) + +def test_user_defined() -> None: + from userdefinedbool import bool + + b: Any = True + assert isinstance(bool(), bool) + assert not isinstance(b, bool) + +[file userdefinedbool.py] +class bool: + pass diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index aaf541194ac65..df5cb209b9025 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' @@ -254,7 +277,6 @@ class bytes_subclass(bytes): return b'spook' [case testBytesFormatting] -[typing fixtures/typing-full.pyi] from testutil import assertRaises # https://www.python.org/dev/peps/pep-0461/ @@ -291,6 +313,7 @@ def test_bytes_formatting_2() -> None: aa = b'\xe4\xbd\xa0\xe5\xa5\xbd%b' % b'\xe4\xbd\xa0\xe5\xa5\xbd' assert aa == b'\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd' assert aa.decode() == '你好你好' +[typing fixtures/typing-full.pyi] class A: @@ -300,3 +323,81 @@ class A: def test_bytes_dunder() -> None: assert b'%b' % A() == b'aaa' assert b'%s' % A() == b'aaa' + +[case testIsInstance] +from copysubclass import subbytes, subbytearray +from typing import Any +def test_bytes() -> None: + b: Any = b'' + assert isinstance(b, bytes) + assert isinstance(b + b'123', bytes) + assert isinstance(b + b'\xff', bytes) + assert isinstance(subbytes(), bytes) + assert isinstance(subbytes(b + b'123'), bytes) + assert isinstance(subbytes(b + b'\xff'), bytes) + + assert not isinstance(set(), bytes) + assert not isinstance((), bytes) + assert not isinstance((b'1',b'2',b'3'), bytes) + assert not isinstance({b'a',b'b'}, bytes) + assert not isinstance(int() + 1, bytes) + assert not isinstance(str() + 'a', bytes) + +def test_user_defined_bytes() -> None: + from userdefinedbytes import bytes + + assert isinstance(bytes(), bytes) + assert not isinstance(b'\x7f', bytes) + +def test_bytearray() -> None: + assert isinstance(bytearray(), bytearray) + assert isinstance(bytearray(b'123'), bytearray) + assert isinstance(bytearray(b'\xff'), bytearray) + assert isinstance(subbytearray(), bytearray) + assert isinstance(subbytearray(bytearray(b'123')), bytearray) + assert isinstance(subbytearray(bytearray(b'\xff')), bytearray) + + assert not isinstance(set(), bytearray) + assert not isinstance((), bytearray) + assert not isinstance((bytearray(b'1'),bytearray(b'2'),bytearray(b'3')), bytearray) + assert not isinstance([bytearray(b'a'),bytearray(b'b')], bytearray) + assert not isinstance(int() + 1, bytearray) + assert not isinstance(str() + 'a', bytearray) + +[file copysubclass.py] +class subbytes(bytes): + pass + +class subbytearray(bytearray): + pass + +[file userdefinedbytes.py] +class bytes: + pass + +[case testBytesOptionalEquality] +from __future__ import annotations + +def eq_b_opt_b(x: bytes | None, y: bytes) -> bool: + return x == y + +def ne_b_b_opt(x: bytes, y: bytes | None) -> bool: + return x != y + +def test_optional_eq() -> None: + b = b'x' + assert eq_b_opt_b(b, b) + assert eq_b_opt_b(b + bytes([int()]), b + bytes([int()])) + + assert not eq_b_opt_b(b'x', b'y') + assert not eq_b_opt_b(b'y', b'x') + assert not eq_b_opt_b(None, b'x') + +def test_optional_ne() -> None: + b = b'x' + assert not ne_b_b_opt(b, b) + assert not ne_b_b_opt(b + b'y', b + bytes() + b'y') + + assert ne_b_b_opt(b'x', b'y') + assert ne_b_b_opt(b'y', b'x') + assert ne_b_b_opt(b'x', None) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 59617714f7e73..02a9934bac713 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: @@ -287,6 +322,17 @@ if sys.version_info[:2] > (3, 5): assert TestEnum.b.name == 'b' assert TestEnum.b.value == 2 +[case testRunSuperYieldFromDict] +from typing import Any, Iterator + +class DictSubclass(dict): + def items(self) -> Iterator[Any]: + yield 1 + yield from super().items() + +def test_sub_dict() -> None: + assert list(DictSubclass().items()) == [1] + [case testGetAttribute] class C: x: int @@ -438,6 +484,25 @@ a = A(10) assert a.foo() == 11 assert foo() == 21 +[case testClassKwargs] +class X: + def __init__(self, msg: str, **variables: int) -> None: + self.msg = msg + self.variables = variables + +[file driver.py] +import traceback +from native import X +x = X('hello', a=0, b=1) +assert x.msg == 'hello' +assert x.variables == {'a': 0, 'b': 1} +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') @@ -662,8 +727,7 @@ Traceback (most recent call last): AttributeError: attribute 'x' of 'X' undefined [case testClassMethods] -from typing import ClassVar, Any -from typing_extensions import final +from typing import ClassVar, Any, final from mypy_extensions import mypyc_attr from interp import make_interpreted_subclass @@ -881,6 +945,53 @@ def welp() -> int: from native import welp assert welp() == 35 +[case testSubclassUnsupportedException] +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class MyError(ZeroDivisionError): + pass + +@mypyc_attr(native_class=False) +class MyError2(ZeroDivisionError): + def __init__(self, s: str) -> None: + super().__init__(s + "!") + self.x = s.upper() + +def f() -> None: + raise MyError("foobar") + +def test_non_native_exception_subclass_basics() -> None: + e = MyError() + assert isinstance(e, MyError) + assert isinstance(e, ZeroDivisionError) + assert isinstance(e, Exception) + + e = MyError("x") + assert repr(e) == "MyError('x')" + + e2 = MyError2("ab") + assert repr(e2) == "MyError2('ab!')", repr(e2) + assert e2.x == "AB" + +def test_raise_non_native_exception_subclass_1() -> None: + try: + f() + except MyError: + x = True + else: + assert False + assert x + +def test_raise_non_native_exception_subclass_2() -> None: + try: + f() + except ZeroDivisionError: + x = True + else: + assert False + assert x + [case testSubclassPy] from b import B, V class A(B): @@ -1020,7 +1131,7 @@ assert b.z is None assert not hasattr(b, 'bogus') [case testProtocol] -from typing_extensions import Protocol +from typing import Protocol class Proto(Protocol): def foo(self, x: int) -> None: @@ -1178,9 +1289,10 @@ class Bar(Foo): def f(self, *args: int, **kwargs: int) -> None: print("stuff", args, kwargs) -z: Foo = Bar() -z.f(1, z=50) -z.f() +def test_override() -> None: + z: Foo = Bar() + z.f(1, z=50) + z.f() [out] stuff (1,) {'z': 50} @@ -1200,18 +1312,19 @@ class Foo: def baz_f(self: Any, *args: int, **kwargs: int) -> None: print("Baz", args, kwargs) -# Make an "interpreted" subtype of Foo -type2: Any = type -Bar = type2('Bar', (Foo,), {}) -Baz = type2('Baz', (Foo,), {'f': baz_f}) +def test_override() -> None: + # Make an "interpreted" subtype of Foo + type2: Any = type + Bar = type2('Bar', (Foo,), {}) + Baz = type2('Baz', (Foo,), {'f': baz_f}) -y: Foo = Bar() -y.f(1, z=2) -y.f() + y: Foo = Bar() + y.f(1, z=2) + y.f() -z: Foo = Baz() -z.f(1, z=2) -z.f() + z: Foo = Baz() + z.f(1, z=2) + z.f() [out] Foo 1 2 @@ -1230,9 +1343,10 @@ class Bar(Foo): def f(self, x: Optional[int]=None) -> None: print(x) -z: Foo = Bar() -z.f(1) -z.f() +def test_override() -> None: + z: Foo = Bar() + z.f(1) + z.f() [out] 1 @@ -1249,10 +1363,11 @@ class Bar(Foo): def f(self, *args: int, **kwargs: int) -> None: print("Bar", args, kwargs) -z: Foo = Bar() -z.f(1, z=2) -z.f(1, 2, 3) -# z.f(x=5) # Not tested because we (knowingly) do the wrong thing and pass it as positional +def test_override() -> None: + z: Foo = Bar() + z.f(1, z=2) + z.f(1, 2, 3) + # z.f(x=5) # Not tested because we (knowingly) do the wrong thing and pass it as positional [out] Bar (1,) {'z': 2} @@ -1270,10 +1385,11 @@ class Bar(Foo): def f(self, x: int = 10, *args: int, **kwargs: int) -> None: print("Bar", x, args, kwargs) -z: Foo = Bar() -z.f(1, z=2) -z.f(1, 2, 3) -z.f() +def test_override() -> None: + z: Foo = Bar() + z.f(1, z=2) + z.f(1, 2, 3) + z.f() [out] Bar 1 () {'z': 2} @@ -1297,18 +1413,19 @@ class Foo: def baz_f(self, a: int=30, y: int=50) -> None: print("Baz", a, y) -# Make an "interpreted" subtype of Foo -type2: Any = type -Baz = type2('Baz', (Foo,), {'f': baz_f}) +def test_override() -> None: + # Make an "interpreted" subtype of Foo + type2: Any = type + Baz = type2('Baz', (Foo,), {'f': baz_f}) -z: Foo = Baz() -z.f() -z.f(y=1) -z.f(1, 2) -# Not tested because we don't (and probably won't) match cpython here -# from testutil import assertRaises -# with assertRaises(TypeError): -# z.f(x=7) + z: Foo = Baz() + z.f() + z.f(y=1) + z.f(1, 2) + # Not tested because we don't (and probably won't) match cpython here + # from testutil import assertRaises + # with assertRaises(TypeError): + # z.f(x=7) [out] Baz 30 50 @@ -2491,10 +2608,11 @@ class Base: class Derived(Base): pass -assert Derived()() == 1 +def test_inherited() -> None: + assert Derived()() == 1 [case testClassWithFinalAttribute] -from typing_extensions import Final +from typing import Final class C: A: Final = -1 @@ -2503,3 +2621,2965 @@ 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 testBufferRoundTrip_librt_internal] +from __future__ import annotations + +from typing import Final, Any +from mypy_extensions import u8 +from librt.internal import ( + ReadBuffer, WriteBuffer, write_bool, read_bool, write_str, read_str, write_float, read_float, + write_int, read_int, write_tag, read_tag, write_bytes, read_bytes, + cache_version, +) + +from testutil import assertRaises + +Tag = u8 +TAG_A: Final[Tag] = 33 +TAG_B: Final[Tag] = 255 +TAG_SPECIAL: Final[Tag] = 239 + +def test_buffer_basic() -> None: + assert cache_version() == 0 + w = WriteBuffer() + write_str(w, "foo") + r = ReadBuffer(w.getvalue()) + assert read_str(r) == "foo" + +def test_buffer_grow() -> None: + w = WriteBuffer() + n = 100 * 1000 + for i in range(n): + write_int(w, i & 63) + r = ReadBuffer(w.getvalue()) + for i in range(n): + assert read_int(r) == (i & 63) + with assertRaises(ValueError): + read_int(r) + +def test_buffer_primitive_types() -> None: + a1: Any = WriteBuffer() + w: WriteBuffer = a1 + write_str(w, "foo") + data = w.getvalue() + assert read_str(ReadBuffer(data)) == "foo" + a2: Any = ReadBuffer(b"foo") + with assertRaises(TypeError): + w2: WriteBuffer = a2 + + a3: Any = ReadBuffer(data) + r: ReadBuffer = a3 + assert read_str(r) == "foo" + a4: Any = WriteBuffer() + with assertRaises(TypeError): + r2: ReadBuffer = a4 + +def test_type_check_args_in_write_functions() -> None: + # Test calling wrapper functions with invalid arg types + from librt import internal + alias: Any = internal + w = WriteBuffer() + with assertRaises(TypeError): + alias.write_str(None, "foo") + with assertRaises(TypeError): + alias.write_str(w, None) + with assertRaises(TypeError): + alias.write_bool(None, True) + with assertRaises(TypeError): + alias.write_bool(w, None) + with assertRaises(TypeError): + alias.write_bytes(None, b"foo") + with assertRaises(TypeError): + alias.write_bytes(w, None) + with assertRaises(TypeError): + alias.write_float(None, 1.5) + with assertRaises(TypeError): + alias.write_float(w, None) + with assertRaises(TypeError): + alias.write_int(None, 15) + with assertRaises(TypeError): + alias.write_int(w, None) + with assertRaises(TypeError): + alias.write_tag(None, 15) + with assertRaises(TypeError): + alias.write_tag(w, None) + +def test_type_check_buffer_in_read_functions() -> None: + # Test calling wrapper functions with invalid arg types + from librt import internal + alias: Any = internal + with assertRaises(TypeError): + alias.read_str(None) + with assertRaises(TypeError): + alias.read_bool(None) + with assertRaises(TypeError): + alias.read_bytes(None) + with assertRaises(TypeError): + alias.read_float(None) + with assertRaises(TypeError): + alias.read_int(None) + with assertRaises(TypeError): + alias.read_tag(None) + +def test_buffer_roundtrip() -> None: + b: WriteBuffer | ReadBuffer + b = WriteBuffer() + write_str(b, "foo") + write_bool(b, True) + write_str(b, "bar" * 1000) + write_bool(b, False) + write_bytes(b, b"bar") + write_bytes(b, b"bar" * 100) + write_bytes(b, b"") + write_bytes(b, b"a" * 117) + write_bytes(b, b"a" * 118) + write_float(b, 0.1) + write_float(b, -1.0) + write_float(b, -113.0) + write_int(b, 0) + write_int(b, 1) + write_tag(b, TAG_A) + write_tag(b, TAG_SPECIAL) + write_tag(b, TAG_B) + write_int(b, 2) + write_int(b, 2 ** 85) + write_int(b, 255) + write_int(b, -1) + write_int(b, -255) + write_int(b, 536860911) + write_int(b, 536860912) + write_int(b, 1234567891) + + b = ReadBuffer(b.getvalue()) + assert read_str(b) == "foo" + assert read_bool(b) is True + assert read_str(b) == "bar" * 1000 + assert read_bool(b) is False + assert read_bytes(b) == b"bar" + assert read_bytes(b) == b"bar" * 100 + assert read_bytes(b) == b"" + assert read_bytes(b) == b"a" * 117 + assert read_bytes(b) == b"a" * 118 + assert read_float(b) == 0.1 + assert read_float(b) == -1.0 + assert read_float(b) == -113.0 + assert read_int(b) == 0 + assert read_int(b) == 1 + assert read_tag(b) == TAG_A + assert read_tag(b) == TAG_SPECIAL + assert read_tag(b) == TAG_B + assert read_int(b) == 2 + assert read_int(b) == 2 ** 85 + assert read_int(b) == 255 + assert read_int(b) == -1 + assert read_int(b) == -255 + assert read_int(b) == 536860911 + assert read_int(b) == 536860912 + assert read_int(b) == 1234567891 + +def test_buffer_int_size() -> None: + b: WriteBuffer | ReadBuffer + for i in (-10, -9, 0, 116, 117): + b = WriteBuffer() + write_int(b, i) + assert len(b.getvalue()) == 1 + b = ReadBuffer(b.getvalue()) + assert read_int(b) == i + for i in (-100, -11, 118, 12344, 16283): + b = WriteBuffer() + write_int(b, i) + assert len(b.getvalue()) == 2 + b = ReadBuffer(b.getvalue()) + assert read_int(b) == i + for i in (-10000, 16284, 123456789): + b = WriteBuffer() + write_int(b, i) + assert len(b.getvalue()) == 4 + b = ReadBuffer(b.getvalue()) + assert read_int(b) == i + +def test_buffer_int_powers() -> None: + # 0, 1, 2 are tested above + for p in range(2, 200): + b = WriteBuffer() + write_int(b, 1 << p) + write_int(b, (1 << p) - 1) + write_int(b, -1 << p) + write_int(b, (-1 << p) + 1) + rb = ReadBuffer(b.getvalue()) + assert read_int(rb) == 1 << p + assert read_int(rb) == (1 << p) - 1 + assert read_int(rb) == -1 << p + assert read_int(rb) == (-1 << p) + 1 + +def test_positive_long_int_serialized_bytes() -> None: + b = WriteBuffer() + n = 0x123456789ab + write_int(b, n) + x = b.getvalue() + # Two prefix bytes, followed by little endian encoded integer in variable-length format + assert x == b"\x0f\x2c\xab\x89\x67\x45\x23\x01" + rb = ReadBuffer(x) + assert read_int(rb) == n + +def test_negative_long_int_serialized_bytes() -> None: + b = WriteBuffer() + n = -0x123456789abcde + write_int(b, n) + x = b.getvalue() + assert x == b"\x0f\x32\xde\xbc\x9a\x78\x56\x34\x12" + rb = ReadBuffer(x) + assert read_int(rb) == n + +def test_buffer_str_size() -> None: + b: WriteBuffer | ReadBuffer + for s in ("", "a", "a" * 117): + b = WriteBuffer() + write_str(b, s) + assert len(b.getvalue()) == len(s) + 1 + b = ReadBuffer(b.getvalue()) + assert read_str(b) == s + + for s in ("a" * 118, "a" * 16283): + b = WriteBuffer() + write_str(b, s) + assert len(b.getvalue()) == len(s) + 2 + b = ReadBuffer(b.getvalue()) + assert read_str(b) == s + +[file driver.py] +from native import * + +test_buffer_basic() +test_buffer_grow() +test_buffer_primitive_types() +test_type_check_args_in_write_functions() +test_type_check_buffer_in_read_functions() +test_buffer_roundtrip() +test_buffer_int_size() +test_buffer_str_size() +test_buffer_int_powers() +test_positive_long_int_serialized_bytes() +test_negative_long_int_serialized_bytes() + +def test_buffer_basic_interpreted() -> None: + b = WriteBuffer() + write_str(b, "foo") + b = ReadBuffer(b.getvalue()) + assert read_str(b) == "foo" + +def test_buffer_roundtrip_interpreted() -> None: + b = WriteBuffer() + write_str(b, "foo") + write_bool(b, True) + write_str(b, "bar" * 1000) + write_bool(b, False) + write_bytes(b, b"bar") + write_bytes(b, b"bar" * 100) + write_bytes(b, b"") + write_bytes(b, b"a" * 117) + write_bytes(b, b"a" * 118) + write_float(b, 0.1) + write_int(b, 0) + write_int(b, 1) + write_tag(b, 33) + write_tag(b, 239) + write_tag(b, 255) + write_int(b, 2) + write_int(b, 2 ** 85) + write_int(b, 255) + write_int(b, -1) + write_int(b, -255) + write_int(b, 536860911) + write_int(b, 536860912) + write_int(b, 1234567891) + + b = ReadBuffer(b.getvalue()) + assert read_str(b) == "foo" + assert read_bool(b) is True + assert read_str(b) == "bar" * 1000 + assert read_bool(b) is False + assert read_bytes(b) == b"bar" + assert read_bytes(b) == b"bar" * 100 + assert read_bytes(b) == b"" + assert read_bytes(b) == b"a" * 117 + assert read_bytes(b) == b"a" * 118 + assert read_float(b) == 0.1 + assert read_int(b) == 0 + assert read_int(b) == 1 + assert read_tag(b) == 33 + assert read_tag(b) == 239 + assert read_tag(b) == 255 + assert read_int(b) == 2 + assert read_int(b) == 2 ** 85 + assert read_int(b) == 255 + assert read_int(b) == -1 + assert read_int(b) == -255 + assert read_int(b) == 536860911 + assert read_int(b) == 536860912 + assert read_int(b) == 1234567891 + +def test_buffer_int_size_interpreted() -> None: + for i in (-10, -9, 0, 116, 117): + b = WriteBuffer() + write_int(b, i) + assert len(b.getvalue()) == 1 + b = ReadBuffer(b.getvalue()) + assert read_int(b) == i + for i in (-100, -11, 118, 12344, 16283): + b = WriteBuffer() + write_int(b, i) + assert len(b.getvalue()) == 2 + b = ReadBuffer(b.getvalue()) + assert read_int(b) == i + for i in (-10000, 16284, 123456789): + b = WriteBuffer() + write_int(b, i) + assert len(b.getvalue()) == 4 + b = ReadBuffer(b.getvalue()) + assert read_int(b) == i + +def test_buffer_int_powers_interpreted() -> None: + # 0, 1, 2 are tested above + for p in range(2, 9): + b = WriteBuffer() + write_int(b, 1 << p) + write_int(b, -1 << p) + b = ReadBuffer(b.getvalue()) + assert read_int(b) == 1 << p + assert read_int(b) == -1 << p + +def test_buffer_str_size_interpreted() -> None: + for s in ("", "a", "a" * 117): + b = WriteBuffer() + write_str(b, s) + assert len(b.getvalue()) == len(s) + 1 + b = ReadBuffer(b.getvalue()) + assert read_str(b) == s + + for s in ("a" * 118, "a" * 16283): + b = WriteBuffer() + write_str(b, s) + assert len(b.getvalue()) == len(s) + 2 + b = ReadBuffer(b.getvalue()) + assert read_str(b) == s + +test_buffer_basic_interpreted() +test_buffer_roundtrip_interpreted() +test_buffer_int_size_interpreted() +test_buffer_str_size_interpreted() +test_buffer_int_powers_interpreted() + +[case testBufferEmpty_librt_internal] +from librt.internal import WriteBuffer, ReadBuffer, write_int, read_int + +def test_empty() -> None: + b = WriteBuffer() + write_int(b, 42) + b1 = ReadBuffer(b.getvalue()) + assert read_int(b1) == 42 + +[case testEnumMethodCalls] +from enum import Enum +from typing import overload, Optional, Union + +class C: + def foo(self, x: Test) -> bool: + assert Test.ONE.is_one() + assert x.next(2) == Test.THREE + assert x.prev(2) == Test.ONE + assert x.enigma(22) + assert x.enigma("22") == 22 + return x.is_one(inverse=True) + +class Test(Enum): + ONE = 1 + TWO = 2 + THREE = 3 + + def is_one(self, *, inverse: bool = False) -> bool: + if inverse: + return self != Test.ONE + return self == Test.ONE + + @classmethod + def next(cls, val: int) -> Test: + return cls(val + 1) + + @staticmethod + def prev(val: int) -> Test: + return Test(val - 1) + + @overload + def enigma(self, val: int) -> bool: ... + @overload + def enigma(self, val: Optional[str] = None) -> int: ... + def enigma(self, val: Union[int, str, None] = None) -> Union[int, bool]: + if isinstance(val, int): + return self.is_one() + return 22 +[file driver.py] +from native import Test, C + +assert Test.ONE.is_one() +assert Test.TWO.is_one(inverse=True) +assert not C().foo(Test.ONE) +assert Test.next(2) == Test.THREE +assert Test.prev(2) == Test.ONE +assert Test.ONE.enigma(22) +assert Test.ONE.enigma("22") == 22 + +[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) + +[case testDataclassInitVar] +import dataclasses + +@dataclasses.dataclass +class C: + init_v: dataclasses.InitVar[int] + v: float = dataclasses.field(init=False) + + def __post_init__(self, init_v): + self.v = init_v + 0.1 + +[file driver.py] +import native +print(native.C(22).v) + +[out] +22.1 + +[case testLastParentEnum] +from enum import Enum + +class ColorCode(str, Enum): + OKGREEN = "okgreen" + +[file driver.py] +import native +print(native.ColorCode.OKGREEN.value) + +[out] +okgreen + +[case testAttrWithSlots] +import attr + +@attr.s(slots=True) +class A: + ints: list[int] = attr.ib() + +[file driver.py] +import native +print(native.A(ints=[1, -17]).ints) + +[out] +\[1, -17] + +[case testDataclassClassReference] +from __future__ import annotations +from dataclasses import dataclass + +class BackwardDefinedClass: + pass + +@dataclass +class Data: + bitem: BackwardDefinedClass + bitems: 'BackwardDefinedClass' + fitem: ForwardDefinedClass + fitems: 'ForwardDefinedClass' + +class ForwardDefinedClass: + pass + +def test_function(): + d = Data( + bitem=BackwardDefinedClass(), + bitems=BackwardDefinedClass(), + fitem=ForwardDefinedClass(), + fitems=ForwardDefinedClass(), + ) + assert(isinstance(d.bitem, BackwardDefinedClass)) + assert(isinstance(d.bitems, BackwardDefinedClass)) + assert(isinstance(d.fitem, ForwardDefinedClass)) + assert(isinstance(d.fitems, ForwardDefinedClass)) + +[case testDelForDictSubclass-xfail] +# The crash in issue mypy#19175 is fixed. +# But, for classes that derive from built-in Python classes, user-defined __del__ method is not +# being invoked. +class DictSubclass(dict): + def __del__(self): + print("deleting DictSubclass...") + +[file driver.py] +import native +native.DictSubclass() + +[out] +deleting DictSubclass... + +[case testDel] +class A: + def __del__(self): + print("deleting A...") + +class B: + def __del__(self): + print("deleting B...") + +class C(B): + def __init__(self): + self.a = A() + + def __del__(self): + print("deleting C...") + super().__del__() + +class D(A): + pass + +# Just make sure that this class compiles (see issue mypy#19175). testDelForDictSubclass tests for +# correct output. +class NormDict(dict): + def __del__(self) -> None: + pass + +[file driver.py] +import native +native.C() +native.D() + +[out] +deleting C... +deleting B... +deleting A... +deleting A... + +[case testDelCircular] +import dataclasses +import typing + +i: int = 1 + +@dataclasses.dataclass +class C: + var: typing.Optional["C"] = dataclasses.field(default=None) + + def __del__(self): + global i + print(f"deleting C{i}...") + i = i + 1 + +[file driver.py] +import native +import gc + +c1 = native.C() +c2 = native.C() +c1.var = c2 +c2.var = c1 +del c1 +del c2 +gc.collect() + +[out] +deleting C1... +deleting C2... + +[case testDelException] +# The error message in the expected output of this test does not match CPython's error message due to the way mypyc compiles Python classes. If the error message is fixed, the expected output of this test will also change. +class F: + def __del__(self): + if True: + raise Exception("e2") + +[file driver.py] +import native +f = native.F() +del f + +[out] +Exception ignored in: +Traceback (most recent call last): + File "native.py", line 5, in __del__ + raise Exception("e2") +Exception: e2 + +[case testMypycAttrNativeClass] +from mypy_extensions import mypyc_attr +from testutil import assertRaises + +@mypyc_attr(native_class=False) +class AnnontatedNonExtensionClass: + pass + +class DerivedClass(AnnontatedNonExtensionClass): + pass + +class ImplicitExtensionClass(): + pass + +@mypyc_attr(native_class=True) +class AnnotatedExtensionClass(): + pass + +def test_function(): + setattr(AnnontatedNonExtensionClass, 'attr_class', 5) + assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == True) + assert(getattr(AnnontatedNonExtensionClass, 'attr_class') == 5) + delattr(AnnontatedNonExtensionClass, 'attr_class') + assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == False) + + inst = AnnontatedNonExtensionClass() + setattr(inst, 'attr_instance', 6) + assert(hasattr(inst, 'attr_instance') == True) + assert(getattr(inst, 'attr_instance') == 6) + delattr(inst, 'attr_instance') + assert(hasattr(inst, 'attr_instance') == False) + + setattr(DerivedClass, 'attr_class', 5) + assert(hasattr(DerivedClass, 'attr_class') == True) + assert(getattr(DerivedClass, 'attr_class') == 5) + delattr(DerivedClass, 'attr_class') + assert(hasattr(DerivedClass, 'attr_class') == False) + + derived_inst = DerivedClass() + setattr(derived_inst, 'attr_instance', 6) + assert(hasattr(derived_inst, 'attr_instance') == True) + assert(getattr(derived_inst, 'attr_instance') == 6) + delattr(derived_inst, 'attr_instance') + assert(hasattr(derived_inst, 'attr_instance') == False) + + ext_inst = ImplicitExtensionClass() + with assertRaises(AttributeError): + setattr(ext_inst, 'attr_instance', 6) + + explicit_ext_inst = AnnotatedExtensionClass() + with assertRaises(AttributeError): + setattr(explicit_ext_inst, 'attr_instance', 6) + +[case testMypycAttrNativeClassDunder] +from mypy_extensions import mypyc_attr +from typing import Generic, Optional, TypeVar + +_T = TypeVar("_T") + +get_count = set_count = del_count = 0 + +@mypyc_attr(native_class=False) +class Bar(Generic[_T]): + # Note the lack of __deletable__ + def __init__(self) -> None: + self.value: str = 'start' + def __get__(self, instance: _T, owner: Optional[type[_T]] = None) -> str: + global get_count + get_count += 1 + return self.value + def __set__(self, instance: _T, value: str) -> None: + global set_count + set_count += 1 + self.value = value + def __delete__(self, instance: _T) -> None: + global del_count + del_count += 1 + del self.value + +@mypyc_attr(native_class=False) +class Foo(object): + bar: Bar = Bar() + +[file driver.py] +import native + +f = native.Foo() +assert(hasattr(f, 'bar')) +assert(native.get_count == 1) +assert(f.bar == 'start') +assert(native.get_count == 2) +f.bar = 'test' +assert(f.bar == 'test') +assert(native.set_count == 1) +del f.bar +assert(not hasattr(f, 'bar')) +assert(native.del_count == 1) + +[case testMypycAttrNativeClassMeta] +from mypy_extensions import mypyc_attr +from typing import ClassVar, TypeVar + +_T = TypeVar("_T") + +@mypyc_attr(native_class=False) +class M(type): + count: ClassVar[int] = 0 + def make(cls: type[_T]) -> _T: + M.count += 1 + return cls() + +# implicit native_class=False +# see testMypycAttrNativeClassMetaError for when trying to set it True +class A(metaclass=M): + pass + +[file driver.py] +import native + +a: native.A = native.A.make() +assert(native.A.count == 1) + +class B(native.A): + pass + +b: B = B.make() +assert(B.count == 2) + +[case testTypeVarNarrowing] +from typing import TypeVar + +class B: + def __init__(self, x: int) -> None: + self.x = x +class C(B): + def __init__(self, x: int, y: str) -> None: + self.x = x + self.y = y + +T = TypeVar("T", bound=B) +def f(x: T) -> T: + if isinstance(x, C): + print("C", x.y) + return x + print("B", x.x) + return x + +[file driver.py] +from native import f, B, C + +f(B(1)) +f(C(1, "yes")) +[out] +B 1 +C yes + +[case testTypeObjectName] +from typing import Any +import re + +from dynamic import E, foo, Thing + +class C: pass +class D(C): pass + +def type_name(t: type[object]) -> str: + return t.__name__ + +def any_name(x: Any) -> str: + return x.__name__ + +def assert_type_name(x: Any) -> None: + assert type_name(x) == getattr(x, "__name__") + assert any_name(x) == getattr(x, "__name__") + +def assert_any_name(x: Any) -> None: + assert any_name(x) == getattr(x, "__name__") + +def test_type_name() -> None: + assert_type_name(C) + assert_type_name(D) + assert_type_name(int) + assert_type_name(E) + assert_type_name(re.Pattern) + +def test_module_name() -> None: + assert_any_name(re) + +def test_function_name() -> None: + assert_any_name(any_name) + assert_any_name(foo) + +def test_obj_name() -> None: + assert_any_name(Thing()) + +[file dynamic.py] +class E: pass + +def foo(): pass + +class Thing: + def __init__(self): + self.__name__ = "xyz" + +[case testTypeOfObject] +from typing import Any + +from dynamic import Dyn + +class Foo: pass +class Bar(Foo): pass + +def generic_type(x) -> type[object]: + return x.__class__ + +def test_built_in_type() -> None: + i: Any = int + l: Any = list + assert type(i()) is i().__class__ + assert type(i()) is int + assert type(l()) is list + n = 5 + assert n.__class__ is i + +def test_native_class() -> None: + f_any: Any = Foo() + b_any: Any = Bar() + f: Foo = f_any + b: Foo = b_any + if int("1"): # use int("1") to avoid constant folding + assert type(f) is Foo + assert type(b) is Bar + if int("2"): + assert f.__class__ is Foo + assert b.__class__ is Bar + if int("3"): + assert f_any.__class__ is Foo + assert b_any.__class__ is Bar + if int("4"): + assert type(f_any) is Foo + assert type(b_any) is Bar + +def test_python_class() -> None: + d = Dyn() + assert type(d) is Dyn + assert d.__class__ is Dyn + +[file dynamic.py] +class Dyn: pass + +[case testDunderNew] +from __future__ import annotations +from typing import Any, Union + +from testutil import assertRaises + +class Add: + l: IntLike + r: IntLike + + def __new__(cls, l: IntLike, r: IntLike) -> Any: + return ( + l if r == 0 else + r if l == 0 else + super().__new__(cls) + ) + + def __init__(self, l: IntLike, r: IntLike): + self.l = l + self.r = r + +IntLike = Union[int, Add] + +class RaisesException: + def __new__(cls, val: int) -> RaisesException: + if val == 0: + raise RuntimeError("Invalid value!") + return super().__new__(cls) + + def __init__(self, val: int) -> None: + self.val = val + +class ClsArgNotPassed: + def __new__(cls) -> Any: + return super().__new__(str) + +def test_dunder_new() -> None: + add_instance: Any = Add(1, 5) + assert type(add_instance) == Add + assert add_instance.l == 1 + assert add_instance.r == 5 + + # TODO: explicit types should not be needed but mypy does not use + # the return type of __new__ which makes mypyc add casts to Add. + right_int: Any = Add(0, 5) + assert type(right_int) == int + assert right_int == 5 + + left_int: Any = Add(1, 0) + assert type(left_int) == int + assert left_int == 1 + + with assertRaises(RuntimeError, "Invalid value!"): + raised = RaisesException(0) + + not_raised = RaisesException(1) + assert not_raised.val == 1 + + with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"): + str_as_cls = ClsArgNotPassed() + + +[case testDunderNewInInterpreted] +from __future__ import annotations +from typing import Any, Union + +class Add: + l: IntLike + r: IntLike + + def __new__(cls, l: IntLike, r: IntLike) -> Any: + print(f'running __new__ with {l} and {r}') + + return ( + l if r == 0 else + r if l == 0 else + super().__new__(cls) + ) + + def __init__(self, l: IntLike, r: IntLike): + self.l = l + self.r = r + + def __repr__(self) -> str: + return f'({self.l} + {self.r})' + +IntLike = Union[int, Add] + +class RaisesException: + def __new__(cls, val: int) -> RaisesException: + if val == 0: + raise RuntimeError("Invalid value!") + return super().__new__(cls) + + def __init__(self, val: int) -> None: + self.val = val + +class ClsArgNotPassed: + def __new__(cls) -> Any: + return super().__new__(str) + +[file driver.py] +from native import Add, ClsArgNotPassed, RaisesException + +from testutil import assertRaises + +print(f'{Add(1, 5)=}') +print(f'{Add(0, 5)=}') +print(f'{Add(1, 0)=}') + +with assertRaises(RuntimeError, "Invalid value!"): + raised = RaisesException(0) + +not_raised = RaisesException(1) +assert not_raised.val == 1 + +with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"): + str_as_cls = ClsArgNotPassed() + +[out] +running __new__ with 1 and 5 +Add(1, 5)=(1 + 5) +running __new__ with 0 and 5 +Add(0, 5)=5 +running __new__ with 1 and 0 +Add(1, 0)=1 + +[case testObjectDunderNew] +from __future__ import annotations +from typing import Any, Union + +from testutil import assertRaises + +class Add: + l: IntLike + r: IntLike + + def __new__(cls, l: IntLike, r: IntLike) -> Any: + return ( + l if r == 0 else + r if l == 0 else + object.__new__(cls) + ) + + def __init__(self, l: IntLike, r: IntLike): + self.l = l + self.r = r + +IntLike = Union[int, Add] + +class RaisesException: + def __new__(cls, val: int) -> RaisesException: + if val == 0: + raise RuntimeError("Invalid value!") + return object.__new__(cls) + + def __init__(self, val: int) -> None: + self.val = val + +class ClsArgNotPassed: + def __new__(cls) -> Any: + return object.__new__(str) + +class SkipsBase(Add): + def __new__(cls) -> Any: + obj = object.__new__(cls) + obj.l = 0 + obj.r = 0 + return obj + +def test_dunder_new() -> None: + add_instance: Any = Add(1, 5) + assert type(add_instance) == Add + assert add_instance.l == 1 + assert add_instance.r == 5 + + # TODO: explicit types should not be needed but mypy does not use + # the return type of __new__ which makes mypyc add casts to Add. + right_int: Any = Add(0, 5) + assert type(right_int) == int + assert right_int == 5 + + left_int: Any = Add(1, 0) + assert type(left_int) == int + assert left_int == 1 + + with assertRaises(RuntimeError, "Invalid value!"): + _ = RaisesException(0) + + not_raised = RaisesException(1) + assert not_raised.val == 1 + + with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"): + _ = ClsArgNotPassed() + + skip = SkipsBase.__new__(SkipsBase) + assert type(skip) == SkipsBase + assert skip.l == 0 + assert skip.r == 0 + +[case testObjectDunderNewInInterpreted] +from __future__ import annotations +from typing import Any, Union + +class Add: + l: IntLike + r: IntLike + + def __new__(cls, l: IntLike, r: IntLike) -> Any: + print(f'running __new__ with {l} and {r}') + + return ( + l if r == 0 else + r if l == 0 else + object.__new__(cls) + ) + + def __init__(self, l: IntLike, r: IntLike): + self.l = l + self.r = r + + def __repr__(self) -> str: + return f'({self.l} + {self.r})' + +IntLike = Union[int, Add] + +class RaisesException: + def __new__(cls, val: int) -> RaisesException: + if val == 0: + raise RuntimeError("Invalid value!") + return object.__new__(cls) + + def __init__(self, val: int) -> None: + self.val = val + +class ClsArgNotPassed: + def __new__(cls) -> Any: + return object.__new__(str) + +class SkipsBase(Add): + def __new__(cls) -> Any: + obj = object.__new__(cls) + obj.l = 0 + obj.r = 0 + return obj + +[file driver.py] +from native import Add, ClsArgNotPassed, RaisesException, SkipsBase + +from testutil import assertRaises + +print(f'{Add(1, 5)=}') +print(f'{Add(0, 5)=}') +print(f'{Add(1, 0)=}') + +with assertRaises(RuntimeError, "Invalid value!"): + raised = RaisesException(0) + +not_raised = RaisesException(1) +assert not_raised.val == 1 + +with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"): + str_as_cls = ClsArgNotPassed() + +skip = SkipsBase.__new__(SkipsBase) +assert type(skip) == SkipsBase +assert skip.l == 0 +assert skip.r == 0 + +[out] +running __new__ with 1 and 5 +Add(1, 5)=(1 + 5) +running __new__ with 0 and 5 +Add(0, 5)=5 +running __new__ with 1 and 0 +Add(1, 0)=1 + +[case testInheritedDunderNew] +from __future__ import annotations +from mypy_extensions import mypyc_attr +from testutil import assertRaises +from typing_extensions import Self + +from m import interpreted_subclass + +@mypyc_attr(allow_interpreted_subclasses=True) +class Base: + val: int + + def __new__(cls, val: int) -> Self: + obj = super().__new__(cls) + obj.val = val + 1 + return obj + + def __init__(self, val: int) -> None: + self.init_val = val + + def method(self) -> int: + raise NotImplementedError + +class Sub(Base): + + def __new__(cls, val: int) -> Self: + return super().__new__(cls, val + 1) + + def __init__(self, val: int) -> None: + super().__init__(val) + self.init_val = self.init_val * 2 + + def method(self) -> int: + return 0 + +class SubWithoutNew(Base): + sub_only_str = "" + sub_only_int: int + + def __init__(self, val: int) -> None: + super().__init__(val) + self.init_val = self.init_val * 2 + + def method(self) -> int: + return 1 + +class BaseWithoutInterpretedSubclasses: + val: int + + def __new__(cls, val: int) -> Self: + obj = super().__new__(cls) + obj.val = val + 1 + return obj + + def __init__(self, val: int) -> None: + self.init_val = val + + def method(self) -> int: + raise NotImplementedError + +class SubNoInterpreted(BaseWithoutInterpretedSubclasses): + def __new__(cls, val: int) -> Self: + return super().__new__(cls, val + 1) + + def __init__(self, val: int) -> None: + super().__init__(val) + self.init_val = self.init_val * 2 + + def method(self) -> int: + return 0 + +class SubNoInterpretedWithoutNew(BaseWithoutInterpretedSubclasses): + def __init__(self, val: int) -> None: + super().__init__(val) + self.init_val = self.init_val * 2 + + def method(self) -> int: + return 1 + +def test_inherited_dunder_new() -> None: + b = Base(42) + assert type(b) == Base + assert b.val == 43 + assert b.init_val == 42 + with assertRaises(NotImplementedError): + b.method() + + s = Sub(42) + assert type(s) == Sub + assert s.val == 44 + assert s.init_val == 84 + assert s.method() == 0 + + s2 = SubWithoutNew(42) + assert type(s2) == SubWithoutNew + assert s2.val == 43 + assert s2.init_val == 84 + assert s2.method() == 1 + assert s2.sub_only_str == "" + with assertRaises(AttributeError): + s2.sub_only_int + s2.sub_only_int = 11 + assert s2.sub_only_int == 11 + +def test_inherited_dunder_new_without_interpreted_subclasses() -> None: + b = BaseWithoutInterpretedSubclasses(42) + assert type(b) == BaseWithoutInterpretedSubclasses + assert b.val == 43 + assert b.init_val == 42 + with assertRaises(NotImplementedError): + b.method() + + s = SubNoInterpreted(42) + assert type(s) == SubNoInterpreted + assert s.val == 44 + assert s.init_val == 84 + assert s.method() == 0 + + s2 = SubNoInterpretedWithoutNew(42) + assert type(s2) == SubNoInterpretedWithoutNew + assert s2.val == 43 + assert s2.init_val == 84 + assert s2.method() == 1 + +def test_interpreted_subclass() -> None: + interpreted_subclass(Base) + +[file m.py] +from __future__ import annotations +from testutil import assertRaises +from typing_extensions import Self + +def interpreted_subclass(base) -> None: + b = base(42) + assert type(b) == base + assert b.val == 43 + assert b.init_val == 42 + with assertRaises(NotImplementedError): + b.method() + + class InterpretedSub(base): + def __new__(cls, val: int) -> Self: + return super().__new__(cls, val + 1) + + def __init__(self, val: int) -> None: + super().__init__(val) + self.init_val : int = self.init_val * 2 + + def method(self) -> int: + return 3 + + s = InterpretedSub(42) + assert type(s) == InterpretedSub + assert s.val == 44 + assert s.init_val == 84 + assert s.method() == 3 + + class InterpretedSubWithoutNew(base): + sub_only_str = "" + sub_only_int: int + + def __init__(self, val: int) -> None: + super().__init__(val) + self.init_val : int = self.init_val * 2 + + def method(self) -> int: + return 4 + + s2 = InterpretedSubWithoutNew(42) + assert type(s2) == InterpretedSubWithoutNew + assert s2.val == 43 + assert s2.init_val == 84 + assert s2.method() == 4 + assert s2.sub_only_str == "" + with assertRaises(AttributeError): + s2.sub_only_int + s2.sub_only_int = 11 + assert s2.sub_only_int == 11 + +[typing fixtures/typing-full.pyi] + +[case testDunderNewInitArgMismatch] +from __future__ import annotations +from testutil import assertRaises + +class Test0: + @classmethod + def __new__(cls, val: int = 42) -> Test0: + obj = super().__new__(cls) + obj.val = val + return obj + + def __init__(self) -> None: + self.val = 0 + +class Test1: + def __new__(cls, val: int) -> Test1: + obj = super().__new__(cls) + obj.val = val + return obj + + def __init__(self) -> None: + self.val = 0 + +class Test2: + def __new__(cls) -> Test2: + obj = super().__new__(cls) + return obj + + def __init__(self, val: int) -> None: + self.val = val + +def test_arg_mismatch() -> None: + t0 = Test0() + assert t0.val == 0 + t0 = Test0.__new__(1) + assert t0.val == 1 + with assertRaises(TypeError, "__new__() missing required argument 'val'"): + t1 = Test1() + t1 = Test1.__new__(Test1, 2) + assert t1.val == 2 + with assertRaises(TypeError, "__new__() takes at most 0 arguments"): + t2 = Test2(42) + t2 = Test2.__new__(Test2) + with assertRaises(AttributeError, "attribute 'val' of 'Test2' undefined"): + print(t2.val) + +[case testDunderNewInitArgMismatchInInterpreted] +from __future__ import annotations + +class Test0: + # TODO: It should be possible to annotate '@classmethod' here + # but when it's added calling __new__ in interpreted code + # without the explicit type param results in a TypeError. + def __new__(cls, val: int = 42) -> Test0: + obj = super().__new__(cls) + obj.val = val + return obj + + def __init__(self) -> None: + self.val = 0 + +class Test1: + def __new__(cls, val: int) -> Test1: + obj = super().__new__(cls) + obj.val = val + return obj + + def __init__(self) -> None: + self.val = 0 + +class Test2: + def __new__(cls) -> Test2: + obj = super().__new__(cls) + return obj + + def __init__(self, val: int) -> None: + self.val = val + +[file driver.py] +from native import Test0, Test1, Test2 +from testutil import assertRaises + +t0 = Test0() +assert t0.val == 0 +t0 = Test0.__new__(Test0, 1) +assert t0.val == 1 +with assertRaises(TypeError, "__new__() missing required argument 'val'"): + t1 = Test1() +t1 = Test1.__new__(Test1, 2) +assert t1.val == 2 +with assertRaises(TypeError, "__new__() takes at most 0 arguments"): + t2 = Test2(42) +t2 = Test2.__new__(Test2) +with assertRaises(AttributeError, "attribute 'val' of 'Test2' undefined"): + print(t2.val) + +[case testDunderNewAttributeAccess] +from __future__ import annotations + +from mypy_extensions import u8 +from testutil import assertRaises + +class Test: + native: int + generic: object + bitfield: u8 + default: int = 5 + + def __new__(cls, native: int, generic: object, bitfield: u8) -> Test: + obj = super().__new__(cls) + + with assertRaises(AttributeError, "attribute 'native' of 'Test' undefined"): + print(obj.native) + with assertRaises(AttributeError, "attribute 'generic' of 'Test' undefined"): + print(obj.generic) + with assertRaises(AttributeError, "attribute 'bitfield' of 'Test' undefined"): + print(obj.bitfield) + + obj.native = native + obj.generic = generic + obj.bitfield = bitfield + + obj.native = obj.native + 1 + obj.generic = obj.generic.__str__() + obj.bitfield = obj.bitfield & 0x0F + obj.default = obj.default * 2 + return obj + +def test_attribute_access() -> None: + t = Test(42, {}, 0xCC) + assert t.native == 43 + assert t.generic == "{}" + assert t.bitfield == 0x0C + assert t.default == 10 + +[case testDunderNewAttributeAccessInInterpreted] +from __future__ import annotations + +from mypy_extensions import u8 +from testutil import assertRaises + +class Test: + native: int + generic: object + bitfield: u8 + default: int = 5 + + def __new__(cls, native: int, generic: object, bitfield: u8) -> Test: + obj = super().__new__(cls) + + with assertRaises(AttributeError, "attribute 'native' of 'Test' undefined"): + print(obj.native) + with assertRaises(AttributeError, "attribute 'generic' of 'Test' undefined"): + print(obj.generic) + with assertRaises(AttributeError, "attribute 'bitfield' of 'Test' undefined"): + print(obj.bitfield) + + obj.native = native + obj.generic = generic + obj.bitfield = bitfield + + obj.native = obj.native + 1 + obj.generic = obj.generic.__str__() + obj.bitfield = obj.bitfield & 0x0F + obj.default = obj.default * 2 + return obj + +[file driver.py] +from native import Test + +t = Test(42, {}, 0xCC) +assert t.native == 43 +assert t.generic == "{}" +assert t.bitfield == 0x0C +assert t.default == 10 + +[case testUntransformedDunderNewCalls] +from testutil import assertRaises +from typing import Any + +class TestStrCls: + def __new__(cls): + return str.__new__(cls) + + @classmethod + def factory(cls): + return str.__new__(cls) + +class TestStrStr: + def __new__(cls): + return str.__new__(str) + + @classmethod + def factory(cls): + return str.__new__(str) + +class TestStrInt: + def __new__(cls): + return str.__new__(int) + + @classmethod + def factory(cls): + return str.__new__(int) + +def test_untransformed_dunder_new() -> None: + with assertRaises(TypeError, "str.__new__(TestStrCls): TestStrCls is not a subtype of str"): + i = TestStrCls() + + j: Any = TestStrStr() + assert j == "" + + with assertRaises(TypeError, "str.__new__(int): int is not a subtype of str"): + k = TestStrInt() + + with assertRaises(TypeError, "str.__new__(TestStrCls): TestStrCls is not a subtype of str"): + i = TestStrCls.factory() + + j = TestStrStr.factory() + assert j == "" + + with assertRaises(TypeError, "str.__new__(int): int is not a subtype of str"): + k = TestStrInt.factory() + +[case testPerTypeFreeList] +from __future__ import annotations + +from mypy_extensions import mypyc_attr + +a = [] + +@mypyc_attr(free_list_len=1) +class Foo: + def __init__(self, x: int) -> None: + self.x = x + a.append(x) + +def test_alloc() -> None: + x: Foo | None + y: Foo | None + + x = Foo(1) + assert x.x == 1 + x = None + + x = Foo(2) + assert x.x == 2 + y = Foo(3) + assert x.x == 2 + assert y.x == 3 + x = None + y = None + assert a == [1, 2, 3] + + x = Foo(4) + assert x.x == 4 + y = Foo(5) + assert x.x == 4 + assert y.x == 5 + +@mypyc_attr(free_list_len=1) +class Base: + def __init__(self, x: str) -> None: + self.x = x + +class Deriv(Base): + def __init__(self, x: str, y: str) -> None: + super().__init__(x) + self.y = y + +@mypyc_attr(free_list_len=1) +class Deriv2(Base): + def __init__(self, x: str, y: str) -> None: + super().__init__(x) + self.y = y + +def test_inheritance() -> None: + x: Base | None + y: Base | None + x = Base('x' + str()) + y = Base('y' + str()) + y = None + d = Deriv('a' + str(), 'b' + str()) + assert type(d) is Deriv + assert d.x == 'a' + assert d.y == 'b' + assert x.x == 'x' + y = Base('z' + str()) + assert d.x == 'a' + assert d.y == 'b' + assert y.x == 'z' + x = None + y = None + +def test_inheritance_2() -> None: + x: Base | None + y: Base | None + d: Deriv2 | None + x = Base('x' + str()) + y = Base('y' + str()) + y = None + d = Deriv2('a' + str(), 'b' + str()) + assert type(d) is Deriv2 + assert d.x == 'a' + assert d.y == 'b' + assert x.x == 'x' + d = None + d = Deriv2('c' + str(), 'd' + str()) + assert type(d) is Deriv2 + assert d.x == 'c' + assert d.y == 'd' + assert x.x == 'x' + y = Base('z' + str()) + assert type(y) is Base + assert d.x == 'c' + assert d.y == 'd' + assert y.x == 'z' + x = None + y = None + d = None + +[case testDunderGetAttr] +from mypy_extensions import mypyc_attr +from typing import ClassVar + +class GetAttr: + class_var = "x" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int): + self.extra_attrs = extra_attrs + self.regular_attr = regular_attr + + def __getattr__(self, attr: str) -> object: + return self.extra_attrs.get(attr) + +class GetAttrDefault: + class_var: ClassVar[str] = "x" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int): + self.extra_attrs = extra_attrs + self.regular_attr = regular_attr + + def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> object: + return self.extra_attrs.get(attr, default * mult) + +class GetAttrInherited(GetAttr): + subclass_var = "y" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int): + super().__init__(extra_attrs, regular_attr) + self.sub_attr = sub_attr + +class GetAttrOverridden(GetAttr): + subclass_var: ClassVar[str] = "y" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int): + super().__init__(extra_attrs, regular_attr) + self.sub_attr = sub_attr + + def __getattr__(self, attr: str) -> str: + return attr + +@mypyc_attr(native_class=False) +class GetAttrNonNative: + class_var = "x" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int): + self.extra_attrs = extra_attrs + self.regular_attr = regular_attr + + def __getattr__(self, attr: str) -> object: + return self.extra_attrs.get(attr) + +def test_getattr() -> None: + i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == None + assert i.__getattr__("class_var") == None + assert i.__getattr__("four") == None + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "class_var") == "x" + assert getattr(i, "four") == None + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.class_var == "x" + assert i.four == None + + assert i.__class__ == GetAttr + + i.extra_attrs["regular_attr"] = (4, 4, 4) + assert i.__getattr__("regular_attr") == (4, 4, 4) + assert getattr(i, "regular_attr") == 42 + assert i.regular_attr == 42 + +def test_getattr_default() -> None: + i = GetAttrDefault({"one": 1, "two": "two", "three": 3.14}, 42) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == 8 + assert i.__getattr__("class_var") == 8 + assert i.__getattr__("four", 4, 3) == 12 + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "class_var") == "x" + assert getattr(i, "four") == 8 + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.class_var == "x" + assert i.four == 8 + + assert i.__class__ == GetAttrDefault + + i.extra_attrs["class_var"] = (4, 4, 4) + assert i.__getattr__("class_var") == (4, 4, 4) + assert getattr(i, "class_var") == "x" + assert i.class_var == "x" + +def test_getattr_inherited() -> None: + i = GetAttrInherited({"one": 1, "two": "two", "three": 3.14}, 42, 24) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == None + assert i.__getattr__("sub_attr") == None + assert i.__getattr__("class_var") == None + assert i.__getattr__("subclass_var") == None + assert i.__getattr__("four") == None + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "sub_attr") == 24 + assert getattr(i, "class_var") == "x" + assert getattr(i, "subclass_var") == "y" + assert getattr(i, "four") == None + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.sub_attr == 24 + assert i.class_var == "x" + assert i.subclass_var == "y" + assert i.four == None + + assert i.__class__ == GetAttrInherited + + i.extra_attrs["sub_attr"] = (4, 4, 4) + assert i.__getattr__("sub_attr") == (4, 4, 4) + assert getattr(i, "sub_attr") == 24 + assert i.sub_attr == 24 + + base_ref: GetAttr = i + assert getattr(base_ref, "sub_attr") == 24 + assert base_ref.sub_attr == 24 + + assert getattr(base_ref, "subclass_var") == "y" + assert base_ref.subclass_var == "y" + + assert getattr(base_ref, "new") == None + assert base_ref.new == None + + assert base_ref.__class__ == GetAttrInherited + + +def test_getattr_overridden() -> None: + i = GetAttrOverridden({"one": 1, "two": "two", "three": 3.14}, 42, 24) + assert i.__getattr__("one") == "one" + assert i.__getattr__("regular_attr") == "regular_attr" + assert i.__getattr__("sub_attr") == "sub_attr" + assert i.__getattr__("class_var") == "class_var" + assert i.__getattr__("subclass_var") == "subclass_var" + assert i.__getattr__("four") == "four" + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "sub_attr") == 24 + assert getattr(i, "class_var") == "x" + assert getattr(i, "subclass_var") == "y" + assert getattr(i, "four") == "four" + + assert i.three == "three" + assert i.regular_attr == 42 + assert i.sub_attr == 24 + assert i.class_var == "x" + assert i.subclass_var == "y" + assert i.four == "four" + + assert i.__class__ == GetAttrOverridden + + i.extra_attrs["subclass_var"] = (4, 4, 4) + assert i.__getattr__("subclass_var") == "subclass_var" + assert getattr(i, "subclass_var") == "y" + assert i.subclass_var == "y" + + base_ref: GetAttr = i + assert getattr(base_ref, "sub_attr") == 24 + assert base_ref.sub_attr == 24 + + assert getattr(base_ref, "subclass_var") == "y" + assert base_ref.subclass_var == "y" + + assert getattr(base_ref, "new") == "new" + assert base_ref.new == "new" + + assert base_ref.__class__ == GetAttrOverridden + +def test_getattr_nonnative() -> None: + i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == None + assert i.__getattr__("class_var") == None + assert i.__getattr__("four") == None + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "class_var") == "x" + assert getattr(i, "four") == None + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.class_var == "x" + assert i.four == None + + assert i.__class__ == GetAttr + + i.extra_attrs["regular_attr"] = (4, 4, 4) + assert i.__getattr__("regular_attr") == (4, 4, 4) + assert getattr(i, "regular_attr") == 42 + assert i.regular_attr == 42 + +[typing fixtures/typing-full.pyi] + +[case testDunderGetAttrInterpreted] +from mypy_extensions import mypyc_attr +from typing import ClassVar + +class GetAttr: + class_var = "x" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int): + self.extra_attrs = extra_attrs + self.regular_attr = regular_attr + + def __getattr__(self, attr: str) -> object: + return self.extra_attrs.get(attr) + +class GetAttrDefault: + class_var: ClassVar[str] = "x" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int): + self.extra_attrs = extra_attrs + self.regular_attr = regular_attr + + def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> object: + return self.extra_attrs.get(attr, default * mult) + +class GetAttrInherited(GetAttr): + subclass_var = "y" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int): + super().__init__(extra_attrs, regular_attr) + self.sub_attr = sub_attr + +class GetAttrOverridden(GetAttr): + subclass_var: ClassVar[str] = "y" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int): + super().__init__(extra_attrs, regular_attr) + self.sub_attr = sub_attr + + def __getattr__(self, attr: str) -> str: + return attr + +@mypyc_attr(native_class=False) +class GetAttrNonNative: + class_var = "x" + + def __init__(self, extra_attrs: dict[str, object], regular_attr: int): + self.extra_attrs = extra_attrs + self.regular_attr = regular_attr + + def __getattr__(self, attr: str) -> object: + return self.extra_attrs.get(attr) + +[file driver.py] +from native import GetAttr, GetAttrDefault, GetAttrInherited, GetAttrOverridden, GetAttrNonNative + +def test_getattr() -> None: + i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == None + assert i.__getattr__("class_var") == None + assert i.__getattr__("four") == None + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "class_var") == "x" + assert getattr(i, "four") == None + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.class_var == "x" + assert i.four == None + + assert i.__class__ == GetAttr + + i.extra_attrs["regular_attr"] = (4, 4, 4) + assert i.__getattr__("regular_attr") == (4, 4, 4) + assert getattr(i, "regular_attr") == 42 + assert i.regular_attr == 42 + +def test_getattr_default() -> None: + i = GetAttrDefault({"one": 1, "two": "two", "three": 3.14}, 42) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == 8 + assert i.__getattr__("class_var") == 8 + assert i.__getattr__("four", 4, 3) == 12 + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "class_var") == "x" + assert getattr(i, "four") == 8 + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.class_var == "x" + assert i.four == 8 + + assert i.__class__ == GetAttrDefault + + i.extra_attrs["class_var"] = (4, 4, 4) + assert i.__getattr__("class_var") == (4, 4, 4) + assert getattr(i, "class_var") == "x" + assert i.class_var == "x" + +def test_getattr_inherited() -> None: + i = GetAttrInherited({"one": 1, "two": "two", "three": 3.14}, 42, 24) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == None + assert i.__getattr__("sub_attr") == None + assert i.__getattr__("class_var") == None + assert i.__getattr__("subclass_var") == None + assert i.__getattr__("four") == None + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "sub_attr") == 24 + assert getattr(i, "class_var") == "x" + assert getattr(i, "subclass_var") == "y" + assert getattr(i, "four") == None + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.sub_attr == 24 + assert i.class_var == "x" + assert i.subclass_var == "y" + assert i.four == None + + assert i.__class__ == GetAttrInherited + + i.extra_attrs["sub_attr"] = (4, 4, 4) + assert i.__getattr__("sub_attr") == (4, 4, 4) + assert getattr(i, "sub_attr") == 24 + assert i.sub_attr == 24 + + base_ref: GetAttr = i + assert getattr(base_ref, "sub_attr") == 24 + assert base_ref.sub_attr == 24 + + assert getattr(base_ref, "subclass_var") == "y" + assert base_ref.subclass_var == "y" + + assert getattr(base_ref, "new") == None + assert base_ref.new == None + + assert base_ref.__class__ == GetAttrInherited + + +def test_getattr_overridden() -> None: + i = GetAttrOverridden({"one": 1, "two": "two", "three": 3.14}, 42, 24) + assert i.__getattr__("one") == "one" + assert i.__getattr__("regular_attr") == "regular_attr" + assert i.__getattr__("sub_attr") == "sub_attr" + assert i.__getattr__("class_var") == "class_var" + assert i.__getattr__("subclass_var") == "subclass_var" + assert i.__getattr__("four") == "four" + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "sub_attr") == 24 + assert getattr(i, "class_var") == "x" + assert getattr(i, "subclass_var") == "y" + assert getattr(i, "four") == "four" + + assert i.three == "three" + assert i.regular_attr == 42 + assert i.sub_attr == 24 + assert i.class_var == "x" + assert i.subclass_var == "y" + assert i.four == "four" + + assert i.__class__ == GetAttrOverridden + + i.extra_attrs["subclass_var"] = (4, 4, 4) + assert i.__getattr__("subclass_var") == "subclass_var" + assert getattr(i, "subclass_var") == "y" + assert i.subclass_var == "y" + + base_ref: GetAttr = i + assert getattr(base_ref, "sub_attr") == 24 + assert base_ref.sub_attr == 24 + + assert getattr(base_ref, "subclass_var") == "y" + assert base_ref.subclass_var == "y" + + assert getattr(base_ref, "new") == "new" + assert base_ref.new == "new" + + assert base_ref.__class__ == GetAttrOverridden + +def test_getattr_nonnative() -> None: + i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42) + assert i.__getattr__("one") == 1 + assert i.__getattr__("regular_attr") == None + assert i.__getattr__("class_var") == None + assert i.__getattr__("four") == None + + assert getattr(i, "two") == "two" + assert getattr(i, "regular_attr") == 42 + assert getattr(i, "class_var") == "x" + assert getattr(i, "four") == None + + assert i.three == 3.14 + assert i.regular_attr == 42 + assert i.class_var == "x" + assert i.four == None + + assert i.__class__ == GetAttr + + i.extra_attrs["regular_attr"] = (4, 4, 4) + assert i.__getattr__("regular_attr") == (4, 4, 4) + assert getattr(i, "regular_attr") == 42 + assert i.regular_attr == 42 + +test_getattr() +test_getattr_default() +test_getattr_inherited() +test_getattr_overridden() +test_getattr_nonnative() + +[typing fixtures/typing-full.pyi] + +[case testDunderSetAttr] +from mypy_extensions import mypyc_attr +from testutil import assertRaises +from typing import ClassVar + +class SetAttr: + _attributes: dict[str, object] + regular_attr: int + class_var: ClassVar[str] = "x" + const: int = 42 + + def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None: + super().__setattr__("_attributes", extra_attrs) + super().__setattr__("regular_attr", regular_attr) + + def __setattr__(self, key: str, val: object) -> None: + if key == "regular_attr": + super().__setattr__("regular_attr", val) + elif key == "class_var" or key == "const": + raise AttributeError() + else: + self._attributes[key] = val + + def __getattr__(self, key: str) -> object: + return self._attributes.get(key) + +class SetAttrInherited(SetAttr): + def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None: + super().__init__(regular_attr, extra_attrs) + +class SetAttrOverridden(SetAttr): + sub_attr: int + subclass_var: ClassVar[str] = "y" + + def __init__(self, regular_attr: int, sub_attr: int, extra_attrs: dict[str, object]) -> None: + super().__init__(regular_attr, extra_attrs) + object.__setattr__(self, "sub_attr", sub_attr) + + def __setattr__(self, key: str, val: object) -> None: + if key == "sub_attr": + object.__setattr__(self, "sub_attr", val) + elif key == "subclass_var": + raise AttributeError() + else: + super().__setattr__(key, val) + + def __delattr__(self, key: str) -> None: + del self._attributes[key] + +@mypyc_attr(native_class=False) +class SetAttrNonNative: + _attributes: dict[str, object] + regular_attr: int + class_var: ClassVar[str] = "x" + const: int = 42 + + def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None: + super().__setattr__("_attributes", extra_attrs) + super().__setattr__("regular_attr", regular_attr) + + def __setattr__(self, key: str, val: object) -> None: + if key == "regular_attr": + super().__setattr__("regular_attr", val) + elif key == "class_var" or key == "const": + raise AttributeError() + else: + self._attributes[key] = val + + def __getattr__(self, key: str) -> object: + return self._attributes.get(key) + +class NoSetAttr: + def __init__(self, attr: int) -> None: + self.attr = attr + + def object_setattr(self, attr: str, val: object) -> None: + object.__setattr__(self, attr, val) + + def super_setattr(self, attr: str, val: object) -> None: + super().__setattr__(attr, val) + +@mypyc_attr(native_class=False) +class NoSetAttrNonNative: + def __init__(self, attr: int) -> None: + self.attr = attr + + def object_setattr(self, attr: str, val: object) -> None: + object.__setattr__(self, attr, val) + + def super_setattr(self, attr: str, val: object) -> None: + super().__setattr__(attr, val) + + def __getattr__(self, attr: str) -> object: + pass + +def test_setattr() -> None: + i = SetAttr(99, {"one": 1}) + assert i.class_var == "x" + assert i.regular_attr == 99 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + with assertRaises(AttributeError): + i.const = 45 + + # Doesn't work because there's no __delattr__. + with assertRaises(AttributeError): + del i.four + +def test_setattr_inherited() -> None: + i = SetAttrInherited(99, {"one": 1}) + assert i.class_var == "x" + assert i.regular_attr == 99 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + with assertRaises(AttributeError): + i.const = 45 + + # Doesn't work because there's no __delattr__. + with assertRaises(AttributeError): + del i.four + +def test_setattr_overridden() -> None: + i = SetAttrOverridden(99, 1, {"one": 1}) + assert i.class_var == "x" + assert i.subclass_var == "y" + assert i.regular_attr == 99 + assert i.sub_attr == 1 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + i.__setattr__("sub_attr", 2) + assert i.sub_attr == 2 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("subclass_var", "a") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + setattr(i, "sub_attr", 3) + assert i.sub_attr == 3 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "subclass_var", "b") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + i.sub_attr = 4 + assert i.sub_attr == 4 + with assertRaises(AttributeError): + i.const = 45 + + del i.four + assert "four" not in i._attributes + + delattr(i, "three") + assert "three" not in i._attributes + + i.__delattr__("two") + assert "two" not in i._attributes + + base_ref: SetAttr = i + setattr(base_ref, "sub_attr", 5) + assert base_ref.sub_attr == 5 + + base_ref.sub_attr = 6 + assert base_ref.sub_attr == 6 + + with assertRaises(AttributeError): + setattr(base_ref, "subclass_var", "c") + + base_ref.new_attr = "new_attr" + assert base_ref.new_attr == "new_attr" + + del base_ref.new_attr + assert "new_attr" not in base_ref._attributes + +def test_setattr_nonnative() -> None: + i = SetAttrNonNative(99, {"one": 1}) + assert i.class_var == "x" + assert i.regular_attr == 99 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + with assertRaises(AttributeError): + i.const = 45 + + # Doesn't work because there's no __delattr__. + with assertRaises(AttributeError): + del i.four + +def test_no_setattr() -> None: + i = NoSetAttr(99) + i.super_setattr("attr", 100) + assert i.attr == 100 + + i.object_setattr("attr", 101) + assert i.attr == 101 + + object.__setattr__(i, "attr", 102) + assert i.attr == 102 + + with assertRaises(AttributeError): + i.super_setattr("not_attr", 100) + + with assertRaises(AttributeError): + i.object_setattr("not_attr", 101) + + with assertRaises(AttributeError): + object.__setattr__(i, "not_attr", 102) + +def test_no_setattr_nonnative() -> None: + i = NoSetAttrNonNative(99) + i.super_setattr("attr", 100) + assert i.attr == 100 + + i.object_setattr("attr", 101) + assert i.attr == 101 + + object.__setattr__(i, "attr", 102) + assert i.attr == 102 + + i.super_setattr("one", 100) + assert i.one == 100 + + i.object_setattr("two", 101) + assert i.two == 101 + + object.__setattr__(i, "three", 102) + assert i.three == 102 + + del i.three + assert i.three == None + + delattr(i, "two") + assert i.two == None + + object.__delattr__(i, "one") + assert i.one == None + +[typing fixtures/typing-full.pyi] + +[case testDunderSetAttrInterpreted] +from mypy_extensions import mypyc_attr +from typing import ClassVar + +class SetAttr: + _attributes: dict[str, object] + regular_attr: int + class_var: ClassVar[str] = "x" + const: int = 42 + + def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None: + super().__setattr__("_attributes", extra_attrs) + super().__setattr__("regular_attr", regular_attr) + + def __setattr__(self, key: str, val: object) -> None: + if key == "regular_attr": + super().__setattr__("regular_attr", val) + elif key == "class_var" or key == "const": + raise AttributeError() + else: + self._attributes[key] = val + + def __getattr__(self, key: str) -> object: + return self._attributes.get(key) + +class SetAttrInherited(SetAttr): + def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None: + super().__init__(regular_attr, extra_attrs) + +class SetAttrOverridden(SetAttr): + sub_attr: int + subclass_var: ClassVar[str] = "y" + + def __init__(self, regular_attr: int, sub_attr: int, extra_attrs: dict[str, object]) -> None: + super().__init__(regular_attr, extra_attrs) + object.__setattr__(self, "sub_attr", sub_attr) + + def __setattr__(self, key: str, val: object) -> None: + if key == "sub_attr": + object.__setattr__(self, "sub_attr", val) + elif key == "subclass_var": + raise AttributeError() + else: + super().__setattr__(key, val) + + def __delattr__(self, key: str) -> None: + del self._attributes[key] + +@mypyc_attr(native_class=False) +class SetAttrNonNative: + _attributes: dict[str, object] + regular_attr: int + class_var: ClassVar[str] = "x" + const: int = 42 + + def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None: + super().__setattr__("_attributes", extra_attrs) + super().__setattr__("regular_attr", regular_attr) + + def __setattr__(self, key: str, val: object) -> None: + if key == "regular_attr": + super().__setattr__("regular_attr", val) + elif key == "class_var" or key == "const": + raise AttributeError() + else: + self._attributes[key] = val + + def __getattr__(self, key: str) -> object: + return self._attributes.get(key) + +class NoSetAttr: + def __init__(self, attr: int) -> None: + self.attr = attr + + def object_setattr(self, attr: str, val: object) -> None: + object.__setattr__(self, attr, val) + + def super_setattr(self, attr: str, val: object) -> None: + super().__setattr__(attr, val) + +@mypyc_attr(native_class=False) +class NoSetAttrNonNative: + def __init__(self, attr: int) -> None: + self.attr = attr + + def object_setattr(self, attr: str, val: object) -> None: + object.__setattr__(self, attr, val) + + def super_setattr(self, attr: str, val: object) -> None: + super().__setattr__(attr, val) + + def __getattr__(self, attr: str) -> object: + pass + +[file driver.py] +from native import SetAttr, SetAttrInherited, SetAttrOverridden, SetAttrNonNative, NoSetAttr, NoSetAttrNonNative +from testutil import assertRaises + +def test_setattr() -> None: + i = SetAttr(99, {"one": 1}) + assert i.class_var == "x" + assert i.regular_attr == 99 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + with assertRaises(AttributeError): + i.const = 45 + + # Doesn't work because there's no __delattr__. + with assertRaises(AttributeError): + del i.four + +def test_setattr_inherited() -> None: + i = SetAttrInherited(99, {"one": 1}) + assert i.class_var == "x" + assert i.regular_attr == 99 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + with assertRaises(AttributeError): + i.const = 45 + + # Doesn't work because there's no __delattr__. + with assertRaises(AttributeError): + del i.four + +def test_setattr_overridden() -> None: + i = SetAttrOverridden(99, 1, {"one": 1}) + assert i.class_var == "x" + assert i.subclass_var == "y" + assert i.regular_attr == 99 + assert i.sub_attr == 1 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + i.__setattr__("sub_attr", 2) + assert i.sub_attr == 2 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("subclass_var", "a") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + setattr(i, "sub_attr", 3) + assert i.sub_attr == 3 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "subclass_var", "b") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + i.sub_attr = 4 + assert i.sub_attr == 4 + with assertRaises(AttributeError): + i.const = 45 + + del i.four + assert "four" not in i._attributes + + delattr(i, "three") + assert "three" not in i._attributes + + i.__delattr__("two") + assert "two" not in i._attributes + + base_ref: SetAttr = i + setattr(base_ref, "sub_attr", 5) + assert base_ref.sub_attr == 5 + + base_ref.sub_attr = 6 + assert base_ref.sub_attr == 6 + + with assertRaises(AttributeError): + setattr(base_ref, "subclass_var", "c") + + base_ref.new_attr = "new_attr" + assert base_ref.new_attr == "new_attr" + + del base_ref.new_attr + assert "new_attr" not in base_ref._attributes + +def test_setattr_nonnative() -> None: + i = SetAttrNonNative(99, {"one": 1}) + assert i.class_var == "x" + assert i.regular_attr == 99 + assert i.one == 1 + assert i.two == None + assert i.const == 42 + + i.__setattr__("two", "2") + assert i.two == "2" + i.__setattr__("regular_attr", 101) + assert i.regular_attr == 101 + with assertRaises(AttributeError): + i.__setattr__("class_var", "y") + with assertRaises(AttributeError): + i.__setattr__("const", 43) + + setattr(i, "three", (3,3,3)) + assert i.three == (3,3,3) + setattr(i, "regular_attr", 102) + assert i.regular_attr == 102 + with assertRaises(AttributeError): + setattr(i, "class_var", "z") + with assertRaises(AttributeError): + setattr(i, "const", 44) + + i.four = [4,4] + assert i.four == [4,4] + i.regular_attr = 103 + assert i.regular_attr == 103 + with assertRaises(AttributeError): + i.const = 45 + + # Doesn't work because there's no __delattr__. + with assertRaises(AttributeError): + del i.four + +def test_no_setattr() -> None: + i = NoSetAttr(99) + i.super_setattr("attr", 100) + assert i.attr == 100 + + i.object_setattr("attr", 101) + assert i.attr == 101 + + object.__setattr__(i, "attr", 102) + assert i.attr == 102 + + with assertRaises(AttributeError): + i.super_setattr("not_attr", 100) + + with assertRaises(AttributeError): + i.object_setattr("not_attr", 101) + + with assertRaises(AttributeError): + object.__setattr__(i, "not_attr", 102) + +def test_no_setattr_nonnative() -> None: + i = NoSetAttrNonNative(99) + i.super_setattr("attr", 100) + assert i.attr == 100 + + i.object_setattr("attr", 101) + assert i.attr == 101 + + object.__setattr__(i, "attr", 102) + assert i.attr == 102 + + i.super_setattr("one", 100) + assert i.one == 100 + + i.object_setattr("two", 101) + assert i.two == 101 + + object.__setattr__(i, "three", 102) + assert i.three == 102 + + del i.three + assert i.three == None + + delattr(i, "two") + assert i.two == None + + object.__delattr__(i, "one") + assert i.one == None + +test_setattr() +test_setattr_inherited() +test_setattr_overridden() +test_setattr_nonnative() +test_no_setattr() +test_no_setattr_nonnative() + +[typing fixtures/typing-full.pyi] + +[case testDelAttrWithDeletableAttr] +from testutil import assertRaises + +class DelAttr: + __deletable__ = ["del_counter"] + + _attributes: dict[str, object] + del_counter: int = 0 + + def __init__(self) -> None: + object.__setattr__(self, "_attributes", {}) + + def __setattr__(self, key: str, val: object) -> None: + if key == "del_counter": + object.__setattr__(self, "del_counter", val) + else: + self._attributes[key] = val + + def __delattr__(self, key: str) -> None: + if key == "del_counter": + self.del_counter += 1 + else: + del self._attributes[key] + +def test_deletable_attr() -> None: + i = DelAttr() + assert i.del_counter == 0 + del i.del_counter + assert i.del_counter == 1 + +[case testDelAttrWithDeletableAttrInterpreted] +class DelAttr: + __deletable__ = ["del_counter"] + + _attributes: dict[str, object] + del_counter: int = 0 + + def __init__(self) -> None: + object.__setattr__(self, "_attributes", {}) + + def __setattr__(self, key: str, val: object) -> None: + if key == "del_counter": + object.__setattr__(self, "del_counter", val) + else: + self._attributes[key] = val + + def __delattr__(self, key: str) -> None: + if key == "del_counter": + self.del_counter += 1 + else: + del self._attributes[key] + +[file driver.py] +from native import DelAttr +from testutil import assertRaises + +def test_deletable_attr() -> None: + i = DelAttr() + assert i.del_counter == 0 + del i.del_counter + assert i.del_counter == 1 + +test_deletable_attr() + +[case testBufferCorruptedData_librt_internal] +from librt.internal import ( + ReadBuffer, read_bool, read_str, read_float, read_int, read_tag, read_bytes +) +from random import randbytes + +def check(data: bytes) -> None: + b = ReadBuffer(data) + try: + while True: + read_bool(b) + except ValueError: + pass + b = ReadBuffer(data) + read_tag(b) # Always succeeds + try: + while True: + read_int(b) + except ValueError: + pass + b = ReadBuffer(data) + try: + while True: + read_str(b) + except ValueError: + pass + b = ReadBuffer(data) + try: + while True: + read_bytes(b) + except ValueError: + pass + b = ReadBuffer(data) + try: + while True: + read_float(b) + except ValueError: + pass + +import time + +def test_read_corrupted_data() -> None: + # Test various deterministic byte sequences (1 to 4 bytes). + t0 = time.time() + for a in range(256): + check(bytes([a])) + for a in range(256): + for b in range(256): + check(bytes([a, b])) + for a in range(32): + for b in range(48): + for c in range(48): + check(bytes([a, b, c])) + for a in range(32): + for b in (0, 5, 17, 34): + for c in (0, 5, 17, 34): + for d in (0, 5, 17, 34): + check(bytes([a, b, c, d])) + # Also test some random data. + for i in range(20000): + data = randbytes(16) + try: + check(data) + except BaseException as e: + print("RANDOMIZED TEST FAILURE -- please open an issue with the following context:") + print(">>>", e, data) + raise diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test index d4f5b945309e5..2b75b32c906e0 100644 --- a/mypyc/test-data/run-dicts.test +++ b/mypyc/test-data/run-dicts.test @@ -95,8 +95,7 @@ assert get_content_set(od) == ({1, 3}, {2, 4}, {(1, 2), (3, 4)}) [typing fixtures/typing-full.pyi] [case testDictIterationMethodsRun] -from typing import Dict, Union -from typing_extensions import TypedDict +from typing import Dict, TypedDict, Union class ExtensionDict(TypedDict): python: str @@ -188,6 +187,7 @@ except TypeError as e: assert str(e) == "a tuple of length 2 expected" else: assert False +[typing fixtures/typing-full.pyi] [out] 1 3 @@ -336,3 +336,35 @@ def test_dict_to_bool() -> None: for x in tmp_list: assert is_true(x) assert not is_false(x) + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance({}, dict) + assert isinstance({'one': 1, 'two': 2}, dict) + assert isinstance({1: 1, 'two': 2}, dict) + assert isinstance(subc(), dict) + assert isinstance(subc({'a': 1, 'b': 2}), dict) + assert isinstance(subc({1: 'a', 2: 'b'}), dict) + + assert not isinstance(set(), dict) + assert not isinstance((), dict) + assert not isinstance((1,2,3), dict) + assert not isinstance({'a','b'}, dict) + assert not isinstance(int() + 1, dict) + assert not isinstance(str() + 'a', dict) + +def test_user_defined() -> None: + from userdefineddict import dict + + assert isinstance(dict(), dict) + assert not isinstance({1: dict()}, dict) + +[file copysubclass.py] +from typing import Any +class subc(dict[Any, Any]): + pass + +[file userdefineddict.py] +class dict: + pass diff --git a/mypyc/test-data/run-dunders-special.test b/mypyc/test-data/run-dunders-special.test new file mode 100644 index 0000000000000..4817435b1e7ce --- /dev/null +++ b/mypyc/test-data/run-dunders-special.test @@ -0,0 +1,12 @@ +[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 overridden by the user. +class UsesNotImplemented: + def __eq__(self, b: object) -> bool: + return NotImplemented + +def test_not_implemented() -> None: + assert UsesNotImplemented() != object() + x = UsesNotImplemented() == object() + assert not x diff --git a/mypyc/test-data/run-dunders.test b/mypyc/test-data/run-dunders.test index 2845187de2c31..a3ec06763d75b 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 @@ -191,7 +185,7 @@ class SeqError: def __contains__(self, x: int) -> bool: raise RuntimeError() - def __len__(self): + def __len__(self) -> int: return -5 def any_seq_error() -> Any: @@ -551,6 +545,7 @@ def test_type_mismatch_fall_back_to_reverse() -> None: assert F()**G() == -6 [case testDundersBinaryNotImplemented] +# mypy: allow-untyped-defs from typing import Any, Union from testutil import assertRaises @@ -623,15 +618,28 @@ def test_unannotated_add() -> None: with assertRaises(TypeError, "unsupported operand type(s) for +: 'F' and 'str'"): o + 'x' + o2: Any = F(4) + assert o2 + 5 == 9 + with assertRaises(TypeError, "unsupported operand type(s) for +: 'F' and 'str'"): + o2 + 'x' + def test_unannotated_add_and_radd_1() -> None: o = F(4) assert o + G() == 5 + o2: Any = F(4) + assert o2 + G() == 5 + def test_unannotated_radd() -> None: assert 'x' + G() == 'a' with assertRaises(TypeError, "unsupported operand type(s) for +: 'int' and 'G'"): 1 + G() + o: Any = G() + assert 'x' + o == 'a' + with assertRaises(TypeError, "unsupported operand type(s) for +: 'int' and 'G'"): + 1 + o + class H: def __add__(self, x): if isinstance(x, int): @@ -650,40 +658,48 @@ def test_unannotated_add_and_radd_2() -> None: with assertRaises(TypeError, "unsupported operand type(s) for +: 'int' and 'H'"): 1 + h + h2: Any = H() + assert h + 5 == 6 + assert 'x' + h == 22 + with assertRaises(TypeError, "unsupported operand type(s) for +: 'int' and 'H'"): + 1 + h + # TODO: Inheritance [case testDifferentReverseDunders] +from typing import Any + class C: # __radd__ and __rsub__ are tested elsewhere - def __rmul__(self, x): + def __rmul__(self, x: Any) -> int: return 1 - def __rtruediv__(self, x): + def __rtruediv__(self, x: Any) -> int: return 2 - def __rmod__(self, x): + def __rmod__(self, x: Any) -> int: return 3 - def __rfloordiv__(self, x): + def __rfloordiv__(self, x: Any) -> int: return 4 - def __rlshift__(self, x): + def __rlshift__(self, x: Any) -> int: return 5 - def __rrshift__(self, x): + def __rrshift__(self, x: Any) -> int: return 6 - def __rand__(self, x): + def __rand__(self, x: Any) -> int: return 7 - def __ror__(self, x): + def __ror__(self, x: Any) -> int: return 8 - def __rxor__(self, x): + def __rxor__(self, x: Any) -> int: return 9 - def __rmatmul__(self, x): + def __rmatmul__(self, x: Any) -> int: return 10 def test_reverse_dunders() -> None: @@ -809,10 +825,10 @@ def test_error() -> None: c += 'x' class BadInplaceAdd: - def __init__(self): + def __init__(self) -> None: self.x = 0 - def __iadd__(self, x): + def __iadd__(self, x: int) -> Any: self.x += x def test_in_place_operator_returns_none() -> None: @@ -943,3 +959,53 @@ 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 + +[case testDundersEq] +class Eq: + def __init__(self, x: int) -> None: + self.x = x + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Eq): + return NotImplemented + return self.x == other.x + +def eq(x: Eq, y: Eq) -> bool: + return x == y + +def ne(x: Eq, y: Eq) -> bool: + return x != y + +def test_equality_with_implicit_ne() -> None: + assert eq(Eq(1), Eq(1)) + assert not eq(Eq(1), Eq(2)) + assert ne(Eq(1), Eq(2)) + assert not ne(Eq(1), Eq(1)) diff --git a/mypyc/test-data/run-floats.test b/mypyc/test-data/run-floats.test index 2c101100549d5..424d52cdb0d55 100644 --- a/mypyc/test-data/run-floats.test +++ b/mypyc/test-data/run-floats.test @@ -2,8 +2,7 @@ [case testFloatOps] from __future__ import annotations -from typing import Any, cast -from typing_extensions import Final +from typing import Final, Any, cast from testutil import assertRaises, float_vals, FLOAT_MAGIC import math @@ -348,8 +347,7 @@ def test_tuples() -> None: assert t2 == tuple([5.0, 1.5, -7.0, -113.0]) [case testFloatGlueMethodsAndInheritance] -from typing import Any -from typing_extensions import Final +from typing import Final, Any from mypy_extensions import trait @@ -514,3 +512,34 @@ def test_implement_trait_attribute() -> None: a.y = 8.0 assert a.x == 7 assert a.y == 8 + +[case testIsInstance] +from copysubclass import subc +from testutil import float_vals +from typing import Any +def test_built_in() -> None: + for f in float_vals: + assert isinstance(float(0) + f, float) + assert isinstance(subc(f), float) + + assert not isinstance(set(), float) + assert not isinstance((), float) + assert not isinstance((1.0, 2.0), float) + assert not isinstance({3.14}, float) + assert not isinstance(int() + 1, float) + assert not isinstance(str() + '4.2', float) + +def test_user_defined() -> None: + from userdefinedfloat import float + + f: Any = 3.14 + assert isinstance(float(), float) + assert not isinstance(f, float) + +[file copysubclass.py] +class subc(float): + pass + +[file userdefinedfloat.py] +class float: + pass diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index cf519f30dad83..9bc5bb05c8d6a 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -140,7 +140,7 @@ def triple(a: int) -> Callable[[], Callable[[int], int]]: return outer def if_else(flag: int) -> str: - def dummy_funtion() -> str: + def dummy_function() -> str: return 'if_else.dummy_function' if flag < 0: @@ -155,7 +155,7 @@ def if_else(flag: int) -> str: return inner() def for_loop() -> int: - def dummy_funtion() -> str: + def dummy_function() -> str: return 'for_loop.dummy_function' for i in range(5): @@ -166,7 +166,7 @@ def for_loop() -> int: return 0 def while_loop() -> int: - def dummy_funtion() -> str: + def dummy_function() -> str: return 'while_loop.dummy_function' i = 0 @@ -1235,15 +1235,14 @@ from contextlib import contextmanager def f() -> Iterator[None]: yield -def g() -> None: +def test_special_case() -> None: a = [''] with f(): a.pop() -g() - [case testUnpackKwargsCompiled] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -1252,8 +1251,10 @@ class Person(TypedDict): def foo(**kwargs: Unpack[Person]) -> None: print(kwargs["name"]) -# This is not really supported yet, just test that we behave reasonably. -foo(name='Jennifer', age=38) +def test_unpack() -> None: + # This is not really supported yet, just test that we behave reasonably. + foo(name='Jennifer', age=38) +[typing fixtures/typing-full.pyi] [out] Jennifer @@ -1267,8 +1268,9 @@ def foo() -> None: print(inner.__dict__) # type: ignore[attr-defined] print(inner.x) # type: ignore[attr-defined] -if sys.version_info >= (3, 12): # type: ignore - foo() +def test_nested() -> None: + if sys.version_info >= (3, 12): # type: ignore + foo() [out] [out version>=3.12] {} @@ -1283,7 +1285,9 @@ def bar() -> None: functools.update_wrapper(inner, bar) # type: ignore print(inner.__dict__) # type: ignore -bar() +def test_update() -> None: + bar() +[typing fixtures/typing-full.pyi] [out] {'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': } @@ -1308,3 +1312,29 @@ from native import f print(f(1)) [out] 2 + +[case testStarArgFastPaths] +from typing import Any, Mapping +def fn(x: str, y: int) -> str: + return x * y +def star_tuple(*args: Any) -> str: + return fn(*args) +def star_list(args: list[Any]) -> str: + return fn(*args) +def star_generic(args: dict[Any, Any]) -> str: + return fn(*args) +def star2(**kwargs: Any) -> str: + return fn(**kwargs) +def star2_generic(kwargs: Mapping[Any, Any]) -> str: + return fn(**kwargs) + +def test_star_fastpath_tuple() -> None: + assert star_tuple("a", 3) == "aaa" +def test_star_fastpath_list() -> None: + assert star_list(["a", 3]) == "aaa" +def test_star_fastpath_generic() -> None: + assert star_generic({"a": None, 3: None}) == "aaa" +def test_star2_fastpath() -> None: + assert star2(x="a", y=3) == "aaa" +def test_star2_fastpath_generic() -> None: + assert star2_generic({"x": "a", "y": 3}) == "aaa" diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test index 7e9804c49582a..cf1dac7c57333 100644 --- a/mypyc/test-data/run-generators.test +++ b/mypyc/test-data/run-generators.test @@ -190,7 +190,9 @@ exit! a exception! ((1,), 'exception!') [case testYieldNested] -from typing import Callable, Generator +from typing import Callable, Generator, Iterator, TypeVar, overload + +from testutil import run_generator def normal(a: int, b: float) -> Callable: def generator(x: int, y: str) -> Generator: @@ -235,15 +237,52 @@ def outer() -> Generator: yield i return recursive(10) -[file driver.py] -from native import normal, generator, triple, another_triple, outer -from testutil import run_generator +def test_return_nested_generator() -> None: + assert run_generator(normal(1, 2.0)(3, '4.00')) == ((1, 2.0, 3, '4.00'), None) + assert run_generator(generator(1)) == ((1, 2, 3), None) + assert run_generator(triple()()) == ((1, 2, 3), None) + assert run_generator(another_triple()()) == ((1,), None) + assert run_generator(outer()) == ((0, 1, 2, 3, 4), None) + +def call_nested(x: int) -> list[int]: + def generator() -> Iterator[int]: + n = int() + 2 + yield x + yield n * x + + a = [] + for x in generator(): + a.append(x) + return a + +T = TypeVar("T") + +def deco(f: T) -> T: + return f -assert run_generator(normal(1, 2.0)(3, '4.00')) == ((1, 2.0, 3, '4.00'), None) -assert run_generator(generator(1)) == ((1, 2, 3), None) -assert run_generator(triple()()) == ((1, 2, 3), None) -assert run_generator(another_triple()()) == ((1,), None) -assert run_generator(outer()) == ((0, 1, 2, 3, 4), None) +def call_nested_decorated(x: int) -> list[int]: + @deco + def generator() -> Iterator[int]: + n = int() + 3 + yield x + yield n * x + + a = [] + for x in generator(): + a.append(x) + return a + +def call_nested_recursive(x: int) -> Iterator: + def recursive(x: int) -> Iterator: + if x > 0: + yield from recursive(x - 1) + yield x + + yield from recursive(x) + +def test_call_nested_generator_in_function() -> None: + assert call_nested_decorated(5) == [5, 15] + assert list(call_nested_recursive(5)) == [0, 1, 2, 3, 4, 5] [case testYieldThrow] from typing import Generator, Iterable, Any, Union @@ -587,16 +626,22 @@ else: from typing import Iterator class Foo: - flag: bool + flag = False class C: - foo: Foo + foo = Foo() def genf(self) -> Iterator[None]: self.foo.flag = True yield self.foo.flag = False +def test_near_yield() -> None: + c = C() + for x in c.genf(): + pass + assert c.foo.flag == False + [case testGeneratorEarlyReturnWithBorrows] from typing import Iterator class Bar: @@ -609,6 +654,12 @@ class Foo: return yield 0 +def test_early_return() -> None: + foo = Foo() + for x in foo.f(): + pass + assert foo.bar.bar == 1 + [case testBorrowingInGeneratorInTupleAssignment] from typing import Iterator @@ -680,3 +731,216 @@ def test_basic() -> None: with context: assert context.x == 1 assert context.x == 0 + +[case testYieldSpill] +from typing import Generator +from testutil import run_generator + +def f() -> int: + return 1 + +def yield_spill() -> Generator[str, int, int]: + return f() + (yield "foo") + +def test_basic() -> None: + x = run_generator(yield_spill(), [2]) + yields, val = x + assert yields == ('foo',) + assert val == 3, val + +[case testGeneratorReuse] +from typing import Iterator, Any + +def gen(x: list[int]) -> Iterator[list[int]]: + y = [9] + for z in x: + yield y + [z] + yield y + +def gen_range(n: int) -> Iterator[int]: + for x in range(n): + yield x + +def test_use_generator_multiple_times_one_at_a_time() -> None: + for i in range(100): + a = [] + for x in gen([2, i]): + a.append(x) + assert a == [[9, 2], [9, i], [9]] + +def test_use_multiple_generator_instances_at_same_time() -> None: + a = [] + for x in gen([2]): + a.append(x) + for y in gen([3, 4]): + a.append(y) + assert a == [[9, 2], [9, 3], [9, 4], [9], [9], [9, 3], [9, 4], [9]] + +def test_use_multiple_generator_instances_at_same_time_2() -> None: + a = [] + for x in gen_range(2): + a.append(x) + b = [] + for y in gen_range(3): + b.append(y) + c = [] + for z in gen_range(4): + c.append(z) + assert c == [0, 1, 2, 3] + assert b == [0, 1, 2] + assert a == [0, 1] + assert list(gen_range(5)) == list(range(5)) + +def gen_a(x: int) -> Iterator[int]: + yield x + 1 + +def gen_b(x: int) -> Iterator[int]: + yield x + 2 + +def test_generator_identities() -> None: + # Sanity check: two distinct live objects can't reuse the same memory location + g1 = gen_a(1) + g2 = gen_a(1) + assert g1 is not g2 + + # If two generators have non-overlapping lifetimes, they should reuse a memory location + g3 = gen_b(1) + id1 = id(g3) + g3 = gen_b(1) + assert id(g3) == id1 + + # More complex case of reuse: allocate other objects in between + g4: Any = gen_a(1) + id2 = id(g4) + g4 = gen_b(1) + g4 = [gen_b(n) for n in range(100)] + g4 = gen_a(1) + assert id(g4) == id2 + +[case testGeneratorReuseWithGilDisabled] +import sys +import threading +from typing import Iterator + +def gen() -> Iterator[int]: + yield 1 + +def is_gil_disabled() -> bool: + return hasattr(sys, "_is_gil_enabled") and not sys._is_gil_enabled() + +def test_each_thread_gets_separate_instance() -> None: + if not is_gil_disabled(): + # This only makes sense if GIL is disabled + return + + g = gen() + id1 = id(g) + + id2 = 0 + + def run() -> None: + nonlocal id2 + g = gen() + id2 = id(g) + + t = threading.Thread(target=run) + t.start() + t.join() + + # Each thread should get a separate reused instance + assert id1 != id2 + +[case testGeneratorWithUndefinedLocalInEnvironment] +from typing import Iterator + +from testutil import assertRaises + +def gen(set: bool) -> Iterator[float]: + if set: + y = float("-113.0") + yield 1.0 + yield y + +def test_bitmap_is_cleared_when_object_is_reused() -> None: + # This updates the bitmap of the shared instance. + list(gen(True)) + + # Ensure bitmap has been cleared. + with assertRaises(AttributeError): # TODO: Should be UnboundLocalError + list(gen(False)) + +def gen2(set: bool) -> Iterator[int]: + if set: + y = int("5") + yield 1 + yield y + +def test_undefined_int_in_environment() -> None: + list(gen2(True)) + + with assertRaises(AttributeError): # TODO: Should be UnboundLocalError + list(gen2(False)) + +[case testVariableWithSameNameAsHelperMethod] +from testutil import assertRaises +from typing import Iterator + +def gen_send() -> Iterator[int]: + send = 1 + yield send + 1 + +def gen_throw() -> Iterator[int]: + throw = 42 + yield throw * 2 + +def undefined() -> Iterator[int]: + if int(): + send = 1 + yield send + 1 + +def test_same_names() -> None: + assert list(gen_send()) == [2] + assert list(gen_throw()) == [84] + + with assertRaises(AttributeError, "attribute 'send' of 'undefined_gen' undefined"): + # TODO: Should be UnboundLocalError, this test verifies that the attribute name + # matches the variable name in the input code, since internally it's generated + # with a prefix. + list(undefined()) + +[case testGeneratorInheritance] +from typing import Iterator + +class Base1: + def foo(self) -> Iterator[int]: + yield 1 + +class Derived1(Base1): + def foo(self) -> Iterator[int]: + yield 2 + yield 3 + +def base1_foo(b: Base1) -> list[int]: + a = [] + for x in b.foo(): + a.append(x) + return a + +def derived1_foo(b: Derived1) -> list[int]: + a = [] + for x in b.foo(): + a.append(x) + return a + +def test_generator_override() -> None: + assert base1_foo(Base1()) == [1] + assert base1_foo(Derived1()) == [2, 3] + assert derived1_foo(Derived1()) == [2, 3] + +[case testGeneratorEmptyTuple] +from collections.abc import Generator +from typing import Optional, Union + +def test_compiledGeneratorEmptyTuple() -> None: + jobs: Generator[Optional[str], None, None] = (_ for _ in ()) + assert list(jobs) == [] diff --git a/mypyc/test-data/run-generics.test b/mypyc/test-data/run-generics.test new file mode 100644 index 0000000000000..55e5adbbb4f9a --- /dev/null +++ b/mypyc/test-data/run-generics.test @@ -0,0 +1,113 @@ +[case testTypeVarMappingBound] +# Dicts are special-cased for efficient iteration. +from typing import Dict, TypedDict, TypeVar, Union + +class TD(TypedDict): + foo: int + +M = TypeVar("M", bound=Dict[str, int]) +U = TypeVar("U", bound=Union[Dict[str, int], Dict[str, str]]) +T = TypeVar("T", bound=TD) + +def fn_mapping(m: M) -> None: + print([x for x in m]) + print([x for x in m.values()]) + print([x for x in m.keys()]) + print({k: v for k, v in m.items()}) + +def fn_union(m: U) -> None: + print([x for x in m]) + print([x for x in m.values()]) + print([x for x in m.keys()]) + print({k: v for k, v in m.items()}) + +def fn_typeddict(t: T) -> None: + print([x for x in t]) + print([x for x in t.values()]) + print([x for x in t.keys()]) + print({k: v for k, v in t.items()}) + +def test_mapping() -> None: + fn_mapping({}) + print("=====") + fn_mapping({"a": 1, "b": 2}) + print("=====") + + fn_union({"a": 1, "b": 2}) + print("=====") + fn_union({"a": "1", "b": "2"}) + print("=====") + + orig: Union[Dict[str, int], Dict[str, str]] = {"a": 1, "b": 2} + fn_union(orig) + print("=====") + + td: TD = {"foo": 1} + fn_typeddict(td) +[typing fixtures/typing-full.pyi] +[out] +\[] +\[] +\[] +{} +===== +\['a', 'b'] +\[1, 2] +\['a', 'b'] +{'a': 1, 'b': 2} +===== +\['a', 'b'] +\[1, 2] +\['a', 'b'] +{'a': 1, 'b': 2} +===== +\['a', 'b'] +\['1', '2'] +\['a', 'b'] +{'a': '1', 'b': '2'} +===== +\['a', 'b'] +\[1, 2] +\['a', 'b'] +{'a': 1, 'b': 2} +===== +\['foo'] +\[1] +\['foo'] +{'foo': 1} + +[case testParamSpecComponentsAreUsable] +from typing import Callable +from typing_extensions import ParamSpec + +P = ParamSpec("P") + +def deco(func: Callable[P, int]) -> Callable[P, int]: + def inner(*args: P.args, **kwargs: P.kwargs) -> int: + print([x for x in args]) + print({k: v for k, v in kwargs.items()}) + print(list(kwargs)) + print(list(kwargs.keys())) + print(list(kwargs.values())) + return func(*args, **kwargs) + + return inner + +@deco +def f(x: int, y: str) -> int: + return x + +def test_usable() -> None: + assert f(1, 'a') == 1 + assert f(2, y='b') == 2 +[out] +\[1, 'a'] +{} +\[] +\[] +\[] +\[2] +{'y': 'b'} +\['y'] +\['y'] +\['b'] diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index 1a82ac3e2dd12..0dcad465cc9a0 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -517,11 +517,10 @@ def test_isinstance() -> None: assert narrow2("foobar") == 6 [case testI64ErrorValuesAndUndefined] -from typing import Any, Tuple +from typing import Any, Final, Tuple import sys from mypy_extensions import mypyc_attr, i64 -from typing_extensions import Final from testutil import assertRaises @@ -905,8 +904,7 @@ def test_undefined_native_int_tuple_via_any() -> None: assert o.t == (-13, 45) [case testI64DefaultArgValues] -from typing import Any, Iterator, Tuple -from typing_extensions import Final +from typing import Any, Final, Iterator, Tuple MAGIC: Final = -113 @@ -1206,7 +1204,7 @@ def test_magic_default() -> None: assert a(MAGIC) == MAGIC [case testI64UndefinedLocal] -from typing_extensions import Final +from typing import Final from mypy_extensions import i64, i32 @@ -1307,28 +1305,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 @@ -1338,8 +1336,7 @@ def test_many_locals() -> None: assert a33 == 20 [case testI64GlueMethodsAndInheritance] -from typing import Any -from typing_extensions import Final +from typing import Final, Any from mypy_extensions import i64, trait diff --git a/mypyc/test-data/run-imports.test b/mypyc/test-data/run-imports.test index c5839d57820e1..ce83a882e2ded 100644 --- a/mypyc/test-data/run-imports.test +++ b/mypyc/test-data/run-imports.test @@ -212,9 +212,10 @@ import shared def do_import() -> None: import a -assert shared.counter == 0 -do_import() -assert shared.counter == 1 +def test_lazy() -> None: + assert shared.counter == 0 + do_import() + assert shared.counter == 1 [file a.py] import shared @@ -224,9 +225,10 @@ shared.counter += 1 counter = 0 [case testDelayedImport] -import a -print("inbetween") -import b +def test_delayed() -> None: + import a + print("inbetween") + import b [file a.py] print("first") @@ -240,19 +242,21 @@ inbetween last [case testImportErrorLineNumber] -try: - import enum - import dataclasses, missing # type: ignore[import] -except ImportError as e: - line = e.__traceback__.tb_lineno # type: ignore[attr-defined] - assert line == 3, f"traceback's line number is {line}, expected 3" +def test_error() -> None: + try: + import enum + import dataclasses, missing # type: ignore[import] + except ImportError as e: + line = e.__traceback__.tb_lineno # type: ignore[attr-defined] + assert line == 4, f"traceback's line number is {line}, expected 4" [case testImportGroupIsolation] def func() -> None: import second -import first -func() +def test_isolation() -> None: + import first + func() [file first.py] print("first") diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test index d575e141b5671..c02f7d808883c 100644 --- a/mypyc/test-data/run-integers.test +++ b/mypyc/test-data/run-integers.test @@ -538,3 +538,51 @@ def test_int_bool_min_max() -> None: assert min(u, z) == -10 assert max(u, y) == False assert max(u, z) == True + +[case testIsInstance] +from copysubclass import subc +from typing import Any +def test_built_in() -> None: + i: Any = 0 + assert isinstance(i + 0, int) + assert isinstance(i + 9223372036854775808, int) + assert isinstance(i + -9223372036854775808, int) + assert isinstance(subc(), int) + assert isinstance(subc(9223372036854775808), int) + assert isinstance(subc(-9223372036854775808), int) + + assert not isinstance(set(), int) + assert not isinstance((), int) + assert not isinstance((1,2,3), int) + assert not isinstance({1,2}, int) + assert not isinstance(float(0) + 1.0, int) + assert not isinstance(str() + '1', int) + +def test_user_defined() -> None: + from userdefinedint import int + + i: Any = 42 + assert isinstance(int(), int) + assert not isinstance(i, int) + +[file copysubclass.py] +class subc(int): + pass + +[file userdefinedint.py] +class int: + pass + +[case testBitLength] +def bit_length(n: int) -> int: + return n.bit_length() +def bit_length_python(n: int) -> int: + return getattr(n, "bit_length")() +def test_bit_length() -> None: + for n in range(256): + i = 1 << n + assert bit_length(i) == bit_length_python(i) + assert bit_length(-(i)) == bit_length_python(-(i)) + i -= 1 + assert bit_length(i) == bit_length_python(i) + assert bit_length(-(i)) == bit_length_python(-(i)) diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test index 84d5ee121a200..40ca1b6e005f9 100644 --- a/mypyc/test-data/run-lists.test +++ b/mypyc/test-data/run-lists.test @@ -51,6 +51,81 @@ print(2, a) 1 [-1, 5] 2 [340282366920938463463374607431768211461, -170141183460469231731687303715884105736] +[case testListClear] +from typing import List, Any +from copysubclass import subc + +def test_list_clear() -> None: + l1 = [1, 2, 3, -4, 5] + l1.clear() + assert l1 == [] + l1.clear() + assert l1 == [] + l2: List[Any] = [] + l2.clear() + assert l2 == [] + l3 = [1, 2, 3, "abcdef"] + l3.clear() + assert l3 == [] + # subclass testing + l4: subc = subc([1, 2, 3]) + l4.clear() + assert l4 == [] + +[file copysubclass.py] +from typing import Any +class subc(list[Any]): + pass + +[case testListCopy] +from typing import List +from copysubclass import subc + +def test_list_copy() -> None: + l1 = [1, 2, 3, -4, 5] + l2 = l1.copy() + assert l1.copy() == l1 + assert l1.copy() == l2 + assert l1 == l2 + assert l1.copy() == l2.copy() + l1 = l2.copy() + assert l1 == l2 + assert l1.copy() == l2 + assert l1 == [1, 2, 3, -4, 5] + l2 = [1, 2, -3] + l1 = [] + assert l1.copy() == [] + assert l2.copy() != l1 + assert l2 == l2.copy() + l1 = l2 + assert l1.copy().copy() == l2.copy().copy().copy() + assert l1.copy() == l2.copy() + l1 == [1, 2, -3].copy() + assert l1 == l2 + l2 = [1, 2, 3].copy() + assert l2 != l1 + l1 = [1, 2, 3] + assert l1.copy() == l2.copy() + l3 = [1, 2 , 3, "abcdef"] + assert l3 == l3.copy() + l4 = ["abc", 5, 10] + l4 = l3.copy() + assert l4 == l3 + #subclass testing + l5: subc = subc([1, 2, 3]) + l6 = l5.copy() + assert l6 == l5 + l6 = [1, 2, "3", 4, 5] + l5 = subc([1,2,"3",4,5]) + assert l5.copy() == l6.copy() + l6 = l5.copy() + assert l5 == l6 + +[file copysubclass.py] +from typing import Any +class subc(list[Any]): + pass + [case testSieve] from typing import List @@ -75,7 +150,9 @@ print(primes(13)) \[0, 0, 1, 1] \[0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1] -[case testListBuild] +[case testListPrimitives] +from testutil import assertRaises + def test_list_build() -> None: # Currently LIST_BUILDING_EXPANSION_THRESHOLD equals to 10 # long list built by list_build_op @@ -94,9 +171,6 @@ def test_list_build() -> None: l3.append('a') assert l3 == ['a'] -[case testListPrims] -from typing import List - def test_append() -> None: l = [1, 2] l.append(10) @@ -114,10 +188,28 @@ def test_pop_last() -> None: def test_pop_index() -> None: l = [1, 2, 10, 3] - l.pop(2) + assert l.pop(2) == 10 assert l == [1, 2, 3] - l.pop(-2) + assert l.pop(-2) == 2 assert l == [1, 3] + assert l.pop(-2) == 1 + assert l.pop(0) == 3 + assert l == [] + l = [int() + 1000, int() + 1001, int() + 1002] + assert l.pop(0) == 1000 + assert l.pop(-1) == 1002 + assert l == [1001] + +def test_pop_index_errors() -> None: + l = [int() + 1000] + with assertRaises(IndexError): + l.pop(1) + with assertRaises(IndexError): + l.pop(-2) + with assertRaises(OverflowError): + l.pop(1 << 100) + with assertRaises(OverflowError): + l.pop(-(1 << 100)) def test_count() -> None: l = [1, 3] @@ -218,6 +310,9 @@ print(g()) 7 [case testListOps] +from typing import Any, cast +from testutil import assertRaises + def test_slicing() -> None: # Use dummy adds to avoid constant folding zero = int() @@ -240,8 +335,35 @@ def test_slicing() -> None: assert s[long_int:] == [] assert s[-long_int:-1] == ["f", "o", "o", "b", "a"] -[case testOperatorInExpression] +def in_place_add(l2: Any) -> list[Any]: + l1 = [1, 2] + l1 += l2 + return l1 + +def test_add() -> None: + res = [1, 2, 3, 4] + assert [1, 2] + [3, 4] == res + with assertRaises(TypeError, 'can only concatenate list (not "tuple") to list'): + assert [1, 2] + cast(Any, (3, 4)) == res + l1 = [1, 2] + id_l1 = id(l1) + l1 += [3, 4] + assert l1 == res + assert id_l1 == id(l1) + assert in_place_add([3, 4]) == res + assert in_place_add((3, 4)) == res + assert in_place_add({3, 4}) == res + assert in_place_add({3: "", 4: ""}) == res + assert in_place_add(range(3, 5)) == res + +def test_multiply() -> None: + l1 = [1] + assert l1 * 3 == [1, 1, 1] + assert 3 * l1 == [1, 1, 1] + l1 *= 3 + assert l1 == [1, 1, 1] +[case testOperatorInExpression] def tuple_in_int0(i: int) -> bool: return i in [] @@ -293,74 +415,73 @@ def list_not_in_str(s: "str") -> bool: def list_in_mixed(i: object): return i in [[], (), "", 0, 0.0, False, 0j, {}, set(), type] -[file driver.py] - -from native import * - -assert not tuple_in_int0(0) -assert not tuple_in_int1(0) -assert tuple_in_int1(1) -assert not tuple_in_int3(0) -assert tuple_in_int3(1) -assert tuple_in_int3(2) -assert tuple_in_int3(3) -assert not tuple_in_int3(4) - -assert tuple_not_in_int0(0) -assert tuple_not_in_int1(0) -assert not tuple_not_in_int1(1) -assert tuple_not_in_int3(0) -assert not tuple_not_in_int3(1) -assert not tuple_not_in_int3(2) -assert not tuple_not_in_int3(3) -assert tuple_not_in_int3(4) - -assert tuple_in_str("foo") -assert tuple_in_str("bar") -assert tuple_in_str("baz") -assert not tuple_in_str("apple") -assert not tuple_in_str("pie") -assert not tuple_in_str("\0") -assert not tuple_in_str("") - -assert not list_in_int0(0) -assert not list_in_int1(0) -assert list_in_int1(1) -assert not list_in_int3(0) -assert list_in_int3(1) -assert list_in_int3(2) -assert list_in_int3(3) -assert not list_in_int3(4) - -assert list_not_in_int0(0) -assert list_not_in_int1(0) -assert not list_not_in_int1(1) -assert list_not_in_int3(0) -assert not list_not_in_int3(1) -assert not list_not_in_int3(2) -assert not list_not_in_int3(3) -assert list_not_in_int3(4) - -assert list_in_str("foo") -assert list_in_str("bar") -assert list_in_str("baz") -assert not list_in_str("apple") -assert not list_in_str("pie") -assert not list_in_str("\0") -assert not list_in_str("") - -assert list_in_mixed(0) -assert list_in_mixed([]) -assert list_in_mixed({}) -assert list_in_mixed(()) -assert list_in_mixed(False) -assert list_in_mixed(0.0) -assert not list_in_mixed([1]) -assert not list_in_mixed(object) -assert list_in_mixed(type) +def test_in_operator_various_cases() -> None: + assert not tuple_in_int0(0) + assert not tuple_in_int1(0) + assert tuple_in_int1(1) + assert not tuple_in_int3(0) + assert tuple_in_int3(1) + assert tuple_in_int3(2) + assert tuple_in_int3(3) + assert not tuple_in_int3(4) + + assert tuple_not_in_int0(0) + assert tuple_not_in_int1(0) + assert not tuple_not_in_int1(1) + assert tuple_not_in_int3(0) + assert not tuple_not_in_int3(1) + assert not tuple_not_in_int3(2) + assert not tuple_not_in_int3(3) + assert tuple_not_in_int3(4) + + assert tuple_in_str("foo") + assert tuple_in_str("bar") + assert tuple_in_str("baz") + assert not tuple_in_str("apple") + assert not tuple_in_str("pie") + assert not tuple_in_str("\0") + assert not tuple_in_str("") + + assert not list_in_int0(0) + assert not list_in_int1(0) + assert list_in_int1(1) + assert not list_in_int3(0) + assert list_in_int3(1) + assert list_in_int3(2) + assert list_in_int3(3) + assert not list_in_int3(4) + + assert list_not_in_int0(0) + assert list_not_in_int1(0) + assert not list_not_in_int1(1) + assert list_not_in_int3(0) + assert not list_not_in_int3(1) + assert not list_not_in_int3(2) + assert not list_not_in_int3(3) + assert list_not_in_int3(4) + + assert list_in_str("foo") + assert list_in_str("bar") + assert list_in_str("baz") + assert not list_in_str("apple") + assert not list_in_str("pie") + assert not list_in_str("\0") + assert not list_in_str("") + + assert list_in_mixed(0) + assert list_in_mixed([]) + assert list_in_mixed({}) + assert list_in_mixed(()) + assert list_in_mixed(False) + assert list_in_mixed(0.0) + assert not list_in_mixed([1]) + assert not list_in_mixed(object) + assert list_in_mixed(type) [case testListBuiltFromGenerator] -def test() -> None: +from typing import Final +abc: Final = "abc" +def test_from_gen() -> None: source_a = ["a", "b", "c"] a = list(x + "f2" for x in source_a) assert a == ["af2", "bf2", "cf2"] @@ -379,12 +500,21 @@ def test() -> None: source_str = "abcd" f = list("str:" + x for x in source_str) assert f == ["str:a", "str:b", "str:c", "str:d"] +def test_known_length() -> None: + # not built from generator but doesnt need its own test either + built = [str(x) for x in [*abc, *"def", *b"ghi", ("j", "k"), *("l", "m", "n")]] + assert built == ['a', 'b', 'c', 'd', 'e', 'f', '103', '104', '105', "('j', 'k')", 'l', 'm', 'n'] + +[case testNext] +from typing import List -[case testNextBug] -from typing import List, Optional +def get_next(x: List[int]) -> int: + return next((i for i in x), -1) -def test(x: List[int]) -> None: - res = next((i for i in x), None) +def test_next() -> None: + assert get_next([]) == -1 + assert get_next([1]) == 1 + assert get_next([3,2,1]) == 3 [case testListGetItemWithBorrow] from typing import List @@ -409,3 +539,57 @@ def test_index_with_literal() -> None: assert d is d2 d = a[-2].d assert d is d1 + +[case testSorted] +from typing import List + +def test_list_sort() -> None: + l1 = [2, 1, 3] + id_l1 = id(l1) + l1.sort() + assert l1 == [1, 2, 3] + assert id_l1 == id(l1) + +def test_sorted() -> None: + res = [1, 2, 3] + l1 = [2, 1, 3] + id_l1 = id(l1) + s_l1 = sorted(l1) + assert s_l1 == res + assert id_l1 != id(s_l1) + assert l1 == [2, 1, 3] + assert sorted((2, 1, 3)) == res + assert sorted({2, 1, 3}) == res + assert sorted({2: "", 1: "", 3: ""}) == res + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance([], list) + assert isinstance([1,2,3], list) + assert isinstance(['a','b'], list) + assert isinstance(subc(), list) + assert isinstance(subc([1,2,3]), list) + assert isinstance(subc(['a','b']), list) + + assert not isinstance({}, list) + assert not isinstance((), list) + assert not isinstance((1,2,3), list) + assert not isinstance(('a','b'), list) + assert not isinstance(1, list) + assert not isinstance('a', list) + +def test_user_defined() -> None: + from userdefinedlist import list + + assert isinstance(list(), list) + assert not isinstance([list()], list) + +[file copysubclass.py] +from typing import Any +class subc(list[Any]): + pass + +[file userdefinedlist.py] +class list: + pass diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 95b79af1a4114..106c2271d3264 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -1,7 +1,7 @@ # Test cases for "range" objects, "for" and "while" loops (compile and run) [case testFor] -from typing import List, Tuple +from typing import Any, List, Tuple def count(n: int) -> None: for i in range(n): print(i) @@ -21,6 +21,10 @@ def list_iter(l: List[int]) -> None: def tuple_iter(l: Tuple[int, ...]) -> None: for i in l: print(i) +def empty_tuple_iter(l: Tuple[()]) -> None: + i: Any + for i in l: + print(i) def str_iter(l: str) -> None: for i in l: print(i) @@ -39,7 +43,7 @@ def count_down_short() -> None: [file driver.py] from native import ( count, list_iter, list_rev_iter, list_rev_iter_lol, count_between, count_down, count_double, - count_down_short, tuple_iter, str_iter, + count_down_short, tuple_iter, empty_tuple_iter, str_iter, ) count(5) list_iter(list(reversed(range(5)))) @@ -52,6 +56,7 @@ count_down_short() print('==') list_rev_iter_lol(list(reversed(range(5)))) tuple_iter((1, 2, 3)) +empty_tuple_iter(()) str_iter("abc") [out] 0 @@ -228,6 +233,7 @@ def nested_enumerate() -> None: assert i == inner inner += 1 outer += 1 + assert i == 2 assert outer_seen == l1 def nested_range() -> None: @@ -465,6 +471,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 @@ -521,3 +550,29 @@ def test_range_object() -> None: r4 = range(4, 12, 0) except ValueError as e: assert "range() arg 3 must not be zero" in str(e) + +[case testNamedTupleLoop] +from collections.abc import Iterable +from typing import NamedTuple, Any +from typing_extensions import Self + + +class Vector2(NamedTuple): + x: int + y: float + + @classmethod + def from_iter(cls, iterable: Iterable[Any]) -> Self: + return cls(*iter(iterable)) + + def __neg__(self) -> Self: + return self.from_iter(-c for c in self) + +[file driver.py] +import native +print(-native.Vector2(2, -3.1)) +print([x for x in native.Vector2(4, -5.2)]) + +[out] +Vector2(x=-2, y=3.1) +\[4, -5.2] diff --git a/mypyc/test-data/run-math.test b/mypyc/test-data/run-math.test index 266b4851575f3..d3102290d2afc 100644 --- a/mypyc/test-data/run-math.test +++ b/mypyc/test-data/run-math.test @@ -1,8 +1,7 @@ # Test cases for the math module (compile and run) [case testMathOps] -from typing import Any, Callable -from typing_extensions import Final +from typing import Any, Callable, Final import math from math import pi, e, tau, inf, nan from testutil import assertRaises, float_vals, assertDomainError, assertMathRangeError diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index f07ac51dae6ce..1074906357eed 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 @@ -109,10 +109,11 @@ def gen(b: bool) -> Generator[Any, None, None]: y = None yield y -assert f(False) == ((1, None), (None, 1)) -assert f(True) == ((None, 1), (1, None)) -assert next(gen(False)) is None -assert next(gen(True)) == 1 +def test_inferred() -> None: + assert f(False) == ((1, None), (None, 1)) + assert f(True) == ((None, 1), (1, None)) + assert next(gen(False)) is None + assert next(gen(True)) == 1 [case testWith] from typing import Any @@ -612,8 +613,7 @@ for a in sorted(s): 9 8 72 [case testDummyTypes] -from typing import Tuple, List, Dict, NamedTuple -from typing_extensions import Literal, TypedDict, NewType +from typing import Tuple, List, Dict, Literal, NamedTuple, NewType, TypedDict class A: pass @@ -664,6 +664,7 @@ except Exception as e: print(type(e).__name__) # ... but not that it is a valid literal value take_literal(10) +[typing fixtures/typing-full.pyi] [out] Lol(a=1, b=[]) 10 @@ -675,7 +676,7 @@ TypeError 10 [case testClassBasedTypedDict] -from typing_extensions import TypedDict +from typing import TypedDict class TD(TypedDict): a: int @@ -707,6 +708,7 @@ def test_non_total_typed_dict() -> None: d4 = TD4(a=1, b=2, c=3, d=4) assert d3['c'] == 3 assert d4['d'] == 4 +[typing fixtures/typing-full.pyi] [case testClassBasedNamedTuple] from typing import NamedTuple @@ -828,23 +830,23 @@ assert call_any_nested([[1, 1, 1], [1, 1], []]) == 1 assert call_any_nested([[1, 1, 1], [0, 1], []]) == 0 [case testSum] -[typing fixtures/typing-full.pyi] -from typing import Any, List +from typing import List +empty: List[int] = [] def test_sum_of_numbers() -> None: assert sum(x for x in [1, 2, 3]) == 6 - assert sum(x for x in [0.0, 1.2, 2]) == 6.2 + assert sum(x for x in [0.0, 1.2, 2]) == 3.2 assert sum(x for x in [1, 1j]) == 1 + 1j def test_sum_callables() -> None: - assert sum((lambda x: x == 0)(x) for x in []) == 0 + assert sum((lambda x: x == 0)(x) for x in empty) == 0 assert sum((lambda x: x == 0)(x) for x in [0]) == 1 assert sum((lambda x: x == 0)(x) for x in [0, 0, 0]) == 3 assert sum((lambda x: x == 0)(x) for x in [0, 1, 0]) == 2 assert sum((lambda x: x % 2 == 0)(x) for x in range(2**10)) == 2**9 def test_sum_comparisons() -> None: - assert sum(x == 0 for x in []) == 0 + assert sum(x == 0 for x in empty) == 0 assert sum(x == 0 for x in [0]) == 1 assert sum(x == 0 for x in [0, 0, 0]) == 3 assert sum(x == 0 for x in [0, 1, 0]) == 2 @@ -864,13 +866,14 @@ def test_sum_misc() -> None: def test_sum_start_given() -> None: a = 1 assert sum((x == 0 for x in [0, 1]), a) == 2 - assert sum(((lambda x: x == 0)(x) for x in []), 1) == 1 + assert sum(((lambda x: x == 0)(x) for x in empty), 1) == 1 assert sum(((lambda x: x == 0)(x) for x in [0]), 1) == 2 assert sum(((lambda x: x == 0)(x) for x in [0, 0, 0]), 1) == 4 assert sum(((lambda x: x == 0)(x) for x in [0, 1, 0]), 1) == 3 assert sum(((lambda x: x % 2 == 0)(x) for x in range(2**10)), 1) == 2**9 + 1 assert sum((x for x in [1, 1j]), 2j) == 1 + 3j assert sum((c == 'd' for c in 'abcdd'), 1) == 3 +[typing fixtures/typing-full.pyi] [case testNoneStuff] from typing import Optional @@ -968,7 +971,10 @@ print(z) [case testCheckVersion] import sys -if sys.version_info[:2] == (3, 13): +if sys.version_info[:2] == (3, 14): + def version() -> int: + return 14 +elif sys.version_info[:2] == (3, 13): def version() -> int: return 13 elif sys.version_info[:2] == (3, 12): @@ -983,15 +989,6 @@ elif sys.version_info[:2] == (3, 10): elif sys.version_info[:2] == (3, 9): def version() -> int: return 9 -elif sys.version_info[:2] == (3, 8): - def version() -> int: - return 8 -elif sys.version_info[:2] == (3, 7): - def version() -> int: - return 7 -elif sys.version_info[:2] == (3, 6): - def version() -> int: - return 6 else: raise Exception("we don't support this version yet!") @@ -1095,19 +1092,20 @@ def test_complex() -> None: from typing import cast import sys -A = sys.platform == 'x' and foobar -B = sys.platform == 'x' and sys.foobar -C = sys.platform == 'x' and f(a, -b, 'y') > [c + e, g(y=2)] -C = sys.platform == 'x' and cast(a, b[c]) -C = sys.platform == 'x' and (lambda x: y + x) -C = sys.platform == 'x' and (x for y in z) -C = sys.platform == 'x' and [x for y in z] -C = sys.platform == 'x' and {x: x for y in z} -C = sys.platform == 'x' and {x for y in z} - -assert not A -assert not B -assert not C +def test_unreachable() -> None: + A = sys.platform == 'x' and foobar + B = sys.platform == 'x' and sys.foobar + C = sys.platform == 'x' and f(a, -b, 'y') > [c + e, g(y=2)] + C = sys.platform == 'x' and cast(a, b[c]) + C = sys.platform == 'x' and (lambda x: y + x) + C = sys.platform == 'x' and (x for y in z) + C = sys.platform == 'x' and [x for y in z] + C = sys.platform == 'x' and {x: x for y in z} + C = sys.platform == 'x' and {x for y in z} + + assert not A + assert not B + assert not C [case testDoesntSegfaultWhenTopLevelFails] # make the initial import fail @@ -1131,6 +1129,10 @@ class B(A): def _(arg): pass def _(arg): pass +def test_underscore() -> None: + A() + B() + [case testGlobalRedefinition_toplevel] # mypy: allow-redefinition i = 0 @@ -1171,3 +1173,26 @@ def test_dummy_context() -> None: with c: assert c.c == 1 assert c.c == 0 + +[case testIsInstanceTuple] +from typing import Any + +def isinstance_empty(x: Any) -> bool: + return isinstance(x, ()) +def isinstance_single(x: Any) -> bool: + return isinstance(x, (str,)) +def isinstance_multi(x: Any) -> bool: + return isinstance(x, (str, int)) + +def test_isinstance_empty() -> None: + assert isinstance_empty("a") is False + assert isinstance_empty(1) is False + assert isinstance_empty(None) is False +def test_isinstance_single() -> None: + assert isinstance_single("a") is True + assert isinstance_single(1) is False + assert isinstance_single(None) is False +def test_isinstance_multi() -> None: + assert isinstance_multi("a") is True + assert isinstance_multi(1) is True + assert isinstance_multi(None) is False diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 5edd5688140ea..9323612cb4fbc 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -155,7 +155,7 @@ def f(c: C) -> int: c = cast(C, o) return a_global + c.x + c.f() + d.x + d.f() + 1 [file other.py] -from typing_extensions import Final +from typing import Final a_global: Final = int('5') class C: @@ -495,7 +495,7 @@ class Bar: bar(self) [file other.py] -from typing_extensions import TYPE_CHECKING +from typing import TYPE_CHECKING MYPY = False if MYPY: from native import Foo @@ -525,7 +525,7 @@ def f(c: 'C') -> int: return c.x [file other.py] -from typing_extensions import TYPE_CHECKING +from typing import TYPE_CHECKING if TYPE_CHECKING: from native import D @@ -735,11 +735,11 @@ def foo() -> int: return X [file other.py] -from typing_extensions import Final +from typing import Final X: Final = 10 [file other.py.2] -from typing_extensions import Final +from typing import Final X: Final = 20 [file driver.py] @@ -816,7 +816,7 @@ def foo() -> int: return 10 [file driver.py] import native -[rechecked native, other_a] +[rechecked other_a] [case testSeparateCompilationWithUndefinedAttribute] from other_a import A @@ -902,3 +902,51 @@ import native [out2] 0 None + +[case testIncrementalCompilationWithNonClassTypeDef] +import other_a +[file other_a.py] +from other_b import MyInt +[file other_a.py.2] +from other_b import MyInt, NT, TD +i = MyInt(42) + +def f(x: MyInt) -> int: + return x + 1 + +def g(x: int) -> MyInt: + return MyInt(x + 2) + +print(i) +print(f(i)) +print(g(13)) + +def make_nt(x: int) -> NT: + return NT(x=MyInt(x)) + +print(make_nt(4)) + +def make_td(x: int) -> TD: + return {"x": MyInt(x)} + +print(make_td(5)) + +[file other_b.py] +from typing import NewType, NamedTuple, TypedDict +from enum import Enum + +MyInt = NewType("MyInt", int) +NT = NamedTuple("NT", [("x", MyInt)]) +TD = TypedDict("TD", {"x": MyInt}) + +[file driver.py] +import native + +[typing fixtures/typing-full.pyi] +[out] +[out2] +42 +43 +15 +NT(x=4) +{'x': 5} 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 a5a3f058d1e2f..5c0a807c375aa 100644 --- a/mypyc/test-data/run-python312.test +++ b/mypyc/test-data/run-python312.test @@ -1,5 +1,6 @@ [case testPEP695Basics] -from typing import Any, TypeAliasType, cast +from enum import Enum +from typing import Any, Literal, TypeAliasType, cast from testutil import assertRaises @@ -188,6 +189,13 @@ type R = int | list[R] def test_recursive_type_alias() -> None: assert isinstance(R, TypeAliasType) assert getattr(R, "__value__") == (int | list[R]) + +class SomeEnum(Enum): + AVALUE = "a" + +type EnumLiteralAlias1 = Literal[SomeEnum.AVALUE] +type EnumLiteralAlias2 = Literal[SomeEnum.AVALUE] | None +EnumLiteralAlias3 = Literal[SomeEnum.AVALUE] | None [typing fixtures/typing-full.pyi] [case testPEP695GenericTypeAlias] diff --git a/mypyc/test-data/run-python38.test b/mypyc/test-data/run-python38.test index 7de43907cb867..cf7c7d7dea52e 100644 --- a/mypyc/test-data/run-python38.test +++ b/mypyc/test-data/run-python38.test @@ -75,11 +75,12 @@ class Bar(Foo): def f(self, *args: int, **kwargs: int) -> None: print("stuff", args, kwargs) -z: Foo = Bar() -z.f(1, z=50) -z.f() -z.f(1) -z.f(z=50) +def test_pos_only() -> None: + z: Foo = Bar() + z.f(1, z=50) + z.f() + z.f(1) + z.f(z=50) [out] stuff (1,) {'z': 50} diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test index 8d178d03a75bf..2668d63bcdaca 100644 --- a/mypyc/test-data/run-sets.test +++ b/mypyc/test-data/run-sets.test @@ -116,9 +116,126 @@ s = {1, 2, 3} update(s, [5, 4, 3]) assert s == {1, 2, 3, 4, 5} +[case testFrozenSets] +from typing import FrozenSet, List, Any, cast +from testutil import assertRaises + +def instantiateLiteral() -> FrozenSet[int]: + return frozenset((1, 2, 3, 5, 8)) + +def emptyFrozenSet1() -> FrozenSet[int]: + return frozenset() + +def emptyFrozenSet2() -> FrozenSet[int]: + return frozenset(()) + +def fromIterator() -> List[FrozenSet[int]]: + a = frozenset([1, 3, 5]) + b = frozenset((1, 3, 5)) + c = frozenset({1, 3, 5}) + d = frozenset({1: '1', 3: '3', 5: '5'}) + e = frozenset(x for x in range(1, 6, 2)) + f = frozenset((x for x in range(1, 6, 2))) + return [a, b, c, d, e, f] + +def fromIterator2() -> FrozenSet[int]: + tmp_list = [1, 2, 3, 4, 5] + return frozenset((x + 1) for x in ((y * 10) for y in (z for z in tmp_list if z < 4))) + +def castFrozenSet() -> FrozenSet[int]: + x: Any = frozenset((1, 2, 3, 5, 8)) + return cast(FrozenSet, x) + +def castFrozenSetError() -> FrozenSet[int]: + x: Any = {1, 2, 3, 5, 8} + return cast(FrozenSet, x) + +def test_frozen_sets() -> None: + val = instantiateLiteral() + assert 1 in val + assert 2 in val + assert 3 in val + assert 5 in val + assert 8 in val + assert len(val) == 5 + assert val == {1, 2, 3, 5, 8} + s = 0 + for i in val: + s += i + assert s == 19 + + empty_set1 = emptyFrozenSet1() + assert empty_set1 == frozenset() + + empty_set2 = emptyFrozenSet2() + assert empty_set2 == frozenset() + + sets = fromIterator() + for s2 in sets: + assert s2 == {1, 3, 5} + + s3 = fromIterator2() + assert s3 == {11, 21, 31} + + val2 = castFrozenSet() + assert val2 == {1, 2, 3, 5, 8} + + with assertRaises(TypeError, "frozenset object expected; got set"): + castFrozenSetError() + +[case testFrozenSetsFromIterables] +from typing import FrozenSet + +def f(x: int) -> int: + return x + +def f1() -> FrozenSet[int]: + tmp_list = [1, 3, 5] + return frozenset(f(x) for x in tmp_list) + +def f2() -> FrozenSet[int]: + tmp_tuple = (1, 3, 5) + return frozenset(f(x) for x in tmp_tuple) + +def f3() -> FrozenSet[int]: + tmp_set = {1, 3, 5} + return frozenset(f(x) for x in tmp_set) + +def f4() -> FrozenSet[int]: + tmp_dict = {1: '1', 3: '3', 5: '5'} + return frozenset(f(x) for x in tmp_dict) + +def f5() -> FrozenSet[int]: + return frozenset(f(x) for x in range(1, 6, 2)) + +def f6() -> FrozenSet[int]: + return frozenset((f(x) for x in range(1, 6, 2))) + +def g1(x: int) -> int: + return x + +def g2(x: int) -> int: + return x * 10 + +def g3(x: int) -> int: + return x + 1 + +def g4() -> FrozenSet[int]: + tmp_list = [1, 2, 3, 4, 5] + return frozenset(g3(x) for x in (g2(y) for y in (g1(z) for z in tmp_list if z < 4))) + +def test_frozen_sets_from_iterables() -> None: + val = frozenset({1, 3, 5}) + assert f1() == val + assert f2() == val + assert f3() == val + assert f4() == val + assert f5() == val + assert f6() == val + assert g4() == frozenset({11, 21, 31}) + [case testPrecomputedFrozenSets] -from typing import Any -from typing_extensions import Final +from typing import Final, Any CONST: Final = "CONST" non_const = "non_const" @@ -148,3 +265,55 @@ def test_in_set() -> None: def test_for_set() -> None: assert not s ^ {None, False, 1, 2.0, "3", b"4", 5j, (6,), CONST}, s + +[case testIsInstance] +from copysubclass import subset, subfrozenset +def test_built_in_set() -> None: + assert isinstance(set(), set) + assert isinstance({'one', 'two'}, set) + assert isinstance({'a', 1}, set) + assert isinstance(subset(), set) + assert isinstance(subset({'one', 'two'}), set) + assert isinstance(subset({'a', 1}), set) + + assert not isinstance(frozenset(), set) + assert not isinstance({}, set) + assert not isinstance([], set) + assert not isinstance((1,2,3), set) + assert not isinstance({1:'a', 2:'b'}, set) + assert not isinstance(int() + 1, set) + assert not isinstance(str() + 'a', set) + +def test_user_defined_set() -> None: + from userdefinedset import set + + assert isinstance(set(), set) + assert not isinstance({set()}, set) + +def test_built_in_frozenset() -> None: + assert isinstance(frozenset(), frozenset) + assert isinstance(frozenset({'one', 'two'}), frozenset) + assert isinstance(frozenset({'a', 1}), frozenset) + assert isinstance(subfrozenset(), frozenset) + assert isinstance(subfrozenset({'one', 'two'}), frozenset) + assert isinstance(subfrozenset({'a', 1}), frozenset) + + assert not isinstance(set(), frozenset) + assert not isinstance({}, frozenset) + assert not isinstance([], frozenset) + assert not isinstance((1,2,3), frozenset) + assert not isinstance({1:'a', 2:'b'}, frozenset) + assert not isinstance(int() + 1, frozenset) + assert not isinstance(str() + 'a', frozenset) + +[file copysubclass.py] +from typing import Any +class subset(set[Any]): + pass + +class subfrozenset(frozenset[Any]): + pass + +[file userdefinedset.py] +class set: + pass diff --git a/mypyc/test-data/run-signatures.test b/mypyc/test-data/run-signatures.test new file mode 100644 index 0000000000000..0a9ea32f5357d --- /dev/null +++ b/mypyc/test-data/run-signatures.test @@ -0,0 +1,223 @@ +[case testSignaturesBasic] +def f1(): pass +def f2(x): pass +def f3(x, /): pass +def f4(*, x): pass +def f5(*x): pass +def f6(**x): pass +def f7(x=None): pass +def f8(x=None, /): pass +def f9(*, x=None): pass +def f10(a, /, b, c=None, *args, d=None, **h): pass + +[file driver.py] +import inspect +from native import * + +assert str(inspect.signature(f1)) == "()" +assert str(inspect.signature(f2)) == "(x)" +assert str(inspect.signature(f3)) == "(x, /)" +assert str(inspect.signature(f4)) == "(*, x)" +assert str(inspect.signature(f5)) == "(*x)" +assert str(inspect.signature(f6)) == "(**x)" +assert str(inspect.signature(f7)) == "(x=None)" +assert str(inspect.signature(f8)) == "(x=None, /)" +assert str(inspect.signature(f9)) == "(*, x=None)" +assert str(inspect.signature(f10)) == "(a, /, b, c=None, *args, d=None, **h)" + +for fn in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10]: + assert getattr(fn, "__doc__") is None + +[case testSignaturesValidDefaults] +from typing import Final +A: Final = 1 + +def default_int(x=1): pass +def default_str(x="a"): pass +def default_float(x=1.0): pass +def default_true(x=True): pass +def default_false(x=False): pass +def default_none(x=None): pass +def default_tuple_empty(x=()): pass +def default_tuple_literals(x=(1, "a", 1.0, False, True, None, (), (1,2,(3,4)))): pass +def default_tuple_singleton(x=(1,)): pass +def default_named_constant(x=A): pass + +[file driver.py] +import inspect +from native import * + +assert str(inspect.signature(default_int)) == "(x=1)" +assert str(inspect.signature(default_str)) == "(x='a')" +assert str(inspect.signature(default_float)) == "(x=1.0)" +assert str(inspect.signature(default_true)) == "(x=True)" +assert str(inspect.signature(default_false)) == "(x=False)" +assert str(inspect.signature(default_none)) == "(x=None)" +assert str(inspect.signature(default_tuple_empty)) == "(x=())" +assert str(inspect.signature(default_tuple_literals)) == "(x=(1, 'a', 1.0, False, True, None, (), (1, 2, (3, 4))))" +assert str(inspect.signature(default_named_constant)) == "(x=1)" + +# Check __text_signature__ directly since inspect.signature produces +# an incorrect signature for 1-tuple default arguments prior to +# Python 3.12 (cpython#102379). +# assert str(inspect.signature(default_tuple_singleton)) == "(x=(1,))" +assert getattr(default_tuple_singleton, "__text_signature__") == "(x=(1,))" + +[case testSignaturesStringDefaults] +def f1(x="'foo"): pass +def f2(x='"foo'): pass +def f3(x=""""Isn\'t," they said."""): pass +def f4(x="\\ \a \b \f \n \r \t \v \x00"): pass +def f5(x="\N{BANANA}sv"): pass + +[file driver.py] +import inspect +from native import * + +assert str(inspect.signature(f1)) == """(x="'foo")""" +assert str(inspect.signature(f2)) == """(x='"foo')""" +assert str(inspect.signature(f3)) == r"""(x='"Isn\'t," they said.')""" +assert str(inspect.signature(f4)) == r"""(x='\\ \x07 \x08 \x0c \n \r \t \x0b \x00')""" +assert str(inspect.signature(f5)) == """(x='\N{BANANA}sv')""" + +[case testSignaturesIrrepresentableDefaults] +import enum +class Color(enum.Enum): + RED = 1 +misc = object() + +# Default arguments that cannot be represented in a __text_signature__ +def bad_object(x=misc): pass +def bad_list_nonliteral(x=[misc]): pass +def bad_dict_nonliteral(x={'a': misc}): pass +def bad_set_nonliteral(x={misc}): pass +def bad_set_empty(x=set()): pass # supported by ast.literal_eval, but not by inspect._signature_fromstr +def bad_nan(x=float("nan")): pass +def bad_enum(x=Color.RED): pass + +# TODO: Default arguments that could potentially be represented in a +# __text_signature__, but which are not currently supported. +# See 'inspect._signature_fromstr' for what default values are supported at runtime. +def bad_complex(x=1+2j): pass +def bad_list_empty(x=[]): pass +def bad_list_literals(x=[1, 2, 3]): pass +def bad_dict_empty(x={}): pass +def bad_dict_literals(x={'a': 1}): pass +def bad_set_literals(x={1, 2, 3}): pass +def bad_tuple_literals(x=([1, 2, 3], {'a': 1}, {1, 2, 3})): pass +def bad_ellipsis(x=...): pass +def bad_literal_fold(x=1+2): pass + +[file driver.py] +import inspect +from testutil import assertRaises +import native + +all_bad = [fn for name, fn in vars(native).items() if name.startswith("bad_")] +assert all_bad + +for bad in all_bad: + assert bad.__text_signature__ is None, f"{bad.__name__} has unexpected __text_signature__" + with assertRaises(ValueError, "no signature found for builtin"): + inspect.signature(bad) + +[case testSignaturesMethods] +class Foo: + def f1(self, x): pass + @classmethod + def f2(cls, x): pass + @staticmethod + def f3(x): pass + def __eq__(self, x: object): pass + +[file driver.py] +import inspect +from native import * + +assert str(inspect.signature(Foo.f1)) == "(self, /, x)" +assert str(inspect.signature(Foo().f1)) == "(x)" + +assert str(inspect.signature(Foo.f2)) == "(x)" +assert str(inspect.signature(Foo().f2)) == "(x)" + +assert str(inspect.signature(Foo.f3)) == "(x)" +assert str(inspect.signature(Foo().f3)) == "(x)" + +assert str(inspect.signature(Foo.__eq__)) == "(self, value, /)" +assert str(inspect.signature(Foo().__eq__)) == "(value, /)" + +[case testSignaturesConstructors] +class Empty: pass + +class HasInit: + def __init__(self, x) -> None: pass + +class InheritedInit(HasInit): pass + +class HasInitBad: + def __init__(self, x=[]) -> None: pass + +[file driver.py] +import inspect +from testutil import assertRaises +from native import * + +assert str(inspect.signature(Empty)) == "()" +assert str(inspect.signature(Empty.__init__)) == "(self, /, *args, **kwargs)" + +assert str(inspect.signature(HasInit)) == "(x)" +assert str(inspect.signature(HasInit.__init__)) == "(self, /, *args, **kwargs)" + +assert str(inspect.signature(InheritedInit)) == "(x)" +assert str(inspect.signature(InheritedInit.__init__)) == "(self, /, *args, **kwargs)" + +assert getattr(HasInitBad, "__text_signature__") is None +with assertRaises(ValueError, "no signature found for builtin"): + inspect.signature(HasInitBad) + +# CPython detail note: type objects whose tp_doc contains only a text signature behave +# differently from method objects whose ml_doc contains only a test signature: type +# objects will have __doc__="" whereas method objects will have __doc__=None. This +# difference stems from the former using _PyType_GetDocFromInternalDoc(...) and the +# latter using PyUnicode_FromString(_PyType_DocWithoutSignature(...)). +for cls in [Empty, HasInit, InheritedInit]: + assert getattr(cls, "__doc__") == "" +assert getattr(HasInitBad, "__doc__") is None + +[case testSignaturesConstructorsNonExt] +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class NonExt: + def __init__(self, x) -> None: pass + +[file driver.py] +import inspect +from testutil import assertRaises +from native import * + +# TODO: support constructor signatures for non-extension classes +with assertRaises(ValueError, "no signature found for builtin"): + inspect.signature(NonExt) + +[case testSignaturesHistoricalPositionalOnly] +import inspect + +def f1(__x): pass +def f2(__x, y): pass +def f3(*, __y): pass +def f4(x, *, __y): pass +def f5(__x, *, __y): pass + +class A: + def func(self, __x): pass + +def test_historical_positional_only() -> None: + assert str(inspect.signature(f1)) == "(__x, /)" + assert str(inspect.signature(f2)) == "(__x, /, y)" + assert str(inspect.signature(f3)) == "(*, __y)" + assert str(inspect.signature(f4)) == "(x, *, __y)" + assert str(inspect.signature(f5)) == "(__x, /, *, __y)" + + assert str(inspect.signature(A.func)) == "(self, __x, /)" + assert str(inspect.signature(A().func)) == "(__x, /)" diff --git a/mypyc/test-data/run-singledispatch.test b/mypyc/test-data/run-singledispatch.test index 61e4897c96d66..03b937261e22a 100644 --- a/mypyc/test-data/run-singledispatch.test +++ b/mypyc/test-data/run-singledispatch.test @@ -4,9 +4,10 @@ [case testSpecializedImplementationUsed] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False @fun.register @@ -19,11 +20,13 @@ def test_specialize() -> None: [case testSubclassesOfExpectedTypeUseSpecialized] from functools import singledispatch +from typing import Any + class A: pass class B(A): pass @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False @fun.register @@ -36,11 +39,13 @@ def test_specialize() -> None: [case testSuperclassImplementationNotUsedWhenSubclassHasImplementation] from functools import singledispatch +from typing import Any + class A: pass class B(A): pass @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: # shouldn't be using this assert False @@ -58,9 +63,10 @@ def test_specialize() -> None: [case testMultipleUnderscoreFunctionsIsntError] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> str: +def fun(arg: Any) -> str: return 'default' @fun.register @@ -72,7 +78,7 @@ def _(arg: int) -> str: return 'int' # extra function to make sure all 3 underscore functions aren't treated as one OverloadedFuncDef -def a(b): pass +def a(b: Any) -> Any: pass @fun.register def _(arg: list) -> str: @@ -86,10 +92,12 @@ def test_singledispatch() -> None: [case testCanRegisterCompiledClasses] from functools import singledispatch +from typing import Any + class A: pass @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False @fun.register def fun_specialized(arg: A) -> bool: @@ -101,13 +109,14 @@ def test_singledispatch() -> None: [case testTypeUsedAsArgumentToRegister] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False @fun.register(int) -def fun_specialized(arg) -> bool: +def fun_specialized(arg: Any) -> bool: return True def test_singledispatch() -> None: @@ -116,12 +125,13 @@ def test_singledispatch() -> None: [case testUseRegisterAsAFunction] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False -def fun_specialized_impl(arg) -> bool: +def fun_specialized_impl(arg: Any) -> bool: return True fun.register(int, fun_specialized_impl) @@ -132,13 +142,14 @@ def test_singledispatch() -> None: [case testRegisterDoesntChangeFunction] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False @fun.register(int) -def fun_specialized(arg) -> bool: +def fun_specialized(arg: Any) -> bool: return True def test_singledispatch() -> None: @@ -147,28 +158,32 @@ def test_singledispatch() -> None: # TODO: turn this into a mypy error [case testNoneIsntATypeWhenUsedAsArgumentToRegister] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False -try: - @fun.register - def fun_specialized(arg: None) -> bool: - return True -except TypeError: - pass +def test_argument() -> None: + try: + @fun.register + def fun_specialized(arg: None) -> bool: + return True + assert False, "expected to raise an exception" + except TypeError: + pass [case testRegisteringTheSameFunctionSeveralTimes] from functools import singledispatch +from typing import Any @singledispatch -def fun(arg) -> bool: +def fun(arg: Any) -> bool: return False @fun.register(int) @fun.register(str) -def fun_specialized(arg) -> bool: +def fun_specialized(arg: Any) -> bool: return True def test_singledispatch() -> None: @@ -177,11 +192,13 @@ def test_singledispatch() -> None: assert not fun([1, 2]) [case testTypeIsAnABC] +# mypy: allow-untyped-defs from functools import singledispatch from collections.abc import Mapping @singledispatch def fun(arg) -> bool: + # TODO: Adding an Any parameter annotation breaks the test case return False @fun.register @@ -239,6 +256,7 @@ def test_singledispatchmethod() -> None: [case testSingledispatchTreeSumAndEqual] from functools import singledispatch +from typing import cast class Tree: pass @@ -284,10 +302,10 @@ def build(n: int) -> Tree: return Leaf() return Node(n, build(n - 1), build(n - 1)) -def test_sum_and_equal(): +def test_sum_and_equal() -> None: tree = build(5) tree2 = build(5) - tree2.right.right.right.value = 10 + cast(Node, cast(Node, cast(Node, cast(Node, tree2).right).right).right).value = 10 assert calc_sum(tree) == 57 assert calc_sum(tree2) == 65 assert equal(tree, tree) @@ -352,7 +370,7 @@ def verify_typevarexpr(stub: TypeVarExpr, a: MaybeMissing[Any], b: List[str]) -> if False: yield None -def verify_list(stub, a, b) -> List[str]: +def verify_list(stub: Any, a: Any, b: Any) -> List[str]: """Helper function that converts iterator of errors to list of messages""" return list(err.msg for err in verify(stub, a, b)) @@ -366,31 +384,33 @@ def test_verify() -> None: [case testArgsInRegisteredImplNamedDifferentlyFromMainFunction] from functools import singledispatch +from typing import Any @singledispatch -def f(a) -> bool: +def f(a: Any) -> bool: return False @f.register def g(b: int) -> bool: return True -def test_singledispatch(): +def test_singledispatch() -> None: assert f(5) assert not f('a') [case testKeywordArguments] from functools import singledispatch +from typing import Any @singledispatch -def f(arg, *, kwarg: int = 0) -> int: +def f(arg: Any, *, kwarg: int = 0) -> int: return kwarg + 10 @f.register def g(arg: int, *, kwarg: int = 5) -> int: return kwarg - 10 -def test_keywords(): +def test_keywords() -> None: assert f('a') == 10 assert f('a', kwarg=3) == 13 assert f('a', kwarg=7) == 17 @@ -411,14 +431,14 @@ def f(arg: Any) -> Iterable[int]: def g(arg: str) -> Iterable[int]: return [0] -def test_iterables(): +def test_iterables() -> None: assert f(1) != [1] assert list(f(1)) == [1] assert f('a') == [0] [case testRegisterUsedAtSameTimeAsOtherDecorators] from functools import singledispatch -from typing import TypeVar +from typing import TypeVar, Any class A: pass class B: pass @@ -429,7 +449,7 @@ def decorator(f: T) -> T: return f @singledispatch -def f(arg) -> int: +def f(arg: Any) -> int: return 0 @f.register @@ -437,7 +457,7 @@ def f(arg) -> int: def h(arg: str) -> int: return 2 -def test_singledispatch(): +def test_singledispatch() -> None: assert f(1) == 0 assert f('a') == 2 @@ -448,12 +468,12 @@ from typing import Callable, Any class A: pass def decorator(f: Callable[[Any], int]) -> Callable[[Any], int]: - def wrapper(x) -> int: + def wrapper(x: Any) -> int: return f(x) * 7 return wrapper @singledispatch -def f(arg) -> int: +def f(arg: Any) -> int: return 10 @f.register @@ -462,17 +482,19 @@ def h(arg: str) -> int: return 5 -def test_singledispatch(): +def test_singledispatch() -> None: assert f('a') == 35 assert f(A()) == 10 [case testMoreSpecificTypeBeforeLessSpecificType] from functools import singledispatch +from typing import Any + class A: pass class B(A): pass @singledispatch -def f(arg) -> str: +def f(arg: Any) -> str: return 'default' @f.register @@ -483,20 +505,21 @@ def g(arg: B) -> str: def h(arg: A) -> str: return 'a' -def test_singledispatch(): +def test_singledispatch() -> None: assert f(B()) == 'b' assert f(A()) == 'a' assert f(5) == 'default' [case testMultipleRelatedClassesBeingRegistered] from functools import singledispatch +from typing import Any class A: pass class B(A): pass class C(B): pass @singledispatch -def f(arg) -> str: return 'default' +def f(arg: Any) -> str: return 'default' @f.register def _(arg: A) -> str: return 'a' @@ -507,7 +530,7 @@ def _(arg: C) -> str: return 'c' @f.register def _(arg: B) -> str: return 'b' -def test_singledispatch(): +def test_singledispatch() -> None: assert f(A()) == 'a' assert f(B()) == 'b' assert f(C()) == 'c' @@ -523,7 +546,7 @@ def a(arg: A) -> int: def _(arg: C) -> int: return 3 -def test_singledispatch(): +def test_singledispatch() -> None: assert f(B()) == 1 assert f(A()) == 2 assert f(C()) == 3 @@ -537,7 +560,7 @@ class B(A): pass class C(B): pass @singledispatch -def f(arg) -> int: +def f(arg: object) -> int: return 0 @f.register @@ -547,6 +570,7 @@ def g(arg: B) -> int: [case testOrderCanOnlyBeDeterminedFromMRONotIsinstanceChecks] from mypy_extensions import trait from functools import singledispatch +from typing import Any @trait class A: pass @@ -556,9 +580,8 @@ class AB(A, B): pass class BA(B, A): pass @singledispatch -def f(arg) -> str: +def f(arg: Any) -> str: return "default" - pass @f.register def fa(arg: A) -> str: @@ -568,18 +591,19 @@ def fa(arg: A) -> str: def fb(arg: B) -> str: return "b" -def test_singledispatch(): +def test_singledispatch() -> None: assert f(AB()) == "a" assert f(BA()) == "b" [case testCallingFunctionBeforeAllImplementationsRegistered] from functools import singledispatch +from typing import Any class A: pass class B(A): pass @singledispatch -def f(arg) -> str: +def f(arg: Any) -> str: return 'default' assert f(A()) == 'default' @@ -598,13 +622,16 @@ assert f(1) == 'default' def _(arg: B) -> str: return 'b' -assert f(A()) == 'a' -assert f(B()) == 'b' -assert f(1) == 'default' +# TODO: Move whole testcase to a function when mypyc#1118 is fixed. +def test_final() -> None: + assert f(A()) == 'a' + assert f(B()) == 'b' + assert f(1) == 'default' [case testDynamicallyRegisteringFunctionFromInterpretedCode] from functools import singledispatch +from typing import Any class A: pass class B(A): pass @@ -612,7 +639,7 @@ class C(B): pass class D(C): pass @singledispatch -def f(arg) -> str: +def f(arg: Any) -> str: return "default" @f.register @@ -643,9 +670,10 @@ assert c(A()) == 'c' [case testMalformedDynamicRegisterCall] from functools import singledispatch +from typing import Any @singledispatch -def f(arg) -> None: +def f(arg: Any) -> None: pass [file register.py] from native import f @@ -663,7 +691,7 @@ import register from functools import singledispatch @singledispatch -def f(arg) -> str: +def f(arg: object) -> str: return 'default' [file register.py] diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index be668435d073f..6a62db6ee3ee0 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -2,6 +2,11 @@ [case testStrBasics] from typing import Tuple +class A: + def __str__(self) -> str: + return "A-str" + def __repr__(self) -> str: + return "A-repr" def f() -> str: return 'some string' def g() -> str: @@ -10,6 +15,14 @@ def tostr(x: int) -> str: return str(x) def booltostr(x: bool) -> str: return str(x) +def clstostr(x: A) -> str: + return str(x) +def torepr(x: int) -> str: + return repr(x) +def booltorepr(x: bool) -> str: + return repr(x) +def clstorepr(x: A) -> str: + return repr(x) def concat(x: str, y: str) -> str: return x + y def eq(x: str) -> int: @@ -20,10 +33,21 @@ def eq(x: str) -> int: return 2 def match(x: str, y: str) -> Tuple[bool, bool]: return (x.startswith(y), x.endswith(y)) +def match_tuple(x: str, y: Tuple[str, ...]) -> Tuple[bool, bool]: + return (x.startswith(y), x.endswith(y)) +def match_tuple_literal_args(x: str, y: str, z: str) -> Tuple[bool, bool]: + return (x.startswith((y, z)), x.endswith((y, z))) +def remove_prefix_suffix(x: str, y: str) -> Tuple[str, str]: + return (x.removeprefix(y), x.removesuffix(y)) [file driver.py] -from native import f, g, tostr, booltostr, concat, eq, match +from native import ( + f, g, A, tostr, booltostr, clstostr, concat, eq, match, match_tuple, + match_tuple_literal_args, remove_prefix_suffix, + torepr, booltorepr, clstorepr +) import sys +from testutil import assertRaises assert f() == 'some string' assert f() is sys.intern('some string') @@ -32,20 +56,103 @@ assert tostr(57) == '57' assert concat('foo', 'bar') == 'foobar' assert booltostr(True) == 'True' assert booltostr(False) == 'False' +assert clstostr(A()) == "A-str" assert eq('foo') == 0 assert eq('zar') == 1 assert eq('bar') == 2 +assert torepr(57) == '57' +assert booltorepr(True) == 'True' +assert booltorepr(False) == 'False' +assert clstorepr(A()) == "A-repr" + assert int(tostr(0)) == 0 assert int(tostr(20)) == 20 +assert int(torepr(0)) == 0 +assert int(torepr(20)) == 20 assert match('', '') == (True, True) assert match('abc', '') == (True, True) assert match('abc', 'a') == (True, False) assert match('abc', 'c') == (False, True) assert match('', 'abc') == (False, False) +assert match_tuple('abc', ('d', 'e')) == (False, False) +assert match_tuple('abc', ('a', 'c')) == (True, True) +assert match_tuple('abc', ('a',)) == (True, False) +assert match_tuple('abc', ('c',)) == (False, True) +assert match_tuple('abc', ('x', 'y', 'z')) == (False, False) +assert match_tuple('abc', ('x', 'y', 'z', 'a', 'c')) == (True, True) +with assertRaises(TypeError, "tuple for startswith must only contain str"): + assert match_tuple('abc', (None,)) +with assertRaises(TypeError, "tuple for endswith must only contain str"): + assert match_tuple('abc', ('a', None)) +assert match_tuple_literal_args('abc', 'z', 'a') == (True, False) +assert match_tuple_literal_args('abc', 'z', 'c') == (False, True) + +assert remove_prefix_suffix('', '') == ('', '') +assert remove_prefix_suffix('abc', 'a') == ('bc', 'abc') +assert remove_prefix_suffix('abc', 'c') == ('abc', 'ab') + +[case testStringEquality] +def eq(a: str, b: str) -> bool: + return a == b +def ne(a: str, b: str) -> bool: + return a != b + +def test_basic() -> None: + xy = "xy" + xy2 = str().join(["x", "y"]) + xx = "xx" + yy = "yy" + xxx = "xxx" + + assert eq("", str()) + assert not ne("", str()) + + assert eq("x", "x" + str()) + assert ne("x", "y") + + assert eq(xy, xy) + assert eq(xy, xy2) + assert not eq(xy, yy) + assert ne(xy, xx) + assert not ne(xy, xy) + assert not ne(xy, xy2) + + assert ne(xx, xxx) + assert ne(xxx, xx) + assert ne("x", "") + assert ne("", "x") + + assert ne("XX", xx) + assert ne(yy, xy) + +def test_unicode() -> None: + assert eq(chr(200), chr(200) + str()) + assert ne(chr(200), chr(201)) + + assert eq(chr(1234), chr(1234) + str()) + assert ne(chr(1234), chr(1235)) + + assert eq("\U0001f4a9", "\U0001f4a9" + str()) + assert eq("\U0001f4a9", "\U0001F4A9" + str()) + assert ne("\U0001f4a9", "\U0002f4a9" + str()) + assert ne("\U0001f4a9", "\U0001f5a9" + str()) + assert ne("\U0001f4a9", "\U0001f4a8" + str()) + + assert eq("foobar\u1234", "foobar\u1234" + str()) + assert eq("\u1234foobar", "\u1234foobar" + str()) + assert ne("foobar\uf234", "foobar\uf235") + assert ne("foobar\uf234", "foobar\uf334") + assert ne("foobar\u1234", "Foobar\u1234" + str()) + + assert eq("foo\U0001f4a9", "foo\U0001f4a9" + str()) + assert eq("\U0001f4a9foo", "\U0001f4a9foo" + str()) + assert ne("foo\U0001f4a9", "foo\U0001f4a8" + str()) + assert ne("\U0001f4a9foo", "\U0001f4a8foo" + str()) [case testStringOps] -from typing import List, Optional +from typing import List, Optional, Tuple +from testutil import assertRaises def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: if sep is not None: @@ -55,6 +162,14 @@ def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) return s.split(sep) return s.split() +def do_rsplit(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: + if sep is not None: + if max_split is not None: + return s.rsplit(sep, max_split) + else: + return s.rsplit(sep) + return s.rsplit() + ss = "abc abcd abcde abcdef" def test_split() -> None: @@ -66,13 +181,77 @@ def test_split() -> None: assert do_split(ss, " ", 1) == ["abc", "abcd abcde abcdef"] assert do_split(ss, " ", 2) == ["abc", "abcd", "abcde abcdef"] +def test_rsplit() -> None: + assert do_rsplit(ss) == ["abc", "abcd", "abcde", "abcdef"] + assert do_rsplit(ss, " ") == ["abc", "abcd", "abcde", "abcdef"] + assert do_rsplit(ss, "-") == ["abc abcd abcde abcdef"] + assert do_rsplit(ss, " ", -1) == ["abc", "abcd", "abcde", "abcdef"] + assert do_rsplit(ss, " ", 0) == ["abc abcd abcde abcdef"] + assert do_rsplit(ss, " ", 1) == ["abc abcd abcde", "abcdef"] # different to do_split + assert do_rsplit(ss, " ", 2) == ["abc abcd", "abcde", "abcdef"] # different to do_split + +def splitlines(s: str, keepends: Optional[bool] = None) -> List[str]: + if keepends is not None: + return s.splitlines(keepends) + return s.splitlines() + +s_text = "This\nis\n\nsome\nlong\ntext.\n" + +def test_splitlines() -> None: + assert splitlines(s_text) == ["This", "is", "", "some", "long", "text."] + assert splitlines(s_text, False) == ["This", "is", "", "some", "long", "text."] + assert splitlines(s_text, True) == ["This\n", "is\n", "\n", "some\n", "long\n", "text.\n"] + +s_partition = "Some long text" + +def partition(s: str, sep: str) -> Tuple[str, str, str]: + return s.partition(sep) + +def rpartition(s: str, sep: str) -> Tuple[str, str, str]: + return s.rpartition(sep) + +def test_partition() -> None: + assert partition(s_partition, " ") == ("Some", " ", "long text") + assert partition(s_partition, "Hello") == ("Some long text", "", "") + assert rpartition(s_partition, " ") == ("Some long", " ", "text") + assert rpartition(s_partition, "Hello") == ("", "", "Some long text") + with assertRaises(ValueError, "empty separator"): + partition(s_partition, "") + with assertRaises(ValueError, "empty separator"): + rpartition(s_partition, "") + +def contains(s: str, o: str) -> bool: + return o in s + def getitem(s: str, index: int) -> str: return s[index] -from testutil import assertRaises +def find(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: + if start is not None: + if end is not None: + return s.find(substr, start, end) + return s.find(substr, start) + return s.find(substr) + +def rfind(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: + if start is not None: + if end is not None: + return s.rfind(substr, start, end) + return s.rfind(substr, start) + return s.rfind(substr) s = "abc" +def test_contains() -> None: + assert contains(s, "a") is True + assert contains(s, "abc") is True + assert contains(s, "Hello") is False + assert contains(s, "bc") is True + assert contains(s, "abcd") is False + assert contains(s, "bb") is False + assert contains(s, "") is True + assert contains(s, " ") is False + def test_getitem() -> None: assert getitem(s, 0) == "a" assert getitem(s, 1) == "b" @@ -85,6 +264,26 @@ def test_getitem() -> None: with assertRaises(IndexError, "string index out of range"): getitem(s, -4) +def test_find() -> None: + s = "abcab" + assert find(s, "Hello") == -1 + assert find(s, "abc") == 0 + assert find(s, "b") == 1 + assert find(s, "b", 1) == 1 + assert find(s, "b", 1, 2) == 1 + assert find(s, "b", 3) == 4 + assert find(s, "b", 3, 5) == 4 + assert find(s, "b", 3, 4) == -1 + + assert rfind(s, "Hello") == -1 + assert rfind(s, "abc") == 0 + assert rfind(s, "b") == 4 + assert rfind(s, "b", 1) == 4 + assert rfind(s, "b", 1, 2) == 1 + assert rfind(s, "b", 3) == 4 + assert rfind(s, "b", 3, 5) == 4 + assert rfind(s, "b", 3, 4) == -1 + def str_to_int(s: str, base: Optional[int] = None) -> int: if base: return int(s, base) @@ -164,7 +363,6 @@ def test_str_min_max() -> None: assert max(x, z) == 'aaa' [case testStringFormattingCStyle] -[typing fixtures/typing-full.pyi] from typing import Tuple var = 'mypyc' @@ -209,13 +407,21 @@ def test_basics() -> None: inf_num = float('inf') assert '%s, %s' % (nan_num, inf_num) == 'nan, inf' assert '%f, %f' % (nan_num, inf_num) == 'nan, inf' +[typing fixtures/typing-full.pyi] [case testFStrings] import decimal from datetime import datetime +from typing import Final var = 'mypyc' num = 20 +final_known_at_compile_time: Final = 'hello' + +def final_value_setter() -> str: + return 'goodbye' + +final_unknown_at_compile_time: Final = final_value_setter() def test_fstring_basics() -> None: assert f'Hello {var}, this is a test' == "Hello mypyc, this is a test" @@ -252,6 +458,8 @@ def test_fstring_basics() -> None: inf_num = float('inf') assert f'{nan_num}, {inf_num}' == 'nan, inf' + assert f'{final_known_at_compile_time} {final_unknown_at_compile_time}' == 'hello goodbye' + # F-strings would be translated into ''.join[string literals, format method call, ...] in mypy AST. # Currently we are using a str.join specializer for f-string speed up. We might not cover all cases # and the rest ones should fall back to a normal str.join method call. @@ -565,35 +773,51 @@ 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] +from testutil import assertRaises + def test_decode() -> None: assert "\N{GREEK CAPITAL LETTER DELTA}" == '\u0394' assert "\u0394" == "\u0394" assert "\U00000394" == '\u0394' assert b'\x80abc'.decode('utf-8', 'replace') == '\ufffdabc' assert b'\x80abc'.decode('utf-8', 'backslashreplace') == '\\x80abc' + assert b''.decode() == '' + assert b'a'.decode() == 'a' assert b'abc'.decode() == 'abc' assert b'abc'.decode('utf-8') == 'abc' + assert b'abc'.decode('utf-8' + str()) == 'abc' + assert b'abc\x00\xce'.decode('latin-1') == 'abc\x00\xce' + assert b'abc\x00\xce'.decode('latin-1' + str()) == 'abc\x00\xce' + assert b'abc\x00\x7f'.decode('ascii') == 'abc\x00\x7f' + assert b'abc\x00\x7f'.decode('ascii' + str()) == 'abc\x00\x7f' assert b'\x80abc'.decode('utf-8', 'ignore') == 'abc' assert b'\x80abc'.decode('UTF-8', 'ignore') == 'abc' assert b'\x80abc'.decode('Utf-8', 'ignore') == 'abc' @@ -602,16 +826,71 @@ def test_decode() -> None: assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('gbk', 'ignore') == '一二三' assert b'\xd2\xbb\xb6\xfe\xc8\xfd'.decode('latin1', 'ignore') == 'Ò»¶þÈý' assert b'Z\xc3\xbcrich'.decode("utf-8") == 'Zürich' - try: - b'Z\xc3\xbcrich'.decode('ascii') - assert False - except UnicodeDecodeError: - pass + assert b'Z\xc3\xbcrich'.decode("utf-8" + str()) == 'Zürich' + assert bytearray(range(5)).decode() == '\x00\x01\x02\x03\x04' b = bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd') assert b.decode() == '你好' assert b.decode('gbk') == '浣犲ソ' assert b.decode('latin1') == 'ä½\xa0好' + assert b.decode('latin1' + str()) == 'ä½\xa0好' + +def test_decode_error() -> None: + try: + b'Z\xc3\xbcrich'.decode('ascii') + assert False + except UnicodeDecodeError: + pass + try: + b'Z\xc3\xbcrich'.decode('ascii' + str()) + assert False + except UnicodeDecodeError: + pass + try: + b'Z\xc3y'.decode('utf8') + assert False + except UnicodeDecodeError: + pass + try: + b'Z\xc3y'.decode('utf8' + str()) + assert False + except UnicodeDecodeError: + pass + +def test_decode_bytearray() -> None: + b: bytes = bytearray(b'foo\x00bar') + assert b.decode() == 'foo\x00bar' + assert b.decode('utf-8') == 'foo\x00bar' + assert b.decode('latin-1') == 'foo\x00bar' + assert b.decode('ascii') == 'foo\x00bar' + assert b.decode('utf-8' + str()) == 'foo\x00bar' + assert b.decode('latin-1' + str()) == 'foo\x00bar' + assert b.decode('ascii' + str()) == 'foo\x00bar' + b2: bytes = bytearray(b'foo\x00bar\xbe') + assert b2.decode('latin-1') == 'foo\x00bar\xbe' + with assertRaises(UnicodeDecodeError): + b2.decode('ascii') + with assertRaises(UnicodeDecodeError): + b2.decode('ascii' + str()) + with assertRaises(UnicodeDecodeError): + b2.decode('utf-8') + with assertRaises(UnicodeDecodeError): + b2.decode('utf-8' + str()) + b3: bytes = bytearray(b'Z\xc3\xbcrich') + assert b3.decode("utf-8") == 'Zürich' + +def test_invalid_encoding() -> None: + try: + b"foo".decode("ut-f-8") + assert False + except Exception as e: + assert repr(e).startswith("LookupError") + try: + encoding = "ut-f-8" + b"foo".decode(encoding) + assert False + except Exception as e: + assert repr(e).startswith("LookupError") [case testEncode] from testutil import assertRaises @@ -639,3 +918,188 @@ 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𒍅'" + +[case testStrip] +def test_all_strips_default() -> None: + s = " a1\t" + assert s.lstrip() == "a1\t" + assert s.strip() == "a1" + assert s.rstrip() == " a1" +def test_all_strips() -> None: + s = "xxb2yy" + assert s.lstrip("xy") == "b2yy" + assert s.strip("xy") == "b2" + assert s.rstrip("xy") == "xxb2" +def test_unicode_whitespace() -> None: + assert "\u200A\u000D\u2009\u2020\u000Dtt\u0085\u000A".strip() == "\u2020\u000Dtt" +def test_unicode_range() -> None: + assert "\u2029 \U00107581 ".lstrip() == "\U00107581 " + assert "\u2029 \U0010AAAA\U00104444B\u205F ".strip() == "\U0010AAAA\U00104444B" + assert " \u3000\u205F ".strip() == "" + assert "\u2029 \U00102865\u205F ".rstrip() == "\u2029 \U00102865" + +[case testCount] +# mypy: disable-error-code="attr-defined" +def test_count() -> None: + string = "abcbcb" + assert string.count("a") == 1 + assert string.count("b") == 3 + assert string.count("c") == 2 +def test_count_start() -> None: + string = "abcbcb" + assert string.count("a", 2) == string.count("a", -4) == 0, (string.count("a", 2), string.count("a", -4)) + assert string.count("b", 2) == string.count("b", -4) == 2, (string.count("b", 2), string.count("b", -4)) + assert string.count("c", 2) == string.count("c", -4) == 2, (string.count("c", 2), string.count("c", -4)) + # out of bounds + assert string.count("a", 8) == 0 + assert string.count("a", -8) == 1 + assert string.count("b", 8) == 0 + assert string.count("b", -8) == 3 + assert string.count("c", 8) == 0 + assert string.count("c", -8) == 2 +def test_count_start_end() -> None: + string = "abcbcb" + assert string.count("a", 0, 4) == 1, string.count("a", 0, 4) + assert string.count("b", 0, 4) == 2, string.count("b", 0, 4) + assert string.count("c", 0, 4) == 1, string.count("c", 0, 4) +def test_count_multi() -> None: + string = "aaabbbcccbbbcccbbb" + assert string.count("aaa") == 1, string.count("aaa") + assert string.count("bbb") == 3, string.count("bbb") + assert string.count("ccc") == 2, string.count("ccc") +def test_count_multi_start() -> None: + string = "aaabbbcccbbbcccbbb" + assert string.count("aaa", 6) == string.count("aaa", -12) == 0, (string.count("aaa", 6), string.count("aaa", -12)) + assert string.count("bbb", 6) == string.count("bbb", -12) == 2, (string.count("bbb", 6), string.count("bbb", -12)) + assert string.count("ccc", 6) == string.count("ccc", -12) == 2, (string.count("ccc", 6), string.count("ccc", -12)) + # out of bounds + assert string.count("aaa", 20) == 0 + assert string.count("aaa", -20) == 1 + assert string.count("bbb", 20) == 0 + assert string.count("bbb", -20) == 3 + assert string.count("ccc", 20) == 0 + assert string.count("ccc", -20) == 2 +def test_count_multi_start_end() -> None: + string = "aaabbbcccbbbcccbbb" + assert string.count("aaa", 0, 12) == 1, string.count("aaa", 0, 12) + assert string.count("bbb", 0, 12) == 2, string.count("bbb", 0, 12) + assert string.count("ccc", 0, 12) == 1, string.count("ccc", 0, 12) +def test_count_emoji() -> None: + string = "😴🚀ñ🚀ñ🚀" + assert string.count("😴") == 1, string.count("😴") + assert string.count("🚀") == 3, string.count("🚀") + assert string.count("ñ") == 2, string.count("ñ") +def test_count_start_emoji() -> None: + string = "😴🚀ñ🚀ñ🚀" + assert string.count("😴", 2) == string.count("😴", -4) == 0, (string.count("😴", 2), string.count("😴", -4)) + assert string.count("🚀", 2) == string.count("🚀", -4) == 2, (string.count("🚀", 2), string.count("🚀", -4)) + assert string.count("ñ", 2) == string.count("ñ", -4) == 2, (string.count("ñ", 2), string.count("ñ", -4)) + # Out of bounds + assert string.count("😴", 8) == 0, string.count("😴", 8) + assert string.count("😴", -8) == 1, string.count("😴", -8) + assert string.count("🚀", 8) == 0, string.count("🚀", 8) + assert string.count("🚀", -8) == 3, string.count("🚀", -8) + assert string.count("ñ", 8) == 0, string.count("ñ", 8) + assert string.count("ñ", -8) == 2, string.count("ñ", -8) +def test_count_start_end_emoji() -> None: + string = "😴🚀ñ🚀ñ🚀" + assert string.count("😴", 0, 4) == 1, string.count("😴", 0, 4) + assert string.count("🚀", 0, 4) == 2, string.count("🚀", 0, 4) + assert string.count("ñ", 0, 4) == 1, string.count("ñ", 0, 4) +def test_count_multi_emoji() -> None: + string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀" + assert string.count("😴😴😴") == 1, string.count("😴😴😴") + assert string.count("🚀🚀🚀") == 3, string.count("🚀🚀🚀") + assert string.count("ñññ") == 2, string.count("ñññ") +def test_count_multi_start_emoji() -> None: + string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀" + assert string.count("😴😴😴", 6) == string.count("😴😴😴", -12) == 0, (string.count("😴😴😴", 6), string.count("😴😴😴", -12)) + assert string.count("🚀🚀🚀", 6) == string.count("🚀🚀🚀", -12) == 2, (string.count("🚀🚀🚀", 6), string.count("🚀🚀🚀", -12)) + assert string.count("ñññ", 6) == string.count("ñññ", -12) == 2, (string.count("ñññ", 6), string.count("ñññ", -12)) + # Out of bounds + assert string.count("😴😴😴", 20) == 0, string.count("😴😴😴", 20) + assert string.count("😴😴😴", -20) == 1, string.count("😴😴😴", -20) + assert string.count("🚀🚀🚀", 20) == 0, string.count("🚀🚀🚀", 20) + assert string.count("🚀🚀🚀", -20) == 3, string.count("🚀🚀🚀", -20) + assert string.count("ñññ", 20) == 0, string.count("ñññ", 20) + assert string.count("ñññ", -20) == 2, string.count("ñññ", -20) +def test_count_multi_start_end_emoji() -> None: + string = "😴😴😴🚀🚀🚀ñññ🚀🚀🚀ñññ🚀🚀🚀" + assert string.count("😴😴😴", 0, 12) == 1, string.count("😴😴😴", 0, 12) + assert string.count("🚀🚀🚀", 0, 12) == 2, string.count("🚀🚀🚀", 0, 12) + assert string.count("ñññ", 0, 12) == 1, string.count("ñññ", 0, 12) + +[case testIsInstance] +from copysubclass import subc +from typing import Any +def test_built_in() -> None: + s: Any = str() + assert isinstance(s, str) + assert isinstance(s + "test", str) + assert isinstance(s + "ñññ", str) + assert isinstance(subc(), str) + assert isinstance(subc("test"), str) + assert isinstance(subc("ñññ"), str) + + assert not isinstance(set(), str) + assert not isinstance((), str) + assert not isinstance(('a','b'), str) + assert not isinstance({'a','b'}, str) + assert not isinstance(int() + 1, str) + assert not isinstance(['a','b'], str) + +def test_user_defined() -> None: + from userdefinedstr import str + + s: Any = "str" + assert isinstance(str(), str) + assert not isinstance(s, str) + +[file copysubclass.py] +from typing import Any +class subc(str): + pass + +[file userdefinedstr.py] +class str: + pass + +[case testStrOptionalEquality] +from __future__ import annotations + +def eq_s_opt_s_opt(x: str | None, y: str | None) -> bool: + return x == y + +def ne_s_opt_s_opt(x: str | None, y: str | None) -> bool: + return x != y + +def test_optional_eq() -> None: + s = 'x' + assert eq_s_opt_s_opt(s, s) + assert eq_s_opt_s_opt(s + str(int()), s + str(int())) + assert eq_s_opt_s_opt(None, None) + + assert not eq_s_opt_s_opt('x', 'y') + assert not eq_s_opt_s_opt('y', 'x') + assert not eq_s_opt_s_opt(None, 'x') + assert not eq_s_opt_s_opt('x', None) + +def test_optional_ne() -> None: + s = 'x' + assert not ne_s_opt_s_opt(s, s) + assert not ne_s_opt_s_opt(s + str(int()), s+ str(int())) + assert not ne_s_opt_s_opt(None, None) + + assert ne_s_opt_s_opt('x', 'y') + assert ne_s_opt_s_opt('y', 'x') + assert ne_s_opt_s_opt(None, 'x') + assert ne_s_opt_s_opt('x', None) diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test index 0851c15e57fdb..e2e8358bb43e2 100644 --- a/mypyc/test-data/run-tuples.test +++ b/mypyc/test-data/run-tuples.test @@ -97,8 +97,7 @@ assert f(Sub(3, 2)) == 3 -- Ref: https://github.com/mypyc/mypyc/issues/924 [case testNamedTupleClassSyntax] -from typing import Dict, List, NamedTuple, Optional, Tuple, Union -from typing_extensions import final +from typing import Dict, List, NamedTuple, Optional, Tuple, Union, final class FuncIR: pass @@ -128,16 +127,37 @@ class Inextensible(NamedTuple): x: int [file driver.py] -from typing import ForwardRef, Optional +import sys +from typing import Optional from native import ClassIR, FuncIR, Record +HAVE_TEST = False +if sys.version_info >= (3, 14): + try: + from test.support import EqualToForwardRef + type_forward_ref = EqualToForwardRef + HAVE_TEST = True + except ImportError as e: + # catch the case of a pymanager installed Python + # without the test module. It is excluded by default + # on Windows. + msg = 'Missing "test" module.' + if sys.platform == "win32": + msg += (' Please install a version of Python with the test module.' + ' If you are using pymanager, try running pymanager install --force PythonTest\\') + raise ImportError(msg) from e + +if not HAVE_TEST: + from typing import ForwardRef + type_forward_ref = ForwardRef + assert Record.__annotations__ == { 'st_mtime': float, 'st_size': int, 'is_borrowed': bool, 'hash': str, 'python_path': tuple, - 'type': ForwardRef('ClassIR'), + 'type': type_forward_ref('ClassIR'), 'method': FuncIR, 'shadow_method': type, 'classes': dict, @@ -147,8 +167,8 @@ assert Record.__annotations__ == { }, Record.__annotations__ [case testTupleOps] -from typing import Tuple, List, Any, Optional -from typing_extensions import Final +from typing import Tuple, Final, List, Any, Optional, cast +from testutil import assertRaises def f() -> Tuple[()]: return () @@ -196,6 +216,22 @@ def f7(x: List[Tuple[int, int]]) -> int: def test_unbox_tuple() -> None: assert f7([(5, 6)]) == 11 +def test_comparison() -> None: + assert ('x','y') == ('x','y') + assert not(('x','y') != ('x','y')) + + assert ('x','y') != ('x','y',1) + assert not(('x','y') == ('x','y',1)) + + assert ('x','y',1) != ('x','y') + assert not(('x','y',1) == ('x','y')) + + assert ('x','y') != () + assert not(('x','y') == ()) + + assert () != ('x','y') + assert not(() == ('x','y')) + # Test that order is irrelevant to unions. Really I only care that this builds. class A: @@ -234,6 +270,11 @@ def test_slicing() -> None: def f8(val: int) -> bool: return val % 2 == 0 +abc: Final = "abc" + +def known_length() -> tuple[str, ...]: + return tuple(str(x) for x in [*abc, *"def", *b"ghi", ("j", "k"), *("l", "m", "n")]) + def test_sequence_generator() -> None: source_list = [1, 2, 3] a = tuple(f8(x) for x in source_list) @@ -251,8 +292,140 @@ def test_sequence_generator() -> None: b = tuple('s:' + x for x in source_str) assert b == ('s:a', 's:b', 's:b', 's:c') + assert known_length() == ('a', 'b', 'c', 'd', 'e', 'f', '103', '104', '105', "('j', 'k')", 'l', 'm', 'n') + TUPLE: Final[Tuple[str, ...]] = ('x', 'y') def test_final_boxed_tuple() -> None: t = TUPLE assert t == ('x', 'y') + assert 'x' in TUPLE + assert 'y' in TUPLE + b: object = 'z' in TUPLE + assert not b + assert 'z' not in TUPLE + b2: object = 'x' not in TUPLE + assert not b2 + b3: object = 'y' not in TUPLE + assert not b3 + +TUP2: Final = ('x', 'y') +TUP1: Final = ('x',) +TUP0: Final = () + +def test_final_tuple_in() -> None: + assert 'x' + str() in TUP2 + assert 'y' + str() in TUP2 + b: object = 'z' + str() in TUP2 + assert not b + + assert 'x' + str() in TUP1 + b2: object = 'y' in TUP1 + assert not b2 + + b3: object = 'x' in TUP0 + assert not b3 + +def test_final_tuple_not_in() -> None: + assert 'z' + str() not in TUP2 + b: object = 'x' + str() not in TUP2 + assert not b + b2: object = 'y' + str() not in TUP2 + assert not b2 + + assert 'y' + str() not in TUP1 + b3: object = 'x' not in TUP1 + assert not b2 + + assert 'x' not in TUP0 + +log = [] + +def f_a() -> str: + log.append('f_a') + return 'a' + +def f_a2() -> str: + log.append('f_a2') + return 'a' + +def f_b() -> str: + log.append('f_b') + return 'b' + +def f_c() -> str: + log.append('f_c') + return 'c' + +def test_tuple_in_order_of_evaluation() -> None: + log.clear() + assert f_a() in (f_b(), f_a2()) + assert log ==["f_a", "f_b", "f_a2"] + + log.clear() + assert f_a() not in (f_b(), f_c()) + assert log ==["f_a", "f_b", "f_c"] + + log.clear() + assert f_a() in (f_b(), f_a2(), f_c()) + assert log ==["f_a", "f_b", "f_a2", "f_c"] + +def f_t() -> tuple[str, ...]: + log.append('f_t') + return ('x', 'a') + +def test_tuple_in_non_specialized() -> None: + log.clear() + assert f_a() in f_t() + assert log == ["f_a", "f_t"] + + log.clear() + assert f_b() not in f_t() + assert log == ["f_b", "f_t"] + +def test_add() -> None: + res = (1, 2, 3, 4) + assert (1, 2) + (3, 4) == res + with assertRaises(TypeError, 'can only concatenate tuple (not "list") to tuple'): + assert (1, 2) + cast(Any, [3, 4]) == res + +def multiply(a: Tuple[Any, ...], b: int) -> Tuple[Any, ...]: + return a * b + +def test_multiply() -> None: + res = (1, 1, 1) + assert (1,) * 3 == res + assert 3 * (1,) == res + assert multiply((1,), 3) == res + +[case testIsInstance] +from copysubclass import subc +def test_built_in() -> None: + assert isinstance((), tuple) + assert isinstance((1, 2), tuple) + assert isinstance(('a', 'b', 'c'), tuple) + assert isinstance(subc(()), tuple) + assert isinstance(subc((1, 2)), tuple) + assert isinstance(subc(('a', 'b', 'c')), tuple) + + assert not isinstance(set(), tuple) + assert not isinstance({}, tuple) + assert not isinstance([1,2,3], tuple) + assert not isinstance({'a','b'}, tuple) + assert not isinstance(int() + 1, tuple) + assert not isinstance(str() + 'a', tuple) + +def test_user_defined() -> None: + from userdefinedtuple import tuple + + assert isinstance(tuple(), tuple) + assert not isinstance((1, tuple()), tuple) + +[file copysubclass.py] +from typing import Any +class subc(tuple[Any]): + pass + +[file userdefinedtuple.py] +class tuple: + pass diff --git a/mypyc/test-data/run-u8.test b/mypyc/test-data/run-u8.test index cddb031e33524..c8580f05e31c7 100644 --- a/mypyc/test-data/run-u8.test +++ b/mypyc/test-data/run-u8.test @@ -1,8 +1,7 @@ [case testU8BasicOps] -from typing import Any, Tuple +from typing import Any, Final, Tuple from mypy_extensions import u8, i16, i32, i64 -from typing_extensions import Final from testutil import assertRaises diff --git a/mypyc/test-data/run-weakref.test b/mypyc/test-data/run-weakref.test new file mode 100644 index 0000000000000..0a0e180d635d7 --- /dev/null +++ b/mypyc/test-data/run-weakref.test @@ -0,0 +1,52 @@ +# Test cases for weakrefs (compile and run) + +[case testWeakrefRef] +# mypy: disable-error-code="union-attr" +from weakref import proxy, ref +from mypy_extensions import mypyc_attr +from testutil import assertRaises +from typing import Optional + +@mypyc_attr(native_class=False) +class Object: + """some random weakreffable object""" + def some_meth(self) -> int: + return 1 + +_callback_called_cache = {"ref": False, "proxy": False} + +def test_weakref_ref() -> None: + obj: Optional[Object] = Object() + r = ref(obj) + assert r() is obj + obj = None + assert r() is None, r() + +def test_weakref_ref_with_callback() -> None: + obj: Optional[Object] = Object() + r = ref(obj, lambda x: _callback_called_cache.__setitem__("ref", True)) + assert r() is obj + obj = None + assert r() is None, r() + assert _callback_called_cache["ref"] is True + +def test_weakref_proxy() -> None: + obj: Optional[Object] = Object() + p = proxy(obj) + assert obj.some_meth() == 1 + assert p.some_meth() == 1 + obj.some_meth() + obj = None + with assertRaises(ReferenceError): + p.some_meth() + +def test_weakref_proxy_with_callback() -> None: + obj: Optional[Object] = Object() + p = proxy(obj, lambda x: _callback_called_cache.__setitem__("proxy", True)) + assert obj.some_meth() == 1 + assert p.some_meth() == 1 + obj.some_meth() + obj = None + with assertRaises(ReferenceError): + p.some_meth() + assert _callback_called_cache["proxy"] is True diff --git a/mypyc/test/test_alwaysdefined.py b/mypyc/test/test_alwaysdefined.py index d6c4214ba6a2b..9f1487a89bfa1 100644 --- a/mypyc/test/test_alwaysdefined.py +++ b/mypyc/test/test_alwaysdefined.py @@ -31,7 +31,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: return with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): try: - ir = build_ir_for_single_file2(testcase.input, options) + ir = build_ir_for_single_file2(testcase.input, options)[0] except CompileError as e: actual = e.messages else: diff --git a/mypyc/test/test_annotate.py b/mypyc/test/test_annotate.py new file mode 100644 index 0000000000000..4a9a2c1a1b93e --- /dev/null +++ b/mypyc/test/test_annotate.py @@ -0,0 +1,71 @@ +"""Test cases for annotating source code to highlight inefficiencies.""" + +from __future__ import annotations + +import os.path + +from mypy.errors import CompileError +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase +from mypyc.annotate import generate_annotations, get_max_prio +from mypyc.ir.pprint import format_func +from mypyc.test.testutil import ( + ICODE_GEN_BUILTINS, + MypycDataSuite, + assert_test_output, + build_ir_for_single_file2, + infer_ir_build_options_from_test_name, + remove_comment_lines, + use_custom_builtins, +) + +files = ["annotate-basic.test"] + + +class TestReport(MypycDataSuite): + files = files + base_path = test_temp_dir + optional_out = True + + def run_case(self, testcase: DataDrivenTestCase) -> None: + """Perform a runtime checking transformation test case.""" + options = infer_ir_build_options_from_test_name(testcase.name) + if options is None: + # Skipped test case + return + with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): + expected_output = remove_comment_lines(testcase.output) + + # Parse "# A: " comments. + for i, line in enumerate(testcase.input): + if "# A:" in line: + msg = line.rpartition("# A:")[2].strip() + expected_output.append(f"main:{i + 1}: {msg}") + + ir = None + try: + ir, tree, type_map, mapper = build_ir_for_single_file2(testcase.input, options) + except CompileError as e: + actual = e.messages + else: + annotations = generate_annotations("native.py", tree, ir, type_map, mapper) + actual = [] + for line_num, line_anns in sorted( + annotations.annotations.items(), key=lambda it: it[0] + ): + anns = get_max_prio(line_anns) + str_anns = [a.message for a in anns] + s = " ".join(str_anns) + actual.append(f"main:{line_num}: {s}") + + try: + assert_test_output(testcase, actual, "Invalid source code output", expected_output) + except BaseException: + if ir: + print("Generated IR:\n") + for fn in ir.functions: + if fn.name == "__top_level__": + continue + for s in format_func(fn): + print(s) + raise diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index f2af41c22ea96..ec9e2c4cf4504 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -7,9 +7,7 @@ import re import unittest -from mypyc.ir.ops import PrimitiveDescription from mypyc.primitives import registry -from mypyc.primitives.registry import CFunctionDescription class TestHeaderInclusion(unittest.TestCase): @@ -26,21 +24,13 @@ 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] for op in ops: if op.c_function_name is not None: check_name(op.c_function_name) diff --git a/mypyc/test/test_emit.py b/mypyc/test/test_emit.py index e4ace3ec01f02..1baed3964299e 100644 --- a/mypyc/test/test_emit.py +++ b/mypyc/test/test_emit.py @@ -3,8 +3,21 @@ import unittest from mypyc.codegen.emit import Emitter, EmitterContext +from mypyc.common import HAVE_IMMORTAL +from mypyc.ir.class_ir import ClassIR from mypyc.ir.ops import BasicBlock, Register, Value -from mypyc.ir.rtypes import RTuple, bool_rprimitive, int_rprimitive, str_rprimitive +from mypyc.ir.rtypes import ( + RInstance, + RTuple, + RUnion, + bool_rprimitive, + int_rprimitive, + list_rprimitive, + none_rprimitive, + object_rprimitive, + str_rprimitive, +) +from mypyc.irbuild.vtable import compute_vtable from mypyc.namegen import NameGenerator @@ -12,10 +25,15 @@ class TestEmitter(unittest.TestCase): def setUp(self) -> None: self.n = Register(int_rprimitive, "n") self.context = EmitterContext(NameGenerator([["mod"]])) + self.emitter = Emitter(self.context, {}) + + ir = ClassIR("A", "mod") + compute_vtable(ir) + ir.mro = [ir] + self.instance_a = RInstance(ir) def test_label(self) -> None: - emitter = Emitter(self.context, {}) - assert emitter.label(BasicBlock(4)) == "CPyL4" + assert self.emitter.label(BasicBlock(4)) == "CPyL4" def test_reg(self) -> None: names: dict[Value, str] = {self.n: "n"} @@ -23,17 +41,16 @@ def test_reg(self) -> None: assert emitter.reg(self.n) == "cpy_r_n" def test_object_annotation(self) -> None: - emitter = Emitter(self.context, {}) - assert emitter.object_annotation("hello, world", "line;") == " /* 'hello, world' */" + assert self.emitter.object_annotation("hello, world", "line;") == " /* 'hello, world' */" assert ( - emitter.object_annotation(list(range(30)), "line;") + self.emitter.object_annotation(list(range(30)), "line;") == """\ /* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] */""" ) def test_emit_line(self) -> None: - emitter = Emitter(self.context, {}) + emitter = self.emitter emitter.emit_line("line;") emitter.emit_line("a {") emitter.emit_line("f();") @@ -51,13 +68,13 @@ def test_emit_line(self) -> None: ) def test_emit_undefined_value_for_simple_type(self) -> None: - emitter = Emitter(self.context, {}) + emitter = self.emitter assert emitter.c_undefined_value(int_rprimitive) == "CPY_INT_TAG" assert emitter.c_undefined_value(str_rprimitive) == "NULL" assert emitter.c_undefined_value(bool_rprimitive) == "2" def test_emit_undefined_value_for_tuple(self) -> None: - emitter = Emitter(self.context, {}) + emitter = self.emitter assert ( emitter.c_undefined_value(RTuple([str_rprimitive, int_rprimitive, bool_rprimitive])) == "(tuple_T3OIC) { NULL, CPY_INT_TAG, 2 }" @@ -67,3 +84,87 @@ def test_emit_undefined_value_for_tuple(self) -> None: emitter.c_undefined_value(RTuple([RTuple([str_rprimitive]), bool_rprimitive])) == "(tuple_T2T1OC) { { NULL }, 2 }" ) + + def test_emit_inc_ref_object(self) -> None: + self.emitter.emit_inc_ref("x", object_rprimitive) + self.assert_output("CPy_INCREF(x);\n") + + def test_emit_inc_ref_int(self) -> None: + self.emitter.emit_inc_ref("x", int_rprimitive) + self.assert_output("CPyTagged_INCREF(x);\n") + + def test_emit_inc_ref_rare(self) -> None: + self.emitter.emit_inc_ref("x", object_rprimitive, rare=True) + self.assert_output("CPy_INCREF(x);\n") + self.emitter.emit_inc_ref("x", int_rprimitive, rare=True) + self.assert_output("CPyTagged_IncRef(x);\n") + + def test_emit_inc_ref_list(self) -> None: + self.emitter.emit_inc_ref("x", list_rprimitive) + if HAVE_IMMORTAL: + self.assert_output("CPy_INCREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_INCREF(x);\n") + + def test_emit_inc_ref_instance(self) -> None: + self.emitter.emit_inc_ref("x", self.instance_a) + if HAVE_IMMORTAL: + self.assert_output("CPy_INCREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_INCREF(x);\n") + + def test_emit_inc_ref_optional(self) -> None: + optional = RUnion([self.instance_a, none_rprimitive]) + self.emitter.emit_inc_ref("o", optional) + self.assert_output("CPy_INCREF(o);\n") + + def test_emit_dec_ref_object(self) -> None: + self.emitter.emit_dec_ref("x", object_rprimitive) + self.assert_output("CPy_DECREF(x);\n") + self.emitter.emit_dec_ref("x", object_rprimitive, is_xdec=True) + self.assert_output("CPy_XDECREF(x);\n") + + def test_emit_dec_ref_int(self) -> None: + self.emitter.emit_dec_ref("x", int_rprimitive) + self.assert_output("CPyTagged_DECREF(x);\n") + self.emitter.emit_dec_ref("x", int_rprimitive, is_xdec=True) + self.assert_output("CPyTagged_XDECREF(x);\n") + + def test_emit_dec_ref_rare(self) -> None: + self.emitter.emit_dec_ref("x", object_rprimitive, rare=True) + self.assert_output("CPy_DecRef(x);\n") + self.emitter.emit_dec_ref("x", int_rprimitive, rare=True) + self.assert_output("CPyTagged_DecRef(x);\n") + + def test_emit_dec_ref_list(self) -> None: + self.emitter.emit_dec_ref("x", list_rprimitive) + if HAVE_IMMORTAL: + self.assert_output("CPy_DECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_DECREF(x);\n") + self.emitter.emit_dec_ref("x", list_rprimitive, is_xdec=True) + if HAVE_IMMORTAL: + self.assert_output("CPy_XDECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_XDECREF(x);\n") + + def test_emit_dec_ref_instance(self) -> None: + self.emitter.emit_dec_ref("x", self.instance_a) + if HAVE_IMMORTAL: + self.assert_output("CPy_DECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_DECREF(x);\n") + self.emitter.emit_dec_ref("x", self.instance_a, is_xdec=True) + if HAVE_IMMORTAL: + self.assert_output("CPy_XDECREF_NO_IMM(x);\n") + else: + self.assert_output("CPy_XDECREF(x);\n") + + def test_emit_dec_ref_optional(self) -> None: + optional = RUnion([self.instance_a, none_rprimitive]) + self.emitter.emit_dec_ref("o", optional) + self.assert_output("CPy_DECREF(o);\n") + + def assert_output(self, expected: str) -> None: + assert "".join(self.emitter.fragments) == expected + self.emitter.fragments = [] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 317427afac5a7..6382271cfe94c 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -5,7 +5,7 @@ from mypy.test.helpers import assert_string_arrays_equal from mypyc.codegen.emit import Emitter, EmitterContext from mypyc.codegen.emitfunc import FunctionEmitterVisitor, generate_native_function -from mypyc.common import PLATFORM_SIZE +from mypyc.common import HAVE_IMMORTAL, PLATFORM_SIZE from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncDecl, FuncIR, FuncSignature, RuntimeArg from mypyc.ir.ops import ( @@ -19,6 +19,7 @@ CallC, Cast, ComparisonOp, + CString, DecRef, Extend, GetAttr, @@ -28,14 +29,17 @@ Integer, IntOp, LoadAddress, + LoadLiteral, LoadMem, Op, Register, Return, SetAttr, + SetElement, SetMem, TupleGet, Unbox, + Undef, Unreachable, Value, ) @@ -48,11 +52,13 @@ RType, bool_rprimitive, c_int_rprimitive, + cstring_rprimitive, dict_rprimitive, int32_rprimitive, int64_rprimitive, int_rprimitive, list_rprimitive, + none_rprimitive, object_rprimitive, pointer_rprimitive, short_int_rprimitive, @@ -109,11 +115,18 @@ def add_local(name: str, rtype: RType) -> Register: "y": int_rprimitive, "i1": int64_rprimitive, "i2": int32_rprimitive, + "t": RTuple([object_rprimitive, object_rprimitive]), } ir.bitmap_attrs = ["i1", "i2"] compute_vtable(ir) ir.mro = [ir] self.r = add_local("r", RInstance(ir)) + self.none = add_local("none", none_rprimitive) + + self.struct_type = RStruct( + "Foo", ["b", "x", "y"], [bool_rprimitive, int32_rprimitive, int64_rprimitive] + ) + self.st = add_local("st", self.struct_type) self.context = EmitterContext(NameGenerator([["mod"]])) @@ -134,7 +147,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 +167,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 +316,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 +330,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 +366,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, @@ -414,6 +428,17 @@ def test_get_attr_with_bitmap(self) -> None: """, ) + def test_get_attr_nullable_with_tuple(self) -> None: + self.assert_emit( + GetAttr(self.r, "t", 1, allow_error_value=True), + """cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_t; + if (cpy_r_r0.f0 != NULL) { + CPy_INCREF(cpy_r_r0.f0); + CPy_INCREF(cpy_r_r0.f1); + } + """, + ) + def test_set_attr(self) -> None: self.assert_emit( SetAttr(self.r, "y", self.m, 1), @@ -492,7 +517,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 +531,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 +545,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, @@ -656,6 +681,17 @@ def test_get_element_ptr(self) -> None: GetElementPtr(self.o, r, "i64"), """cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->i64;""" ) + def test_set_element(self) -> None: + # Use compact syntax when setting the initial element of an undefined value + self.assert_emit( + SetElement(Undef(self.struct_type), "b", self.b), """cpy_r_r0.b = cpy_r_b;""" + ) + # We propagate the unchanged values in subsequent assignments + self.assert_emit( + SetElement(self.st, "x", self.i32), + """cpy_r_r0 = (Foo) { cpy_r_st.b, cpy_r_i32, cpy_r_st.y };""", + ) + def test_load_address(self) -> None: self.assert_emit( LoadAddress(object_rprimitive, "PyDict_Type"), @@ -804,9 +840,49 @@ def test_extend(self) -> None: Extend(a, int_rprimitive, signed=False), """cpy_r_r0 = (uint32_t)cpy_r_a;""" ) + def test_inc_ref_none(self) -> None: + b = Box(self.none) + self.assert_emit([b, IncRef(b)], "" if HAVE_IMMORTAL else "CPy_INCREF(cpy_r_r0);") + + def test_inc_ref_bool(self) -> None: + b = Box(self.b) + self.assert_emit([b, IncRef(b)], "" if HAVE_IMMORTAL else "CPy_INCREF(cpy_r_r0);") + + def test_inc_ref_int_literal(self) -> None: + for x in -5, 0, 1, 5, 255, 256: + b = LoadLiteral(x, object_rprimitive) + self.assert_emit([b, IncRef(b)], "" if HAVE_IMMORTAL else "CPy_INCREF(cpy_r_r0);") + for x in -1123355, -6, 257, 123235345: + b = LoadLiteral(x, object_rprimitive) + self.assert_emit([b, IncRef(b)], "CPy_INCREF(cpy_r_r0);") + + def test_c_string(self) -> None: + s = Register(cstring_rprimitive, "s") + self.assert_emit(Assign(s, CString(b"foo")), """cpy_r_s = "foo";""") + self.assert_emit(Assign(s, CString(b'foo "o')), r"""cpy_r_s = "foo \"o";""") + self.assert_emit(Assign(s, CString(b"\x00")), r"""cpy_r_s = "\x00";""") + self.assert_emit(Assign(s, CString(b"\\")), r"""cpy_r_s = "\\";""") + for i in range(256): + b = bytes([i]) + if b == b"\n": + target = "\\n" + elif b == b"\r": + target = "\\r" + elif b == b"\t": + target = "\\t" + elif b == b'"': + target = '\\"' + elif b == b"\\": + target = "\\\\" + elif i < 32 or i >= 127: + target = "\\x%.2x" % i + else: + target = b.decode("ascii") + self.assert_emit(Assign(s, CString(b)), f'cpy_r_s = "{target}";') + def assert_emit( self, - op: Op, + op: Op | list[Op], expected: str, next_block: BasicBlock | None = None, *, @@ -815,7 +891,11 @@ def assert_emit( skip_next: bool = False, ) -> None: block = BasicBlock(0) - block.ops.append(op) + if isinstance(op, Op): + block.ops.append(op) + else: + block.ops.extend(op) + op = op[-1] value_names = generate_names_for_ir(self.registers, [block]) emitter = Emitter(self.context, value_names) declarations = Emitter(self.context, value_names) diff --git a/mypyc/test/test_external.py b/mypyc/test/test_external.py index 22eb8019133c8..a416cf2ee1300 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. @@ -32,6 +34,7 @@ def test_c_unit_test(self) -> None: "build_ext", f"--build-lib={tmpdir}", f"--build-temp={tmpdir}", + "--run-capi-tests", ], env=env, cwd=os.path.join(base_dir, "mypyc", "lib-rt"), diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 5b3f678d8f173..7c248640246d1 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -8,7 +8,7 @@ from mypy.errors import CompileError from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase -from mypyc.common import TOP_LEVEL_NAME +from mypyc.common import IS_FREE_THREADED, TOP_LEVEL_NAME from mypyc.ir.pprint import format_func from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, @@ -32,6 +32,7 @@ "irbuild-str.test", "irbuild-bytes.test", "irbuild-float.test", + "irbuild-frozenset.test", "irbuild-statements.test", "irbuild-nested.test", "irbuild-classes.test", @@ -52,6 +53,8 @@ "irbuild-constant-fold.test", "irbuild-glue-methods.test", "irbuild-math.test", + "irbuild-weakref.test", + "irbuild-base64.test", ] if sys.version_info >= (3, 10): @@ -69,6 +72,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: if options is None: # Skipped test case return + if "_withgil" in testcase.name and IS_FREE_THREADED: + # Test case should only run on a non-free-threaded build. + return with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) expected_output = replace_word_size(expected_output) diff --git a/mypyc/test/test_lowering.py b/mypyc/test/test_lowering.py index 50a9a73908552..86745b6d390ba 100644 --- a/mypyc/test/test_lowering.py +++ b/mypyc/test/test_lowering.py @@ -15,6 +15,7 @@ MypycDataSuite, assert_test_output, build_ir_for_single_file, + infer_ir_build_options_from_test_name, remove_comment_lines, replace_word_size, use_custom_builtins, @@ -31,11 +32,15 @@ class TestLowering(MypycDataSuite): base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: + options = infer_ir_build_options_from_test_name(testcase.name) + if options is None: + # Skipped test case + return with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) expected_output = replace_word_size(expected_output) try: - ir = build_ir_for_single_file(testcase.input) + ir = build_ir_for_single_file(testcase.input, options) except CompileError as e: actual = e.messages else: diff --git a/mypyc/test/test_misc.py b/mypyc/test/test_misc.py new file mode 100644 index 0000000000000..f92da2ca3fe1f --- /dev/null +++ b/mypyc/test/test_misc.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +import unittest + +from mypyc.ir.ops import BasicBlock +from mypyc.ir.pprint import format_blocks, generate_names_for_ir +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.options import CompilerOptions + + +class TestMisc(unittest.TestCase): + def test_debug_op(self) -> None: + block = BasicBlock() + builder = LowLevelIRBuilder(errors=None, options=CompilerOptions()) + builder.activate_block(block) + builder.debug_print("foo") + + names = generate_names_for_ir([], [block]) + code = format_blocks([block], names, {}) + assert code[:-1] == ["L0:", " r0 = 'foo'", " CPyDebug_PrintObject(r0)"] diff --git a/mypyc/test/test_namegen.py b/mypyc/test/test_namegen.py index 509018b4c3bde..a4688747037f2 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"]]) @@ -46,3 +52,17 @@ def test_name_generator(self) -> None: assert g.private_name("foo", "C_x_y") == "foo___C_x_y" assert g.private_name("foo", "C_x_y") == "foo___C_x_y" assert g.private_name("foo", "___") == "foo______3_" + + g = NameGenerator([["foo.zar"]]) + assert g.private_name("foo.zar", "f") == "f" + + def test_name_generator_with_separate(self) -> None: + g = NameGenerator([["foo", "foo.zar"]], separate=True) + assert g.private_name("foo", "f") == "foo___f" + assert g.private_name("foo", "C.x.y") == "foo___C___x___y" + assert g.private_name("foo.zar", "C.x.y") == "foo___zar___C___x___y" + assert g.private_name("foo", "C.x_y") == "foo___C___x_y" + assert g.private_name("foo", "___") == "foo______3_" + + g = NameGenerator([["foo.zar"]], separate=True) + assert g.private_name("foo.zar", "f") == "foo___zar___f" diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 668e5b124841e..6b63a4d546d07 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -11,18 +11,20 @@ import subprocess import sys import time -from typing import Any, Iterator +from collections.abc import Iterator +from typing import Any from mypy import build from mypy.errors import CompileError from mypy.options import Options -from mypy.test.config import test_temp_dir +from mypy.test.config import mypyc_output_dir, test_temp_dir from mypy.test.data import DataDrivenTestCase from mypy.test.helpers import assert_module_equivalence, perform_file_operations from mypyc.build import construct_groups from mypyc.codegen import emitmodule from mypyc.errors import Errors from mypyc.options import CompilerOptions +from mypyc.test.config import test_data_prefix from mypyc.test.test_serialization import check_serialization_roundtrip from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, @@ -59,14 +61,19 @@ "run-classes.test", "run-traits.test", "run-generators.test", + "run-generics.test", "run-multimodule.test", "run-bench.test", "run-mypy-sim.test", "run-dunders.test", + "run-dunders-special.test", "run-singledispatch.test", "run-attrs.test", + "run-signatures.test", + "run-weakref.test", "run-python37.test", "run-python38.test", + "run-base64.test", ] if sys.version_info >= (3, 10): @@ -80,7 +87,8 @@ setup(name='test_run_output', ext_modules=mypycify({}, separate={}, skip_cgen_input={!r}, strip_asserts=False, - multi_file={}, opt_level='{}'), + multi_file={}, opt_level='{}', install_librt={}, + experimental_features={}), ) """ @@ -140,13 +148,15 @@ 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 # by chdiring into tmp/ - with use_custom_builtins( - os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase - ), chdir_manager("tmp"): + with ( + use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase), + chdir_manager("tmp"), + ): self.run_case_inner(testcase) def run_case_inner(self, testcase: DataDrivenTestCase) -> None: @@ -229,10 +239,22 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> else False ) - groups = construct_groups(sources, separate, len(module_names) > 1) + groups = construct_groups(sources, separate, len(module_names) > 1, None) + # Use _librt_internal to test mypy-specific parts of librt (they have + # some special-casing in mypyc), for everything else use _librt suffix. + librt_internal = testcase.name.endswith("_librt_internal") + librt = testcase.name.endswith("_librt") or "_librt_" in testcase.name + # Enable experimental features (local librt build also includes experimental features) + experimental_features = testcase.name.endswith("_experimental") 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, + depends_on_librt_internal=librt_internal, + experimental_features=experimental_features, + ) result = emitmodule.parse_and_typecheck( sources=sources, options=options, @@ -241,7 +263,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> alt_lib_path=".", ) errors = Errors(options) - ir, cfiles = emitmodule.compile_modules_to_c( + ir, cfiles, _ = emitmodule.compile_modules_to_c( result, compiler_options=compiler_options, errors=errors, groups=groups ) if errors.num_errors: @@ -258,20 +280,32 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> check_serialization_roundtrip(ir) opt_level = int(os.environ.get("MYPYC_OPT_LEVEL", 0)) - debug_level = int(os.environ.get("MYPYC_DEBUG_LEVEL", 0)) setup_file = os.path.abspath(os.path.join(WORKDIR, "setup.py")) # We pass the C file information to the build script via setup.py unfortunately with open(setup_file, "w", encoding="utf-8") as f: f.write( setup_format.format( - module_paths, separate, cfiles, self.multi_file, opt_level, debug_level + module_paths, + separate, + cfiles, + self.multi_file, + opt_level, + librt, + experimental_features, ) ) + if librt: + # This hack forces Python to prefer the local "installation". + os.makedirs("librt", exist_ok=True) + with open(os.path.join("librt", "__init__.py"), "a"): + pass + if not run_setup(setup_file, ["build_ext", "--inplace"]): if testcase.config.getoption("--mypyc-showc"): show_c(cfiles) + copy_output_files(mypyc_output_dir) assert False, "Compilation failed" # Assert that an output file got created @@ -283,9 +317,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> # No driver.py provided by test case. Use the default one # (mypyc/test-data/driver/driver.py) that calls each # function named test_*. - default_driver = os.path.join( - os.path.dirname(__file__), "..", "test-data", "driver", "driver.py" - ) + default_driver = os.path.join(test_data_prefix, "driver", "driver.py") shutil.copy(default_driver, driver_path) env = os.environ.copy() env["MYPYC_RUN_BENCH"] = "1" if bench else "0" @@ -314,13 +346,30 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> # TODO: testDecorators1 hangs on 3.12, remove this once fixed proc.wait(timeout=30) output = proc.communicate()[0].decode("utf8") + output = output.replace(f' File "{os.getcwd()}{os.sep}', ' File "') outlines = output.splitlines() if testcase.config.getoption("--mypyc-showc"): show_c(cfiles) if proc.returncode != 0: print() - print("*** Exit status: %d" % proc.returncode) + signal = proc.returncode == -11 + extra = "" + if signal: + extra = " (likely segmentation fault)" + print(f"*** Exit status: {proc.returncode}{extra}") + if signal and not sys.platform.startswith("win"): + print() + if sys.platform == "darwin": + debugger = "lldb" + else: + debugger = "gdb" + print( + f'hint: Use "pytest -n0 -s --mypyc-debug={debugger} -k " to run test in debugger' + ) + print("hint: You may need to build a debug version of Python first and use it") + print('hint: See also "Debugging Segfaults" in mypyc/doc/dev-intro.md') + copy_output_files(mypyc_output_dir) # Verify output. if bench: @@ -401,6 +450,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. @@ -426,3 +483,17 @@ def fix_native_line_number(message: str, fnam: str, delta: int) -> str: message, ) return message + + +def copy_output_files(target_dir: str) -> None: + try: + os.mkdir(target_dir) + except OSError: + # Only copy data for the first failure, to avoid excessive output in case + # many tests fail + return + + for fnam in glob.glob("build/*.[ch]"): + shutil.copy(fnam, target_dir) + + sys.stderr.write(f"\nGenerated files: {target_dir} (for first failure only)\n\n") diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 6446af3427af1..3e9abc231d9a0 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -7,14 +7,17 @@ import os.path import re import shutil -from typing import Callable, Iterator +from collections.abc import Iterator +from typing import Callable from mypy import build from mypy.errors import CompileError +from mypy.nodes import Expression, MypyFile from mypy.options import Options from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal +from mypy.types import Type from mypyc.analysis.ircheck import assert_func_ir_valid from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE from mypyc.errors import Errors @@ -92,23 +95,23 @@ def perform_test( def build_ir_for_single_file( input_lines: list[str], compiler_options: CompilerOptions | None = None ) -> list[FuncIR]: - return build_ir_for_single_file2(input_lines, compiler_options).functions + return build_ir_for_single_file2(input_lines, compiler_options)[0].functions def build_ir_for_single_file2( input_lines: list[str], compiler_options: CompilerOptions | None = None -) -> ModuleIR: +) -> tuple[ModuleIR, MypyFile, dict[Expression, Type], Mapper]: program_text = "\n".join(input_lines) # By default generate IR compatible with the earliest supported Python C API. # If a test needs more recent API features, this should be overridden. - compiler_options = compiler_options or CompilerOptions(capi_version=(3, 7)) + compiler_options = compiler_options or CompilerOptions(capi_version=(3, 9)) options = Options() options.show_traceback = True options.hide_error_codes = True options.use_builtins_fixtures = True options.strict_optional = True - options.python_version = compiler_options.python_version or (3, 6) + options.python_version = compiler_options.python_version or (3, 9) options.export_types = True options.preserve_asts = True options.allow_empty_bodies = True @@ -122,13 +125,9 @@ def build_ir_for_single_file2( raise CompileError(result.errors) errors = Errors(options) + mapper = Mapper({"__main__": None}) modules = build_ir( - [result.files["__main__"]], - result.graph, - result.types, - Mapper({"__main__": None}), - compiler_options, - errors, + [result.files["__main__"]], result.graph, result.types, mapper, compiler_options, errors ) if errors.num_errors: raise CompileError(errors.new_messages()) @@ -136,7 +135,9 @@ def build_ir_for_single_file2( module = list(modules.values())[0] for fn in module.functions: assert_func_ir_valid(fn) - return module + tree = result.graph[module.fullname].tree + assert tree is not None + return module, tree, result.types, mapper def update_testcase_output(testcase: DataDrivenTestCase, output: list[str]) -> None: @@ -272,12 +273,14 @@ def infer_ir_build_options_from_test_name(name: str) -> CompilerOptions | None: return None if "_32bit" in name and not IS_32_BIT_PLATFORM: return None - options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 7)) - # A suffix like _python3.8 is used to set the target C API version. + options = CompilerOptions(strip_asserts="StripAssert" in name, capi_version=(3, 9)) + # A suffix like _python3_9 is used to set the target C API version. m = re.search(r"_python([3-9]+)_([0-9]+)(_|\b)", name) if m: options.capi_version = (int(m.group(1)), int(m.group(2))) options.python_version = options.capi_version elif "_py" in name or "_Python" in name: assert False, f"Invalid _py* suffix (should be _pythonX_Y): {name}" + if re.search("_experimental(_|$)", name): + options.experimental_features = True return options diff --git a/mypyc/transform/flag_elimination.py b/mypyc/transform/flag_elimination.py index 605e5bc46ae4b..c78e60d47cbdc 100644 --- a/mypyc/transform/flag_elimination.py +++ b/mypyc/transform/flag_elimination.py @@ -78,10 +78,9 @@ def __init__(self, builder: LowLevelIRBuilder, branch_map: dict[Register, Branch self.branches = set(branch_map.values()) def visit_assign(self, op: Assign) -> None: - old_branch = self.branch_map.get(op.dest) - if old_branch: + if old_branch := self.branch_map.get(op.dest): # Replace assignment with a copy of the old branch, which is in a - # separate basic block. The old branch will be deletecd in visit_branch. + # separate basic block. The old branch will be deleted in visit_branch. new_branch = Branch( op.src, old_branch.true, diff --git a/mypyc/transform/ir_transform.py b/mypyc/transform/ir_transform.py index a631bd7352b58..bcb6db9b0daf5 100644 --- a/mypyc/transform/ir_transform.py +++ b/mypyc/transform/ir_transform.py @@ -39,6 +39,7 @@ RaiseStandardError, Return, SetAttr, + SetElement, SetMem, Truncate, TupleGet, @@ -119,6 +120,9 @@ def visit_unreachable(self, op: Unreachable) -> None: self.add(op) def visit_assign(self, op: Assign) -> Value | None: + if op.src in self.op_map and self.op_map[op.src] is None: + # Special case: allow removing register initialization assignments + return None return self.add(op) def visit_assign_multi(self, op: AssignMulti) -> Value | None: @@ -211,6 +215,9 @@ def visit_set_mem(self, op: SetMem) -> Value | None: def visit_get_element_ptr(self, op: GetElementPtr) -> Value | None: return self.add(op) + def visit_set_element(self, op: SetElement) -> Value | None: + return self.add(op) + def visit_load_address(self, op: LoadAddress) -> Value | None: return self.add(op) @@ -351,10 +358,13 @@ def visit_set_mem(self, op: SetMem) -> None: def visit_get_element_ptr(self, op: GetElementPtr) -> None: op.src = self.fix_op(op.src) + def visit_set_element(self, op: SetElement) -> None: + op.src = self.fix_op(op.src) + def visit_load_address(self, op: LoadAddress) -> None: if isinstance(op.src, LoadStatic): new = self.fix_op(op.src) - assert isinstance(new, LoadStatic) + assert isinstance(new, LoadStatic), new op.src = new def visit_keep_alive(self, op: KeepAlive) -> None: diff --git a/mypyc/transform/log_trace.py b/mypyc/transform/log_trace.py new file mode 100644 index 0000000000000..cec76b9b4f888 --- /dev/null +++ b/mypyc/transform/log_trace.py @@ -0,0 +1,158 @@ +"""This optional pass adds logging of various executed operations. + +Some subset of the executed operations are logged to the mypyc_trace.txt file. + +This is useful for performance analysis. For example, it's possible +to identify how frequently various primitive functions are called, +and in which code locations they are called. +""" + +from __future__ import annotations + +from typing import Final + +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.ops import ( + Box, + Call, + CallC, + Cast, + CString, + DecRef, + GetAttr, + IncRef, + LoadLiteral, + LoadStatic, + Op, + PrimitiveOp, + SetAttr, + Unbox, + Value, +) +from mypyc.ir.rtypes import none_rprimitive +from mypyc.irbuild.ll_builder import LowLevelIRBuilder +from mypyc.options import CompilerOptions +from mypyc.primitives.misc_ops import log_trace_event +from mypyc.transform.ir_transform import IRTransform + + +def insert_event_trace_logging(fn: FuncIR, options: CompilerOptions) -> None: + builder = LowLevelIRBuilder(None, options) + transform = LogTraceEventTransform(builder, fn.decl.fullname) + transform.transform_blocks(fn.blocks) + fn.blocks = builder.blocks + + +def get_load_global_name(op: CallC) -> str | None: + name = op.function_name + if name == "CPyDict_GetItem": + arg = op.args[0] + if ( + isinstance(arg, LoadStatic) + and arg.namespace == "static" + and arg.identifier == "globals" + and isinstance(op.args[1], LoadLiteral) + ): + return str(op.args[1].value) + return None + + +# These primitives perform an implicit IncRef for the return value. Only some of the most common ones +# are included, and mostly ops that could be switched to use borrowing in some contexts. +primitives_that_inc_ref: Final = { + "list_get_item_unsafe", + "CPyList_GetItemShort", + "CPyDict_GetWithNone", + "CPyList_GetItem", + "CPyDict_GetItem", + "CPyList_PopLast", +} + + +class LogTraceEventTransform(IRTransform): + def __init__(self, builder: LowLevelIRBuilder, fullname: str) -> None: + super().__init__(builder) + self.fullname = fullname.encode("utf-8") + + def visit_call(self, op: Call) -> Value: + # TODO: Use different op name when constructing an instance + return self.log(op, "call", op.fn.fullname) + + def visit_primitive_op(self, op: PrimitiveOp) -> Value: + value = self.log(op, "primitive_op", op.desc.name) + if op.desc.name in primitives_that_inc_ref: + self.log_inc_ref(value) + return value + + def visit_call_c(self, op: CallC) -> Value: + if global_name := get_load_global_name(op): + return self.log(op, "globals_dict_get_item", global_name) + + func_name = op.function_name + if func_name == "PyObject_Vectorcall" and isinstance(op.args[0], CallC): + if global_name := get_load_global_name(op.args[0]): + return self.log(op, "python_call_global", global_name) + elif func_name == "CPyObject_GetAttr" and isinstance(op.args[1], LoadLiteral): + return self.log(op, "python_get_attr", str(op.args[1].value)) + elif func_name == "PyObject_VectorcallMethod" and isinstance(op.args[0], LoadLiteral): + return self.log(op, "python_call_method", str(op.args[0].value)) + + value = self.log(op, "call_c", func_name) + if func_name in primitives_that_inc_ref: + self.log_inc_ref(value) + return value + + def visit_get_attr(self, op: GetAttr) -> Value: + value = self.log(op, "get_attr", f"{op.class_type.name}.{op.attr}") + if not op.is_borrowed and op.type.is_refcounted: + self.log_inc_ref(op) + return value + + def visit_set_attr(self, op: SetAttr) -> Value: + name = "set_attr" if not op.is_init else "set_attr_init" + return self.log(op, name, f"{op.class_type.name}.{op.attr}") + + def visit_box(self, op: Box) -> Value: + if op.src.type is none_rprimitive: + # Boxing 'None' is a very quick operation, so we don't log it. + return self.add(op) + else: + return self.log(op, "box", str(op.src.type)) + + def visit_unbox(self, op: Unbox) -> Value: + return self.log(op, "unbox", str(op.type)) + + def visit_cast(self, op: Cast) -> Value | None: + value = self.log(op, "cast", str(op.type)) + if not op.is_borrowed: + self.log_inc_ref(value) + return value + + def visit_inc_ref(self, op: IncRef) -> Value: + return self.log(op, "inc_ref", str(op.src.type)) + + def visit_dec_ref(self, op: DecRef) -> Value: + return self.log(op, "dec_ref", str(op.src.type)) + + def log_inc_ref(self, value: Value) -> None: + self.log_event("inc_ref", str(value.type), value.line) + + def log(self, op: Op, name: str, details: str) -> Value: + self.log_event(name, details, op.line) + return self.add(op) + + def log_event(self, name: str, details: str, line: int) -> None: + if line >= 0: + line_str = str(line) + else: + line_str = "" + self.builder.primitive_op( + log_trace_event, + [ + CString(self.fullname), + CString(line_str.encode("ascii")), + CString(name.encode("utf-8")), + CString(details.encode("utf-8")), + ], + line, + ) diff --git a/mypyc/transform/lower.py b/mypyc/transform/lower.py index b717657095f94..f5768242aff10 100644 --- a/mypyc/transform/lower.py +++ b/mypyc/transform/lower.py @@ -9,6 +9,8 @@ package. """ +from __future__ import annotations + from mypyc.ir.func_ir import FuncIR from mypyc.ir.ops import PrimitiveOp, Value from mypyc.irbuild.ll_builder import LowLevelIRBuilder @@ -25,7 +27,7 @@ def lower_ir(ir: FuncIR, options: CompilerOptions) -> None: class LoweringVisitor(IRTransform): - def visit_primitive_op(self, op: PrimitiveOp) -> Value: + def visit_primitive_op(self, op: PrimitiveOp) -> Value | None: # The lowering implementation functions of various primitive ops are stored # in a registry, which is populated using function decorators. The name # of op (such as "int_eq") is used as the key. diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py index f2ab438f6576b..beacb409edfbf 100644 --- a/mypyc/transform/refcount.py +++ b/mypyc/transform/refcount.py @@ -18,7 +18,7 @@ from __future__ import annotations -from typing import Dict, Iterable, Tuple +from collections.abc import Iterable from mypyc.analysis.dataflow import ( AnalysisDict, @@ -33,6 +33,7 @@ Assign, BasicBlock, Branch, + CallC, ControlOp, DecRef, Goto, @@ -43,16 +44,17 @@ Op, Register, RegisterOp, + Undef, Value, ) -Decs = Tuple[Tuple[Value, bool], ...] -Incs = Tuple[Value, ...] +Decs = tuple[tuple[Value, bool], ...] +Incs = tuple[Value, ...] # A cache of basic blocks that decrement and increment specific values # and then jump to some target block. This lets us cut down on how # much code we generate in some circumstances. -BlockCache = Dict[Tuple[BasicBlock, Decs, Incs], BasicBlock] +BlockCache = dict[tuple[BasicBlock, Decs, Incs], BasicBlock] def insert_ref_count_opcodes(ir: FuncIR) -> None: @@ -88,13 +90,15 @@ def insert_ref_count_opcodes(ir: FuncIR) -> None: def is_maybe_undefined(post_must_defined: set[Value], src: Value) -> bool: - return isinstance(src, Register) and src not in post_must_defined + return (isinstance(src, Register) and src not in post_must_defined) or ( + isinstance(src, CallC) and src.returns_null + ) def maybe_append_dec_ref( ops: list[Op], dest: Value, defined: AnalysisDict[Value], key: tuple[BasicBlock, int] ) -> None: - if dest.type.is_refcounted and not isinstance(dest, Integer): + if dest.type.is_refcounted and not isinstance(dest, (Integer, Undef)): ops.append(DecRef(dest, is_xdec=is_maybe_undefined(defined[key], dest))) @@ -127,7 +131,7 @@ def transform_block( # For assignments to registers that were already live, # decref the old value. if dest not in pre_borrow[key] and dest in pre_live[key]: - assert isinstance(op, Assign) + assert isinstance(op, Assign), op maybe_append_dec_ref(ops, dest, post_must_defined, key) # Strip KeepAlive. Its only purpose is to help with this transform. diff --git a/mypyc/transform/spill.py b/mypyc/transform/spill.py new file mode 100644 index 0000000000000..d92dd661e7eb4 --- /dev/null +++ b/mypyc/transform/spill.py @@ -0,0 +1,113 @@ +"""Insert spills for values that are live across yields.""" + +from __future__ import annotations + +from mypyc.analysis.dataflow import AnalysisResult, analyze_live_regs, get_cfg +from mypyc.common import TEMP_ATTR_NAME +from mypyc.ir.class_ir import ClassIR +from mypyc.ir.func_ir import FuncIR +from mypyc.ir.ops import ( + BasicBlock, + Branch, + DecRef, + GetAttr, + IncRef, + LoadErrorValue, + Register, + SetAttr, + Value, +) + + +def insert_spills(ir: FuncIR, env: ClassIR) -> None: + cfg = get_cfg(ir.blocks, use_yields=True) + live = analyze_live_regs(ir.blocks, cfg) + entry_live = live.before[ir.blocks[0], 0] + + entry_live = {op for op in entry_live if not (isinstance(op, Register) and op.is_arg)} + # TODO: Actually for now, no Registers at all -- we keep the manual spills + entry_live = {op for op in entry_live if not isinstance(op, Register)} + + ir.blocks = spill_regs(ir.blocks, env, entry_live, live, ir.arg_regs[0]) + + +def spill_regs( + blocks: list[BasicBlock], + env: ClassIR, + to_spill: set[Value], + live: AnalysisResult[Value], + self_reg: Register, +) -> list[BasicBlock]: + env_reg: Value + for op in blocks[0].ops: + if isinstance(op, GetAttr) and op.attr == "__mypyc_env__": + env_reg = op + break + else: + # Environment has been merged into generator object + env_reg = self_reg + + spill_locs = {} + for i, val in enumerate(to_spill): + name = f"{TEMP_ATTR_NAME}2_{i}" + env.attributes[name] = val.type + if val.type.error_overlap: + # We can safely treat as always initialized, since the type has no pointers. + # This way we also don't need to manage the defined attribute bitfield. + env._always_initialized_attrs.add(name) + spill_locs[val] = name + + for block in blocks: + ops = block.ops + block.ops = [] + + for i, op in enumerate(ops): + to_decref = [] + + if isinstance(op, IncRef) and op.src in spill_locs: + raise AssertionError("not sure what to do with an incref of a spill...") + if isinstance(op, DecRef) and op.src in spill_locs: + # When we decref a spilled value, we turn that into + # NULLing out the attribute, but only if the spilled + # value is not live *when we include yields in the + # CFG*. (The original decrefs are computed without that.) + # + # We also skip a decref is the env register is not + # live. That should only happen when an exception is + # being raised, so everything should be handled there. + if op.src not in live.after[block, i] and env_reg in live.after[block, i]: + # Skip the DecRef but null out the spilled location + null = LoadErrorValue(op.src.type) + block.ops.extend([null, SetAttr(env_reg, spill_locs[op.src], null, op.line)]) + continue + + if ( + any(src in spill_locs for src in op.sources()) + # N.B: IS_ERROR should be before a spill happens + # XXX: but could we have a regular branch? + and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR) + ): + new_sources: list[Value] = [] + stolen = op.stolen() + for src in op.sources(): + if src in spill_locs: + read = GetAttr(env_reg, spill_locs[src], op.line) + block.ops.append(read) + new_sources.append(read) + if src.type.is_refcounted and src not in stolen: + to_decref.append(read) + else: + new_sources.append(src) + + op.set_sources(new_sources) + + block.ops.append(op) + + for dec in to_decref: + block.ops.append(DecRef(dec)) + + if op in spill_locs: + # XXX: could we set uninit? + block.ops.append(SetAttr(env_reg, spill_locs[op], op, op.line)) + + return blocks diff --git a/mypyc/transform/uninit.py b/mypyc/transform/uninit.py index 6bf71ac4a8bcd..45b403588f8eb 100644 --- a/mypyc/transform/uninit.py +++ b/mypyc/transform/uninit.py @@ -69,14 +69,19 @@ def split_blocks_at_uninits( and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR) and not isinstance(op, LoadAddress) ): - new_block, error_block = BasicBlock(), BasicBlock() - new_block.error_handler = error_block.error_handler = cur_block.error_handler - new_blocks += [error_block, new_block] - if src not in init_registers_set: init_registers.append(src) init_registers_set.add(src) + # XXX: if src.name is empty, it should be a + # temp... and it should be OK?? + if not src.name: + continue + + new_block, error_block = BasicBlock(), BasicBlock() + new_block.error_handler = error_block.error_handler = cur_block.error_handler + new_blocks += [error_block, new_block] + if not src.type.error_overlap: cur_block.ops.append( Branch( diff --git a/pyproject.toml b/pyproject.toml index 12a0dc109cd53..fa56caeaa4bc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,21 +3,111 @@ 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", + "pathspec>=0.9.0", "tomli>=1.1.0; python_version<'3.11'", + "librt>=0.6.2; platform_python_implementation != 'PyPy'", # the following is from build-requirements.txt "types-psutil", "types-setuptools", ] 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.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Software Development", + "Typing :: Typed", +] +requires-python = ">=3.9" +dependencies = [ + # When changing this, also update build-system.requires and mypy-requirements.txt + "typing_extensions>=4.6.0", + "mypy_extensions>=1.0.0", + "pathspec>=0.9.0", + "tomli>=1.1.0; python_version<'3.11'", + "librt>=0.6.2; platform_python_implementation != 'PyPy'", +] +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*"] +exclude = ["mypyc.test-data*"] +namespaces = false + +[tool.setuptools.package-data] +mypy = [ + "py.typed", + "typeshed/**/*.py", + "typeshed/**/*.pyi", + "typeshed/stdlib/VERSIONS", + "xml/*.xsd", + "xml/*.xslt", + "xml/*.css", +] +[tool.setuptools.exclude-package-data] +mypyc = [ + "README.md", + "doc/**", + "external/**", + "lib-rt/test_capi.cc", + "lib-rt/setup.py", + "test-data/**", +] + [tool.black] line-length = 99 -target-version = ["py38", "py39", "py310", "py311", "py312"] +target-version = ["py39", "py310", "py311", "py312", "py313", "py314"] skip-magic-trailing-comma = true force-exclude = ''' ^/mypy/typeshed| @@ -27,7 +117,7 @@ force-exclude = ''' [tool.ruff] line-length = 99 -target-version = "py38" +target-version = "py39" fix = true extend-exclude = [ @@ -50,11 +140,19 @@ select = [ "W", # pycodestyle (warning) "B", # flake8-bugbear "I", # isort + "N", # pep8-naming + "PIE", # flake8-pie + "PLE", # pylint error "RUF100", # Unused noqa comments "PGH004", # blanket noqa comments "UP", # pyupgrade "C4", # flake8-comprehensions - "SIM201", "SIM202", # simplify comparisons involving not + "SIM101", # merge duplicate isinstance calls + "SIM201", "SIM202", "SIM222", "SIM223", # flake8-simplify + "FURB168", # Prefer is operator over isinstance for None checks + "FURB169", # Do not use is comparison with type(None). Use None + "FURB187", # avoid list reverse copy + "FURB188", # use str.remove(pre|suf)fix "ISC001", # implicitly concatenated string "RET501", "RET502", # better return None handling ] @@ -66,10 +164,17 @@ 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 + "C409", # https://github.com/astral-sh/ruff/issues/12912 + "C420", # reads a little worse. fromkeys predates dict comprehensions "C416", # There are a few cases where it's nice to have names for the dict items + "PIE790", # there's nothing wrong with pass ] unfixable = [ @@ -78,8 +183,14 @@ unfixable = [ "F602", # automatic fix might obscure issue "B018", # automatic fix might obscure issue "UP036", # sometimes it's better to just noqa this + "SIM222", # automatic fix might obscure issue + "SIM223", # automatic fix might obscure issue ] +[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"] @@ -113,6 +224,11 @@ addopts = "-nauto --strict-markers --strict-config" # treat xpasses as test failures so they get converted to regular tests as soon as possible xfail_strict = true +# Force warnings as errors +filterwarnings = [ + "error", +] + [tool.coverage.run] branch = true source = ["mypy"] diff --git a/runtests.py b/runtests.py index 80ef8d814ee1e..3f49107f3ce05 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 @@ -59,6 +65,15 @@ "-p", "mypyc", ], + # Type check setup.py as well + "self-packaging": [ + executable, + "-m", + "mypy", + "--config-file", + "mypy_self_check.ini", + "setup.py", + ], # Lint "lint": ["pre-commit", "run", "--all-files"], # Fast test cases only (this is the bulk of the test suite) @@ -77,6 +92,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 +103,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) @@ -95,7 +111,13 @@ def run_cmd(name: str) -> int: status = 0 - cmd = cmds[name] + if name in cmds: + cmd = cmds[name] + else: + if name.endswith(".test"): + cmd = ["pytest", f"mypy/test/testcheck.py::TypeCheckSuite::{name}"] + else: + cmd = ["pytest", "-n0", "-k", name] print(f"run {name}: {cmd}") proc = subprocess.run(cmd, stderr=subprocess.STDOUT) if proc.returncode: @@ -128,13 +150,22 @@ def main() -> None: prog, *args = argv if not set(args).issubset(cmds): - print("usage:", prog, " ".join(f"[{k}]" for k in cmds)) + print( + "usage:", + prog, + " ".join(f"[{k}]" for k in cmds), + "[names of individual tests and files...]", + ) print() print( "Run the given tests. If given no arguments, run everything except" - + " pytest-extra and mypyc-extra." + + " pytest-extra and mypyc-extra. Unrecognized arguments will be" + + " interpreted as individual test names / substring expressions" + + " (or, if they end in .test, individual test files)" + + " and this script will try to run them." ) - exit(1) + if "-h" in args or "--help" in args: + exit(1) if not args: args = DEFAULT_COMMANDS.copy() diff --git a/setup.py b/setup.py index 160e2b054b0ec..f20c1db5d0450 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,8 @@ import sys from typing import TYPE_CHECKING, Any -if sys.version_info < (3, 8, 0): # noqa: UP036 - sys.stderr.write("ERROR: You need Python 3.8 or later to use mypy.\n") +if sys.version_info < (3, 9, 0): # noqa: UP036, RUF100 + sys.stderr.write("ERROR: You need Python 3.9 or later to use mypy.\n") exit(1) # we'll import stuff from the source tree, let's ensure is on the sys path @@ -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,25 +26,12 @@ 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) -def find_package_data(base, globs, root="mypy"): +def find_package_data(base: str, globs: list[str], root: str = "mypy") -> list[str]: """Find all interesting data files, for setup(package_data=) Arguments: @@ -65,26 +52,19 @@ def find_package_data(base, globs, root="mypy"): class CustomPythonBuild(build_py): - def pin_version(self): + def pin_version(self) -> None: path = os.path.join(self.build_lib, "mypy") self.mkpath(path) with open(os.path.join(path, "version.py"), "w") as stream: stream.write(f'__version__ = "{version}"\n') - def run(self): + def run(self) -> None: self.execute(self.pin_version, ()) build_py.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: @@ -101,6 +81,7 @@ def run(self): "__main__.py", "pyinfo.py", os.path.join("dmypy", "__main__.py"), + "exportjson.py", # Uses __getattr__/__setattr__ "split_namespace.py", # Lies to mypy about code reachability @@ -118,6 +99,7 @@ def run(self): os.path.join("mypyc", "lib-rt", "setup.py"), # Uses __file__ at top level https://github.com/mypyc/mypyc/issues/700 os.path.join("mypyc", "__main__.py"), + os.path.join("mypyc", "build_setup.py"), # for monkeypatching ) everything = [os.path.join("mypy", x) for x in find_package_data("mypy", ["*.py"])] + [ @@ -165,6 +147,7 @@ def run(self): opt_level = os.getenv("MYPYC_OPT_LEVEL", "3") debug_level = os.getenv("MYPYC_DEBUG_LEVEL", "1") force_multifile = os.getenv("MYPYC_MULTI_FILE", "") == "1" + log_trace = bool(int(os.getenv("MYPYC_LOG_TRACE", "0"))) ext_modules = mypycify( mypyc_targets + ["--config-file=mypy_bootstrap.ini"], opt_level=opt_level, @@ -172,72 +155,14 @@ def run(self): # Use multi-file compilation mode on windows because without it # our Appveyor builds run out of memory sometimes. multi_file=sys.platform == "win32" or force_multifile, + log_trace=log_trace, + # Mypy itself is allowed to use native_internal extension. + depends_on_librt_internal=True, ) - assert is_list_of_setuptools_extension(ext_modules), "Expected mypycify to use setuptools" else: ext_modules = [] +assert is_list_of_setuptools_extension(ext_modules), "Expected mypycify to use setuptools" -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/packages/modulefinder-site-packages/foo-stubs/qux.pyi b/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi new file mode 100644 index 0000000000000..5605b14540396 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi @@ -0,0 +1 @@ +qux_var: int diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi new file mode 100644 index 0000000000000..579a7556fdd1e --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi @@ -0,0 +1 @@ +pkg_typed_w_stubs_var: str = ... diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi new file mode 100644 index 0000000000000..e3ef9cce59055 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi @@ -0,0 +1 @@ +spam_var: str diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py new file mode 100644 index 0000000000000..11fa3635a2c7a --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py @@ -0,0 +1 @@ +pkg_typed_w_stubs_var = "pkg_typed_w_stubs" diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi new file mode 100644 index 0000000000000..3a03f395d0144 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi @@ -0,0 +1 @@ +pkg_typed_w_stubs_var: object diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py new file mode 100644 index 0000000000000..0aff1579b57f7 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py @@ -0,0 +1 @@ +spam_var = "spam" diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi new file mode 100644 index 0000000000000..8eca196a79817 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi @@ -0,0 +1 @@ +spam_var: object diff --git a/test-data/packages/typedpkg_ns_nested/pyproject.toml b/test-data/packages/typedpkg_ns_nested/pyproject.toml new file mode 100644 index 0000000000000..b5bf038b8e14c --- /dev/null +++ b/test-data/packages/typedpkg_ns_nested/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = 'typedpkg_namespace.nested' +version = '0.1' +description = 'Two namespace packages, one of them typed' + +[tool.hatch.build] +include = ["**/*.py", "**/*.pyi", "**/py.typed"] + +[build-system] +requires = ["hatchling==1.18"] +build-backend = "hatchling.build" diff --git a/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/__init__.py b/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/py.typed b/test-data/packages/typedpkg_ns_nested/typedpkg_ns/a/py.typed new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test-data/packages/typedpkg_ns_nested/typedpkg_ns/b/__init__.py b/test-data/packages/typedpkg_ns_nested/typedpkg_ns/b/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi index 90afb46d6d94a..c841b207c130c 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/__init__.pyi @@ -1,4 +1,5 @@ -import os +import pathlib +import typing from . import demo as demo from typing import overload @@ -6,12 +7,12 @@ class StaticMethods: def __init__(self, *args, **kwargs) -> None: ... @overload @staticmethod - def overloaded_static_method(value: int) -> int: ... + def overloaded_static_method(value: typing.SupportsInt) -> int: ... @overload @staticmethod - def overloaded_static_method(value: float) -> float: ... + def overloaded_static_method(value: typing.SupportsFloat) -> float: ... @staticmethod - def some_static_method(a: int, b: int) -> int: ... + def some_static_method(a: typing.SupportsInt, b: typing.SupportsInt) -> int: ... class TestStruct: field_readwrite: int @@ -23,5 +24,5 @@ class TestStruct: def func_incomplete_signature(*args, **kwargs): ... def func_returning_optional() -> int | None: ... def func_returning_pair() -> tuple[int, float]: ... -def func_returning_path() -> os.PathLike: ... +def func_returning_path() -> pathlib.Path: ... def func_returning_vector() -> list[float]: ... diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi index 87b8ec0e4ad6d..09e75e1ad4aa1 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi @@ -1,3 +1,4 @@ +import typing from typing import ClassVar, overload PI: float @@ -9,7 +10,7 @@ class Point: __entries: ClassVar[dict] = ... degree: ClassVar[Point.AngleUnit] = ... radian: ClassVar[Point.AngleUnit] = ... - def __init__(self, value: int) -> None: ... + def __init__(self, value: typing.SupportsInt) -> None: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... def __index__(self) -> int: ... @@ -26,7 +27,7 @@ class Point: inch: ClassVar[Point.LengthUnit] = ... mm: ClassVar[Point.LengthUnit] = ... pixel: ClassVar[Point.LengthUnit] = ... - def __init__(self, value: int) -> None: ... + def __init__(self, value: typing.SupportsInt) -> None: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... def __index__(self) -> int: ... @@ -46,16 +47,16 @@ class Point: @overload def __init__(self) -> None: ... @overload - def __init__(self, x: float, y: float) -> None: ... + def __init__(self, x: typing.SupportsFloat, y: typing.SupportsFloat) -> None: ... def as_list(self) -> list[float]: ... @overload - def distance_to(self, x: float, y: float) -> float: ... + def distance_to(self, x: typing.SupportsFloat, y: typing.SupportsFloat) -> float: ... @overload def distance_to(self, other: Point) -> float: ... @property def length(self) -> float: ... def answer() -> int: ... -def midpoint(left: float, right: float) -> float: ... -def sum(arg0: int, arg1: int) -> int: ... -def weighted_midpoint(left: float, right: float, alpha: float = ...) -> float: ... +def midpoint(left: typing.SupportsFloat, right: typing.SupportsFloat) -> float: ... +def sum(arg0: typing.SupportsInt, arg1: typing.SupportsInt) -> int: ... +def weighted_midpoint(left: typing.SupportsFloat, right: typing.SupportsFloat, alpha: typing.SupportsFloat = ...) -> float: ... diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi index db04bccab028f..701a837580b44 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi @@ -1,4 +1,5 @@ -import os +import pathlib +import typing from . import demo as demo from typing import overload @@ -7,27 +8,27 @@ class StaticMethods: """Initialize self. See help(type(self)) for accurate signature.""" @overload @staticmethod - def overloaded_static_method(value: int) -> int: + def overloaded_static_method(value: typing.SupportsInt) -> int: """overloaded_static_method(*args, **kwargs) Overloaded function. - 1. overloaded_static_method(value: int) -> int + 1. overloaded_static_method(value: typing.SupportsInt) -> int - 2. overloaded_static_method(value: float) -> float + 2. overloaded_static_method(value: typing.SupportsFloat) -> float """ @overload @staticmethod - def overloaded_static_method(value: float) -> float: + def overloaded_static_method(value: typing.SupportsFloat) -> float: """overloaded_static_method(*args, **kwargs) Overloaded function. - 1. overloaded_static_method(value: int) -> int + 1. overloaded_static_method(value: typing.SupportsInt) -> int - 2. overloaded_static_method(value: float) -> float + 2. overloaded_static_method(value: typing.SupportsFloat) -> float """ @staticmethod - def some_static_method(a: int, b: int) -> int: - """some_static_method(a: int, b: int) -> int + def some_static_method(a: typing.SupportsInt, b: typing.SupportsInt) -> int: + """some_static_method(a: typing.SupportsInt, b: typing.SupportsInt) -> int None """ @@ -38,15 +39,18 @@ class TestStruct: def __init__(self, *args, **kwargs) -> None: """Initialize self. See help(type(self)) for accurate signature.""" @property - def field_readonly(self) -> int: ... + def field_readonly(self) -> int: + """some docstring + (arg0: pybind11_fixtures.TestStruct) -> int + """ def func_incomplete_signature(*args, **kwargs): """func_incomplete_signature() -> dummy_sub_namespace::HasNoBinding""" def func_returning_optional() -> int | None: - """func_returning_optional() -> Optional[int]""" + """func_returning_optional() -> int | None""" def func_returning_pair() -> tuple[int, float]: - """func_returning_pair() -> Tuple[int, float]""" -def func_returning_path() -> os.PathLike: - """func_returning_path() -> os.PathLike""" + """func_returning_pair() -> tuple[int, float]""" +def func_returning_path() -> pathlib.Path: + """func_returning_path() -> pathlib.Path""" def func_returning_vector() -> list[float]: - """func_returning_vector() -> List[float]""" + """func_returning_vector() -> list[float]""" diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi index 1be0bc905a439..580aa27001784 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi @@ -1,3 +1,4 @@ +import typing from typing import ClassVar, overload PI: float @@ -5,49 +6,71 @@ __version__: str class Point: class AngleUnit: + """Members: + + radian + + degree""" __members__: ClassVar[dict] = ... # read-only __entries: ClassVar[dict] = ... degree: ClassVar[Point.AngleUnit] = ... radian: ClassVar[Point.AngleUnit] = ... - def __init__(self, value: int) -> None: - """__init__(self: pybind11_fixtures.demo.Point.AngleUnit, value: int) -> None""" + def __init__(self, value: typing.SupportsInt) -> None: + """__init__(self: pybind11_fixtures.demo.Point.AngleUnit, value: typing.SupportsInt) -> None""" def __eq__(self, other: object) -> bool: - """__eq__(self: object, other: object) -> bool""" + """__eq__(self: object, other: object, /) -> bool""" def __hash__(self) -> int: - """__hash__(self: object) -> int""" + """__hash__(self: object, /) -> int""" def __index__(self) -> int: - """__index__(self: pybind11_fixtures.demo.Point.AngleUnit) -> int""" + """__index__(self: pybind11_fixtures.demo.Point.AngleUnit, /) -> int""" def __int__(self) -> int: - """__int__(self: pybind11_fixtures.demo.Point.AngleUnit) -> int""" + """__int__(self: pybind11_fixtures.demo.Point.AngleUnit, /) -> int""" def __ne__(self, other: object) -> bool: - """__ne__(self: object, other: object) -> bool""" + """__ne__(self: object, other: object, /) -> bool""" @property - def name(self) -> str: ... + def name(self) -> str: + """name(self: object, /) -> str + + name(self: object, /) -> str + """ @property - def value(self) -> int: ... + def value(self) -> int: + """(arg0: pybind11_fixtures.demo.Point.AngleUnit) -> int""" class LengthUnit: + """Members: + + mm + + pixel + + inch""" __members__: ClassVar[dict] = ... # read-only __entries: ClassVar[dict] = ... inch: ClassVar[Point.LengthUnit] = ... mm: ClassVar[Point.LengthUnit] = ... pixel: ClassVar[Point.LengthUnit] = ... - def __init__(self, value: int) -> None: - """__init__(self: pybind11_fixtures.demo.Point.LengthUnit, value: int) -> None""" + def __init__(self, value: typing.SupportsInt) -> None: + """__init__(self: pybind11_fixtures.demo.Point.LengthUnit, value: typing.SupportsInt) -> None""" def __eq__(self, other: object) -> bool: - """__eq__(self: object, other: object) -> bool""" + """__eq__(self: object, other: object, /) -> bool""" def __hash__(self) -> int: - """__hash__(self: object) -> int""" + """__hash__(self: object, /) -> int""" def __index__(self) -> int: - """__index__(self: pybind11_fixtures.demo.Point.LengthUnit) -> int""" + """__index__(self: pybind11_fixtures.demo.Point.LengthUnit, /) -> int""" def __int__(self) -> int: - """__int__(self: pybind11_fixtures.demo.Point.LengthUnit) -> int""" + """__int__(self: pybind11_fixtures.demo.Point.LengthUnit, /) -> int""" def __ne__(self, other: object) -> bool: - """__ne__(self: object, other: object) -> bool""" + """__ne__(self: object, other: object, /) -> bool""" @property - def name(self) -> str: ... + def name(self) -> str: + """name(self: object, /) -> str + + name(self: object, /) -> str + """ @property - def value(self) -> int: ... + def value(self) -> int: + """(arg0: pybind11_fixtures.demo.Point.LengthUnit) -> int""" angle_unit: ClassVar[Point.AngleUnit] = ... length_unit: ClassVar[Point.LengthUnit] = ... x_axis: ClassVar[Point] = ... # read-only @@ -62,25 +85,25 @@ class Point: 1. __init__(self: pybind11_fixtures.demo.Point) -> None - 2. __init__(self: pybind11_fixtures.demo.Point, x: float, y: float) -> None + 2. __init__(self: pybind11_fixtures.demo.Point, x: typing.SupportsFloat, y: typing.SupportsFloat) -> None """ @overload - def __init__(self, x: float, y: float) -> None: + def __init__(self, x: typing.SupportsFloat, y: typing.SupportsFloat) -> None: """__init__(*args, **kwargs) Overloaded function. 1. __init__(self: pybind11_fixtures.demo.Point) -> None - 2. __init__(self: pybind11_fixtures.demo.Point, x: float, y: float) -> None + 2. __init__(self: pybind11_fixtures.demo.Point, x: typing.SupportsFloat, y: typing.SupportsFloat) -> None """ def as_list(self) -> list[float]: - """as_list(self: pybind11_fixtures.demo.Point) -> List[float]""" + """as_list(self: pybind11_fixtures.demo.Point) -> list[float]""" @overload - def distance_to(self, x: float, y: float) -> float: + def distance_to(self, x: typing.SupportsFloat, y: typing.SupportsFloat) -> float: """distance_to(*args, **kwargs) Overloaded function. - 1. distance_to(self: pybind11_fixtures.demo.Point, x: float, y: float) -> float + 1. distance_to(self: pybind11_fixtures.demo.Point, x: typing.SupportsFloat, y: typing.SupportsFloat) -> float 2. distance_to(self: pybind11_fixtures.demo.Point, other: pybind11_fixtures.demo.Point) -> float """ @@ -89,24 +112,25 @@ class Point: """distance_to(*args, **kwargs) Overloaded function. - 1. distance_to(self: pybind11_fixtures.demo.Point, x: float, y: float) -> float + 1. distance_to(self: pybind11_fixtures.demo.Point, x: typing.SupportsFloat, y: typing.SupportsFloat) -> float 2. distance_to(self: pybind11_fixtures.demo.Point, other: pybind11_fixtures.demo.Point) -> float """ @property - def length(self) -> float: ... + def length(self) -> float: + """(arg0: pybind11_fixtures.demo.Point) -> float""" def answer() -> int: '''answer() -> int answer docstring, with end quote" ''' -def midpoint(left: float, right: float) -> float: - """midpoint(left: float, right: float) -> float""" -def sum(arg0: int, arg1: int) -> int: - '''sum(arg0: int, arg1: int) -> int +def midpoint(left: typing.SupportsFloat, right: typing.SupportsFloat) -> float: + """midpoint(left: typing.SupportsFloat, right: typing.SupportsFloat) -> float""" +def sum(arg0: typing.SupportsInt, arg1: typing.SupportsInt) -> int: + '''sum(arg0: typing.SupportsInt, arg1: typing.SupportsInt) -> int multiline docstring test, edge case quotes """\'\'\' ''' -def weighted_midpoint(left: float, right: float, alpha: float = ...) -> float: - """weighted_midpoint(left: float, right: float, alpha: float = 0.5) -> float""" +def weighted_midpoint(left: typing.SupportsFloat, right: typing.SupportsFloat, alpha: typing.SupportsFloat = ...) -> float: + """weighted_midpoint(left: typing.SupportsFloat, right: typing.SupportsFloat, alpha: typing.SupportsFloat = 0.5) -> float""" diff --git a/test-data/pybind11_fixtures/pyproject.toml b/test-data/pybind11_fixtures/pyproject.toml index 773d036e62f56..56eaa50720654 100644 --- a/test-data/pybind11_fixtures/pyproject.toml +++ b/test-data/pybind11_fixtures/pyproject.toml @@ -4,7 +4,7 @@ requires = [ "wheel", # Officially supported pybind11 version. This is pinned to guarantee 100% reproducible CI. # As a result, the version needs to be bumped manually at will. - "pybind11==2.9.2", + "pybind11==3.0.1", ] build-backend = "setuptools.build_meta" 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-abstract.test b/test-data/unit/check-abstract.test index 3b0b9c520b75f..7507a31d115a9 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -191,8 +191,8 @@ def f(cls: Type[A]) -> A: def g() -> A: return A() # E: Cannot instantiate abstract class "A" with abstract attribute "m" -f(A) # E: Only concrete class can be given where "Type[A]" is expected -f(B) # E: Only concrete class can be given where "Type[A]" is expected +f(A) # E: Only concrete class can be given where "type[A]" is expected +f(B) # E: Only concrete class can be given where "type[A]" is expected f(C) # OK x: Type[B] f(x) # OK @@ -207,7 +207,7 @@ class Class: def method(self) -> None: pass -my_dict_init: Dict[int, Type[Class]] = {0: Class} # E: Only concrete class can be given where "Tuple[int, Type[Class]]" is expected +my_dict_init: Dict[int, Type[Class]] = {0: Class} # E: Only concrete class can be given where "tuple[int, type[Class]]" is expected class Child(Class): def method(self) -> None: ... @@ -235,7 +235,7 @@ Alias = A GoodAlias = C Alias() # E: Cannot instantiate abstract class "A" with abstract attribute "m" GoodAlias() -f(Alias) # E: Only concrete class can be given where "Type[A]" is expected +f(Alias) # E: Only concrete class can be given where "type[A]" is expected f(GoodAlias) [out] @@ -255,18 +255,18 @@ class C(B): var: Type[A] var() if int(): - var = A # E: Can only assign concrete classes to a variable of type "Type[A]" + var = A # E: Can only assign concrete classes to a variable of type "type[A]" if int(): - var = B # E: Can only assign concrete classes to a variable of type "Type[A]" + var = B # E: Can only assign concrete classes to a variable of type "type[A]" if int(): var = C # OK var_old = None # type: Type[A] # Old syntax for variable annotations var_old() if int(): - var_old = A # E: Can only assign concrete classes to a variable of type "Type[A]" + var_old = A # E: Can only assign concrete classes to a variable of type "type[A]" if int(): - var_old = B # E: Can only assign concrete classes to a variable of type "Type[A]" + var_old = B # E: Can only assign concrete classes to a variable of type "type[A]" if int(): var_old = C # OK @@ -277,7 +277,7 @@ class D(A): def __new__(cls) -> "D": ... def __new__(cls, a=None) -> "D": ... if int(): - var = D # E: Can only assign concrete classes to a variable of type "Type[A]" + var = D # E: Can only assign concrete classes to a variable of type "type[A]" [out] [case testInstantiationAbstractsInTypeForClassMethods] @@ -571,8 +571,12 @@ from abc import abstractmethod, ABCMeta import typing class A(metaclass=ABCMeta): pass -class B(object, A): pass \ - # E: Cannot determine consistent method resolution order (MRO) for "B" +class B(object, A, metaclass=ABCMeta): # E: Cannot determine consistent method resolution order (MRO) for "B" + pass + +class C(object, A): # E: Cannot determine consistent method resolution order (MRO) for "C" \ + # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases + pass [case testOverloadedAbstractMethod] from foo import * @@ -990,7 +994,6 @@ class Mixin: class C(Mixin, A): pass [builtins fixtures/property.pyi] -[out] [case testMixinSubtypedProperty] class X: @@ -1006,7 +1009,6 @@ class Mixin: class C(Mixin, A): pass [builtins fixtures/property.pyi] -[out] [case testMixinTypedPropertyReversed] class A: @@ -1015,10 +1017,9 @@ class A: return "no" class Mixin: foo = "foo" -class C(A, Mixin): # E: Definition of "foo" in base class "A" is incompatible with definition in base class "Mixin" +class C(A, Mixin): # E: Cannot override writeable attribute "foo" in base "Mixin" with read-only property in base "A" pass [builtins fixtures/property.pyi] -[out] -- Special cases -- ------------- diff --git a/test-data/unit/check-annotated.test b/test-data/unit/check-annotated.test index d4309b8ad213e..24f4a1d945c64 100644 --- a/test-data/unit/check-annotated.test +++ b/test-data/unit/check-annotated.test @@ -105,7 +105,7 @@ from typing_extensions import Annotated T = TypeVar('T') Alias = Annotated[Tuple[T, T], ...] x: Alias[int] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/tuple.pyi] [case testAnnotatedAliasGenericUnion] @@ -139,20 +139,12 @@ reveal_type(f2) # N: Revealed type is "def (a: builtins.str) -> Any" def f3(a: Annotated["notdefined", "metadata"]): # E: Name "notdefined" is not defined pass T = TypeVar('T') -def f4(a: Annotated[T, "metatdata"]): +def f4(a: Annotated[T, "metadata"]): pass reveal_type(f4) # N: Revealed type is "def [T] (a: T`-1) -> Any" [builtins fixtures/tuple.pyi] -[case testSliceAnnotated39] -# flags: --python-version 3.9 -from typing_extensions import Annotated -a: Annotated[int, 1:2] -reveal_type(a) # N: Revealed type is "builtins.int" -[builtins fixtures/tuple.pyi] - -[case testSliceAnnotated38] -# flags: --python-version 3.8 +[case testSliceAnnotated] from typing_extensions import Annotated a: Annotated[int, 1:2] reveal_type(a) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-assert-type-fail.test b/test-data/unit/check-assert-type-fail.test index 89b3a863f8c75..98aae0ba6b329 100644 --- a/test-data/unit/check-assert-type-fail.test +++ b/test-data/unit/check-assert-type-fail.test @@ -30,4 +30,20 @@ def f(si: arr.array[int]): [case testAssertTypeFailCallableArgKind] from typing import assert_type, Callable def myfunc(arg: int) -> None: pass -assert_type(myfunc, Callable[[int], None]) # E: Expression is of type "Callable[[Arg(int, 'arg')], None]", not "Callable[[int], None]" +assert_type(myfunc, Callable[[int], None]) # E: Expression is of type "def myfunc(arg: int) -> None", not "Callable[[int], None]" + +[case testAssertTypeOverload] +from typing import assert_type, overload + +class Foo: + @overload + def __new__(cls, x: int) -> Foo: ... + @overload + def __new__(cls, x: str) -> Foo: ... + def __new__(cls, x: "int | str") -> Foo: + return cls(0) + +assert_type(Foo, type[Foo]) +A = Foo +assert_type(A, type[Foo]) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 0ef08e5a07751..979da62aca925 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -163,7 +163,7 @@ async def f() -> None: [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] [out] -main:4: error: "List[int]" has no attribute "__aiter__" (not async iterable) +main:4: error: "list[int]" has no attribute "__aiter__" (not async iterable) [case testAsyncForErrorNote] @@ -502,7 +502,7 @@ async def gen() -> AsyncGenerator[int, str]: async def h() -> None: g = gen() - await g.asend(()) # E: Argument 1 to "asend" of "AsyncGenerator" has incompatible type "Tuple[()]"; expected "str" + await g.asend(()) # E: Argument 1 to "asend" of "AsyncGenerator" has incompatible type "tuple[()]"; expected "str" reveal_type(await g.asend('hello')) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] @@ -913,9 +913,9 @@ async def test(x: Sub[D], tx: Type[Sub[D]]) -> None: unknown2: Awaitable[Any] d: C = unknown2 # E: Incompatible types in assignment (expression has type "Awaitable[Any]", variable has type "C") - # The notes are not show for Type[...] (because awaiting them will not work) - tx.x # E: "Type[Sub[D]]" has no attribute "x" - a2: C = tx # E: Incompatible types in assignment (expression has type "Type[Sub[D]]", variable has type "C") + # The notes are not show for type[...] (because awaiting them will not work) + tx.x # E: "type[Sub[D]]" has no attribute "x" + a2: C = tx # E: Incompatible types in assignment (expression has type "type[Sub[D]]", variable has type "C") class F: def __await__(self: T) -> Generator[Any, Any, T]: ... diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 959d80cb21042..07ed5fd77082f 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -289,7 +289,7 @@ x in 1, # E: Unsupported right operand type for in ("int") [case testTrailingCommaInIfParsing] if x in 1, : pass [out] -main:1: error: invalid syntax +main:1: error: Invalid syntax [case testInitReturnTypeError] class C: @@ -378,8 +378,8 @@ reveal_type(b) # N: Revealed type is "Literal[False]" from typing import List x: List[int] y: List[float] -y = x # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +y = x # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[float]") \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] @@ -387,8 +387,8 @@ y = x # E: Incompatible types in assignment (expression has type "List[int]", va from typing import Dict x: Dict[str, int] y: Dict[str, float] -y = x # E: Incompatible types in assignment (expression has type "Dict[str, int]", variable has type "Dict[str, float]") \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +y = x # E: Incompatible types in assignment (expression has type "dict[str, int]", variable has type "dict[str, float]") \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type [builtins fixtures/dict.pyi] @@ -396,8 +396,7 @@ y = x # E: Incompatible types in assignment (expression has type "Dict[str, int] import b [file a.py] -from typing import NamedTuple -from typing_extensions import TypedDict +from typing import NamedTuple, TypedDict from enum import Enum class A: pass N = NamedTuple('N', [('x', int)]) @@ -406,8 +405,7 @@ class B(Enum): b = 10 [file b.py] -from typing import List, Optional, Union, Sequence, NamedTuple, Tuple, Type -from typing_extensions import Literal, Final, TypedDict +from typing import Final, List, Literal, Optional, Union, Sequence, NamedTuple, Tuple, Type, TypedDict from enum import Enum import a class A: pass @@ -422,7 +420,7 @@ def foo() -> Optional[A]: def bar() -> List[A]: l = [a.A()] - return l # E: Incompatible return value type (got "List[a.A]", expected "List[b.A]") + return l # E: Incompatible return value type (got "list[a.A]", expected "list[b.A]") def baz() -> Union[A, int]: b = True @@ -433,39 +431,39 @@ def spam() -> Optional[A]: def eggs() -> Sequence[A]: x = [a.A()] - return x # E: Incompatible return value type (got "List[a.A]", expected "Sequence[b.A]") + return x # E: Incompatible return value type (got "list[a.A]", expected "Sequence[b.A]") def eggs2() -> Sequence[N]: x = [a.N(0)] - return x # E: Incompatible return value type (got "List[a.N]", expected "Sequence[b.N]") + return x # E: Incompatible return value type (got "list[a.N]", expected "Sequence[b.N]") def asdf1() -> Sequence[Tuple[a.A, A]]: x = [(a.A(), a.A())] - return x # E: Incompatible return value type (got "List[Tuple[a.A, a.A]]", expected "Sequence[Tuple[a.A, b.A]]") + return x # E: Incompatible return value type (got "list[tuple[a.A, a.A]]", expected "Sequence[tuple[a.A, b.A]]") def asdf2() -> Sequence[Tuple[A, a.A]]: x = [(a.A(), a.A())] - return x # E: Incompatible return value type (got "List[Tuple[a.A, a.A]]", expected "Sequence[Tuple[b.A, a.A]]") + return x # E: Incompatible return value type (got "list[tuple[a.A, a.A]]", expected "Sequence[tuple[b.A, a.A]]") def arg() -> Tuple[A, A]: - return A() # E: Incompatible return value type (got "A", expected "Tuple[A, A]") + return A() # E: Incompatible return value type (got "A", expected "tuple[A, A]") def types() -> Sequence[Type[A]]: x = [a.A] - return x # E: Incompatible return value type (got "List[Type[a.A]]", expected "Sequence[Type[b.A]]") + return x # E: Incompatible return value type (got "list[type[a.A]]", expected "Sequence[type[b.A]]") def literal() -> Sequence[Literal[B.b]]: x = [a.B.b] # type: List[Literal[a.B.b]] - return x # E: Incompatible return value type (got "List[Literal[a.B.b]]", expected "Sequence[Literal[b.B.b]]") + return x # E: Incompatible return value type (got "list[Literal[a.B.b]]", expected "Sequence[Literal[b.B.b]]") def typeddict() -> Sequence[D]: x = [{'x': 0}] # type: List[a.D] - return x # E: Incompatible return value type (got "List[a.D]", expected "Sequence[b.D]") + return x # E: Incompatible return value type (got "list[a.D]", expected "Sequence[b.D]") a = (a.A(), A()) -a.x # E: "Tuple[a.A, b.A]" has no attribute "x" - +a.x # E: "tuple[a.A, b.A]" has no attribute "x" [builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testReturnAnyFromFunctionDeclaredToReturnObject] # flags: --warn-return-any diff --git a/test-data/unit/check-bound.test b/test-data/unit/check-bound.test index 1c713fd77c38d..1f9eba6120201 100644 --- a/test-data/unit/check-bound.test +++ b/test-data/unit/check-bound.test @@ -46,7 +46,7 @@ z = G(B()) [case testBoundVoid] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types from typing import TypeVar, Generic T = TypeVar('T', bound=int) class C(Generic[T]): @@ -75,7 +75,7 @@ z: C [case testBoundHigherOrderWithVoid] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types from typing import TypeVar, Callable class A: pass T = TypeVar('T', bound=A) diff --git a/test-data/unit/check-callable.test b/test-data/unit/check-callable.test index 39e6c4fa3ff1c..0157ff3d2c536 100644 --- a/test-data/unit/check-callable.test +++ b/test-data/unit/check-callable.test @@ -587,6 +587,54 @@ class C(B): class B: ... [builtins fixtures/classmethod.pyi] +[case testClassMethodAliasInClass] +from typing import overload + +class C: + @classmethod + def foo(cls) -> int: ... + + bar = foo + + @overload + @classmethod + def foo2(cls, x: int) -> int: ... + @overload + @classmethod + def foo2(cls, x: str) -> str: ... + @classmethod + def foo2(cls, x): + ... + + bar2 = foo2 + +reveal_type(C.bar) # N: Revealed type is "def () -> builtins.int" +reveal_type(C().bar) # N: Revealed type is "def () -> builtins.int" +reveal_type(C.bar2) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)" +reveal_type(C().bar2) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)" +[builtins fixtures/classmethod.pyi] + +[case testPropertyAliasInClassBody] +class A: + @property + def f(self) -> int: ... + + g = f + + @property + def f2(self) -> int: ... + @f2.setter + def f2(self, val: int) -> None: ... + + g2 = f2 + +reveal_type(A().g) # N: Revealed type is "builtins.int" +reveal_type(A().g2) # N: Revealed type is "builtins.int" +A().g = 1 # E: Property "g" defined in "A" is read-only +A().g2 = 1 +A().g2 = "no" # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[builtins fixtures/property.pyi] + [case testCallableUnionCallback] from typing import Union, Callable, TypeVar @@ -606,13 +654,13 @@ class Call(Protocol): def f1() -> None: ... a1: Call = f1 # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "Call") \ - # N: "Call.__call__" has type "Callable[[Arg(int, 'x'), VarArg(Any), KwArg(Any)], None]" + # N: "Call.__call__" has type "def __call__(self, x: int, *args: Any, **kwargs: Any) -> None" def f2(x: str) -> None: ... a2: Call = f2 # E: Incompatible types in assignment (expression has type "Callable[[str], None]", variable has type "Call") \ - # N: "Call.__call__" has type "Callable[[Arg(int, 'x'), VarArg(Any), KwArg(Any)], None]" + # N: "Call.__call__" has type "def __call__(self, x: int, *args: Any, **kwargs: Any) -> None" def f3(y: int) -> None: ... a3: Call = f3 # E: Incompatible types in assignment (expression has type "Callable[[int], None]", variable has type "Call") \ - # N: "Call.__call__" has type "Callable[[Arg(int, 'x'), VarArg(Any), KwArg(Any)], None]" + # N: "Call.__call__" has type "def __call__(self, x: int, *args: Any, **kwargs: Any) -> None" def f4(x: int) -> None: ... a4: Call = f4 diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index fd564c7e96cb2..fe8a1551f81bd 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -187,9 +187,9 @@ t: Tuple[int, str] if int(): b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") if int(): - a = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A") + a = t # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "A") if int(): - b = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B") + b = t # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "B") if int(): t = a if int(): @@ -212,7 +212,7 @@ a = l[0] (i,) = l[0] i, i = l[0] # E: Need more than 1 value to unpack (2 expected) l = [A(1)] -a = (1,) # E: Incompatible types in assignment (expression has type "Tuple[int]", \ +a = (1,) # E: Incompatible types in assignment (expression has type "tuple[int]", \ variable has type "A") [builtins fixtures/list.pyi] @@ -223,7 +223,7 @@ class MyNamedTuple(NamedTuple): a: int b: str -MyNamedTuple.x # E: "Type[MyNamedTuple]" has no attribute "x" +MyNamedTuple.x # E: "type[MyNamedTuple]" has no attribute "x" [builtins fixtures/tuple.pyi] [case testNewNamedTupleEmptyItems] @@ -281,7 +281,7 @@ class X(NamedTuple): y: str x: X -reveal_type(x._replace()) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]" +reveal_type(x._replace()) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.X]" x._replace(x=5) x._replace(y=5) # E: Argument "y" to "_replace" of "X" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] @@ -293,7 +293,7 @@ class X(NamedTuple): x: int y: str -reveal_type(X._fields) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(X._fields) # N: Revealed type is "tuple[builtins.str, builtins.str]" reveal_type(X._field_types) # N: Revealed type is "builtins.dict[builtins.str, Any]" reveal_type(X._field_defaults) # N: Revealed type is "builtins.dict[builtins.str, Any]" @@ -324,7 +324,7 @@ class Y(NamedTuple): x: int y: str -reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" +reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" [builtins fixtures/list.pyi] @@ -335,8 +335,8 @@ class X(NamedTuple): x: int y: str -reveal_type([(3, 'b'), X(1, 'a')]) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" -reveal_type([X(1, 'a'), (3, 'b')]) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" +reveal_type([(3, 'b'), X(1, 'a')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" +reveal_type([X(1, 'a'), (3, 'b')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" [builtins fixtures/list.pyi] @@ -386,8 +386,8 @@ class X(NamedTuple): x: int y: int = 2 -reveal_type(X(1)) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.X]" -reveal_type(X(1, 2)) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.X]" +reveal_type(X(1)) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.X]" +reveal_type(X(1, 2)) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.X]" X(1, 'a') # E: Argument 2 to "X" has incompatible type "str"; expected "int" X(1, z=3) # E: Unexpected keyword argument "z" for "X" @@ -396,14 +396,14 @@ class HasNone(NamedTuple): x: int y: Optional[int] = None -reveal_type(HasNone(1)) # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]" +reveal_type(HasNone(1)) # N: Revealed type is "tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]" class Parameterized(NamedTuple): x: int y: List[int] = [1] + [2] z: List[int] = [] -reveal_type(Parameterized(1)) # N: Revealed type is "Tuple[builtins.int, builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.Parameterized]" +reveal_type(Parameterized(1)) # N: Revealed type is "tuple[builtins.int, builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.Parameterized]" Parameterized(1, ['not an int']) # E: List item 0 has incompatible type "str"; expected "int" class Default: @@ -412,8 +412,8 @@ class Default: class UserDefined(NamedTuple): x: Default = Default() -reveal_type(UserDefined()) # N: Revealed type is "Tuple[__main__.Default, fallback=__main__.UserDefined]" -reveal_type(UserDefined(Default())) # N: Revealed type is "Tuple[__main__.Default, fallback=__main__.UserDefined]" +reveal_type(UserDefined()) # N: Revealed type is "tuple[__main__.Default, fallback=__main__.UserDefined]" +reveal_type(UserDefined(Default())) # N: Revealed type is "tuple[__main__.Default, fallback=__main__.UserDefined]" UserDefined(1) # E: Argument 1 to "UserDefined" has incompatible type "int"; expected "Default" [builtins fixtures/list.pyi] @@ -425,7 +425,7 @@ class HasNone(NamedTuple): x: int y: Optional[int] = None -reveal_type(HasNone(1)) # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]" +reveal_type(HasNone(1)) # N: Revealed type is "tuple[builtins.int, Union[builtins.int, None], fallback=__main__.HasNone]" HasNone(None) # E: Argument 1 to "HasNone" has incompatible type "None"; expected "int" HasNone(1, y=None) HasNone(1, y=2) @@ -463,7 +463,7 @@ class Y(X): self.y return self.x -reveal_type(Y('a')) # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.Y]" +reveal_type(Y('a')) # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.Y]" Y(y=1, x='1').method() class CallsBaseInit(X): @@ -511,7 +511,7 @@ class Overloader(NamedTuple): reveal_type(Overloader(1).method('string')) # N: Revealed type is "builtins.str" reveal_type(Overloader(1).method(1)) # N: Revealed type is "builtins.int" -Overloader(1).method(('tuple',)) # E: No overload variant of "method" of "Overloader" matches argument type "Tuple[str]" \ +Overloader(1).method(('tuple',)) # E: No overload variant of "method" of "Overloader" matches argument type "tuple[str]" \ # N: Possible overload variants: \ # N: def method(self, y: str) -> str \ # N: def method(self, y: int) -> int @@ -528,7 +528,7 @@ class Base(NamedTuple): reveal_type(self) # N: Revealed type is "T`-1" return self def good_override(self) -> int: - reveal_type(self) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Base]" + reveal_type(self) # N: Revealed type is "tuple[builtins.int, fallback=__main__.Base]" reveal_type(self[0]) # N: Revealed type is "builtins.int" self[0] = 3 # E: Unsupported target for indexed assignment ("Base") reveal_type(self.x) # N: Revealed type is "builtins.int" @@ -538,14 +538,14 @@ class Base(NamedTuple): # E: No overload variant of "__getitem__" of "tuple" matches argument type "TypeVar" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> int \ - # N: def __getitem__(self, slice, /) -> Tuple[int, ...] + # N: def __getitem__(self, slice, /) -> tuple[int, ...] return self.x def bad_override(self) -> int: return self.x class Child(Base): def new_method(self) -> int: - reveal_type(self) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Child]" + reveal_type(self) # N: Revealed type is "tuple[builtins.int, fallback=__main__.Child]" reveal_type(self[0]) # N: Revealed type is "builtins.int" self[0] = 3 # E: Unsupported target for indexed assignment ("Child") reveal_type(self.x) # N: Revealed type is "builtins.int" @@ -560,8 +560,8 @@ class Child(Base): def takes_base(base: Base) -> int: return base.x -reveal_type(Base(1).copy()) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Base]" -reveal_type(Child(1).copy()) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Child]" +reveal_type(Base(1).copy()) # N: Revealed type is "tuple[builtins.int, fallback=__main__.Base]" +reveal_type(Child(1).copy()) # N: Revealed type is "tuple[builtins.int, fallback=__main__.Child]" reveal_type(Base(1).good_override()) # N: Revealed type is "builtins.int" reveal_type(Child(1).good_override()) # N: Revealed type is "builtins.int" reveal_type(Base(1).bad_override()) # N: Revealed type is "builtins.int" @@ -635,8 +635,8 @@ class HasClassMethod(NamedTuple): @classmethod def new(cls, f: str) -> 'HasClassMethod': - reveal_type(cls) # N: Revealed type is "Type[Tuple[builtins.str, fallback=__main__.HasClassMethod]]" - reveal_type(HasClassMethod) # N: Revealed type is "def (x: builtins.str) -> Tuple[builtins.str, fallback=__main__.HasClassMethod]" + reveal_type(cls) # N: Revealed type is "type[tuple[builtins.str, fallback=__main__.HasClassMethod]]" + reveal_type(HasClassMethod) # N: Revealed type is "def (x: builtins.str) -> tuple[builtins.str, fallback=__main__.HasClassMethod]" return cls(x=f) [builtins fixtures/classmethod.pyi] @@ -661,7 +661,7 @@ class HasStaticMethod(NamedTuple): @property def size(self) -> int: - reveal_type(self) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.HasStaticMethod]" + reveal_type(self) # N: Revealed type is "tuple[builtins.str, fallback=__main__.HasStaticMethod]" return 4 [builtins fixtures/property.pyi] diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 82208d27df41a..0e9d6357af1ac 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -113,7 +113,7 @@ A().f = None # E: Cannot assign to a method \ from typing import Protocol class Base: - __hash__ = None + __hash__: None = None class Derived(Base): def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base" \ @@ -145,26 +145,25 @@ class Base: pass class Derived(Base): - __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]") - + __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[], int]") [case testOverridePartialAttributeWithMethod] # This was crashing: https://github.com/python/mypy/issues/11686. class Base: def __init__(self, arg: int): - self.partial_type = [] # E: Need type annotation for "partial_type" (hint: "partial_type: List[] = ...") + self.partial_type = [] # E: Need type annotation for "partial_type" (hint: "partial_type: list[] = ...") self.force_deferral = [] # Force inference of the `force_deferral` attribute in `__init__` to be # deferred to a later pass by providing a definition in another context, # which means `partial_type` remains only partially inferred. - force_deferral = [] # E: Need type annotation for "force_deferral" (hint: "force_deferral: List[] = ...") + force_deferral = [] # E: Need type annotation for "force_deferral" (hint: "force_deferral: list[] = ...") class Derived(Base): def partial_type(self) -> int: # E: Signature of "partial_type" incompatible with supertype "Base" \ # N: Superclass: \ - # N: List[Any] \ + # N: list[Any] \ # N: Subclass: \ # N: def partial_type(self) -> int ... @@ -398,7 +397,7 @@ class A: def __eq__(self, other: A) -> bool: pass # Fail [builtins fixtures/plugin_attrs.pyi] [out] -main:2: error: Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object" +main:2: error: Argument 1 of "__eq__" is incompatible with supertype "builtins.object"; supertype defines the argument type as "object" main:2: note: This violates the Liskov substitution principle main:2: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides main:2: note: It is recommended for "__eq__" to work with arbitrary objects, for example: @@ -473,6 +472,27 @@ class B(A): def __new__(cls) -> B: pass +[case testOverride__new__WithLiteralReturnPassing] +from typing import Literal + +class Falsy: + def __bool__(self) -> Literal[False]: pass + +reveal_type(bool(Falsy())) # N: Revealed type is "Literal[False]" +reveal_type(int()) # N: Revealed type is "Literal[0]" + +[builtins fixtures/literal__new__.pyi] +[typing fixtures/typing-medium.pyi] + +[case testOverride__new__WithLiteralReturnFailing] +from typing import Literal + +class Foo: + def __new__(cls) -> Literal[1]: pass # E: Incompatible return type for "__new__" (returns "Literal[1]", but must return a subtype of "Foo") + +[builtins fixtures/__new__.pyi] +[typing fixtures/typing-medium.pyi] + [case testOverride__new__AndCallObject] from typing import TypeVar, Generic @@ -565,6 +585,58 @@ class B(A): @dec def f(self) -> int: pass +[case testOverrideWithDecoratorReturningCallable] +from typing import Any, Callable, TypeVar + +class Base: + def get(self, a: str) -> None: ... + +def dec(fn: Any) -> Callable[[Any, int], None]: ... + +class Derived(Base): + @dec + def get(self) -> None: ... # E: Argument 1 of "get" is incompatible with supertype "Base"; supertype defines the argument type as "str" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +[builtins fixtures/tuple.pyi] + +[case testOverrideWithDecoratorReturningCallable2] +# flags: --pretty +from typing import Any, Callable, TypeVar + +_C = TypeVar("_C", bound=Callable[..., Any]) + +def infer_signature(f: _C) -> Callable[[Any], _C]: ... + +class Base: + def get(self, a: str, b: str, c: str) -> None: ... + def post(self, a: str, b: str) -> None: ... + +# Third argument incompatible +def get(self, a: str, b: str, c: int) -> None: ... + +# Second argument incompatible - still should not map to **kwargs +def post(self, a: str, b: int) -> None: ... + +class Derived(Base): + @infer_signature(get) + def get(self, *args: Any, **kwargs: Any) -> None: ... + + @infer_signature(post) + def post(self, *args: Any, **kwargs: Any) -> None: ... +[builtins fixtures/tuple.pyi] +[out] +main:20: error: Argument 3 of "get" is incompatible with supertype "Base"; supertype defines the argument type as "str" + def get(self, *args: Any, **kwargs: Any) -> None: ... + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +main:20: note: This violates the Liskov substitution principle +main:20: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +main:23: error: Argument 2 of "post" is incompatible with supertype "Base"; supertype defines the argument type as "str" + def post(self, *args: Any, **kwargs: Any) -> None: ... + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +main:23: note: This violates the Liskov substitution principle +main:23: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides + [case testOverrideWithDecoratorReturningInstance] def dec(f) -> str: pass @@ -688,6 +760,20 @@ class B(A): def h(cls) -> int: pass [builtins fixtures/classmethod.pyi] +[case testOverrideReplaceMethod] +# flags: --show-error-codes +from typing import Optional +from typing_extensions import Self +class A: + def __replace__(self, x: Optional[str]) -> Self: pass + +class B(A): + def __replace__(self, x: str) -> Self: pass # E: \ + # E: Argument 1 of "__replace__" is incompatible with supertype "A"; supertype defines the argument type as "Optional[str]" [override] \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +[builtins fixtures/tuple.pyi] + [case testAllowCovarianceInReadOnlyAttributes] from typing import Callable, TypeVar @@ -710,6 +796,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 + @f.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 + def f(self) -> Callable[[object], None]: pass + @f.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 -- ------------ @@ -1032,7 +1235,7 @@ b = A.x # type: B # E: Incompatible types in assignment (expression has type "A" [case testAccessingUndefinedAttributeViaClass] import typing class A: pass -A.x # E: "Type[A]" has no attribute "x" +A.x # E: "type[A]" has no attribute "x" [case testAccessingUndefinedAttributeViaClassWithOverloadedInit] from foo import * @@ -1043,7 +1246,7 @@ class A: def __init__(self): pass @overload def __init__(self, x): pass -A.x # E: "Type[A]" has no attribute "x" +A.x # E: "type[A]" has no attribute "x" [case testAccessMethodOfClassWithOverloadedInit] from foo import * @@ -1097,7 +1300,7 @@ import typing class A: class B: pass A.B = None # E: Cannot assign to a type \ - # E: Incompatible types in assignment (expression has type "None", variable has type "Type[B]") + # E: Incompatible types in assignment (expression has type "None", variable has type "type[B]") [targets __main__] [case testAccessingClassAttributeWithTypeInferenceIssue] @@ -1113,7 +1316,7 @@ class C: x = C.x [builtins fixtures/list.pyi] [out] -main:2: error: Need type annotation for "x" (hint: "x: List[] = ...") +main:2: error: Need type annotation for "x" (hint: "x: list[] = ...") [case testAccessingGenericClassAttribute] from typing import Generic, TypeVar @@ -1380,7 +1583,7 @@ class C: cls(1) # E: Too many arguments for "C" cls.bar() cls.bar(1) # E: Too many arguments for "bar" of "C" - cls.bozo() # E: "Type[C]" has no attribute "bozo" + cls.bozo() # E: "type[C]" has no attribute "bozo" [builtins fixtures/classmethod.pyi] [out] @@ -1391,7 +1594,7 @@ class C: def foo(cls) -> None: pass C.foo() C.foo(1) # E: Too many arguments for "foo" of "C" -C.bozo() # E: "Type[C]" has no attribute "bozo" +C.bozo() # E: "type[C]" has no attribute "bozo" [builtins fixtures/classmethod.pyi] [case testClassMethodCalledOnInstance] @@ -1401,7 +1604,7 @@ class C: def foo(cls) -> None: pass C().foo() C().foo(1) # E: Too many arguments for "foo" of "C" -C.bozo() # E: "Type[C]" has no attribute "bozo" +C.bozo() # E: "type[C]" has no attribute "bozo" [builtins fixtures/classmethod.pyi] [case testClassMethodMayCallAbstractMethod] @@ -1492,7 +1695,81 @@ class A: self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") return '' [builtins fixtures/property.pyi] + +[case testPropertyNameIsChecked] +class A: + @property + def f(self) -> str: ... + @not_f.setter # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... + +a = A() +reveal_type(a.f) # N: Revealed type is "builtins.str" +a.f = '' # E: Property "f" defined in "A" is read-only + +class B: + @property + def f(self) -> str: ... + @not_f.deleter # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self) -> None: ... + +class C: + @property + def f(self) -> str: ... + @not_f.setter # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... + @not_f.deleter # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self) -> None: ... +[builtins fixtures/property.pyi] + +[case testPropertyAttributeIsChecked] +class A: + @property + def f(self) -> str: ... + @f.unknown # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... + @f.bad.setter # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... + @f # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... + @int # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... +[builtins fixtures/property.pyi] + +[case testPropertyNameAndAttributeIsCheckedPretty] +# flags: --pretty +class A: + @property + def f(self) -> str: ... + @not_f.setter + def f(self, val: str) -> None: ... + @not_f.deleter + def f(self) -> None: ... + +class B: + @property + def f(self) -> str: ... + @f.unknown + def f(self, val: str) -> None: ... +[builtins fixtures/property.pyi] [out] +main:5: error: Only supported top decorators are "@f.setter" and "@f.deleter" + @not_f.setter + ^~~~~~~~~~~~ +main:7: error: Only supported top decorators are "@f.setter" and "@f.deleter" + @not_f.deleter + ^~~~~~~~~~~~~ +main:13: error: Only supported top decorators are "@f.setter" and "@f.deleter" + @f.unknown + ^~~~~~~~~ + +[case testPropertyGetterDecoratorIsRejected] +class A: + @property + def f(self) -> str: ... + @f.getter # E: Only supported top decorators are "@f.setter" and "@f.deleter" + def f(self, val: str) -> None: ... +[builtins fixtures/property.pyi] [case testDynamicallyTypedProperty] import typing @@ -1661,12 +1938,12 @@ class D: def __get__(self, inst: Base, own: Type[Base]) -> str: pass [builtins fixtures/bool.pyi] [out] -main:4: error: Argument 2 to "__get__" of "D" has incompatible type "Type[A]"; expected "Type[Base]" +main:4: error: Argument 2 to "__get__" of "D" has incompatible type "type[A]"; expected "type[Base]" main:4: note: Revealed type is "d.D" -main:5: error: No overload variant of "__get__" of "D" matches argument types "A", "Type[A]" +main:5: error: No overload variant of "__get__" of "D" matches argument types "A", "type[A]" main:5: note: Possible overload variants: -main:5: note: def __get__(self, inst: None, own: Type[Base]) -> D -main:5: note: def __get__(self, inst: Base, own: Type[Base]) -> str +main:5: note: def __get__(self, inst: None, own: type[Base]) -> D +main:5: note: def __get__(self, inst: Base, own: type[Base]) -> str main:5: note: Revealed type is "Any" [case testAccessingGenericNonDataDescriptor] @@ -1760,10 +2037,10 @@ class D(Generic[T, V]): def __get__(self, inst: T, own: Type[T]) -> V: pass [builtins fixtures/bool.pyi] [out] -main:4: error: No overload variant of "__get__" of "D" matches argument types "None", "Type[A]" +main:4: error: No overload variant of "__get__" of "D" matches argument types "None", "type[A]" main:4: note: Possible overload variants: main:4: note: def __get__(self, inst: None, own: None) -> D[A, int] -main:4: note: def __get__(self, inst: A, own: Type[A]) -> int +main:4: note: def __get__(self, inst: A, own: type[A]) -> int main:4: note: Revealed type is "Any" [case testAccessingNonDataDescriptorSubclass] @@ -1922,7 +2199,7 @@ class D: def __get__(self, inst: Any, own: str) -> Any: pass class A: f = D() -A().f # E: Argument 2 to "__get__" of "D" has incompatible type "Type[A]"; expected "str" +A().f # E: Argument 2 to "__get__" of "D" has incompatible type "type[A]"; expected "str" [case testDescriptorGetSetDifferentTypes] from typing import Any @@ -2103,7 +2380,7 @@ reveal_type(B() + A()) # N: Revealed type is "__main__.A" reveal_type(A() + B()) # N: Revealed type is "__main__.A" [builtins fixtures/isinstance.pyi] -[case testBinaryOpeartorMethodPositionalArgumentsOnly] +[case testBinaryOperatorMethodPositionalArgumentsOnly] class A: def __add__(self, other: int) -> int: pass def __iadd__(self, other: int) -> int: pass @@ -2357,6 +2634,60 @@ reveal_type(Num3() + Num1()) # N: Revealed type is "__main__.Num3" reveal_type(Num2() + Num3()) # N: Revealed type is "__main__.Num2" reveal_type(Num3() + Num2()) # N: Revealed type is "__main__.Num3" +[case testReverseOperatorWithOverloads3] +from typing import Union, overload + +class A: + def __mul__(self, value: A, /) -> A: ... + def __rmul__(self, value: A, /) -> A: ... + +class B: + @overload + def __mul__(self, other: B, /) -> B: ... + @overload + def __mul__(self, other: A, /) -> str: ... + def __mul__(self, other: Union[B, A], /) -> Union[B, str]: pass + + @overload + def __rmul__(self, other: B, /) -> B: ... + @overload + def __rmul__(self, other: A, /) -> str: ... + def __rmul__(self, other: Union[B, A], /) -> Union[B, str]: pass + +[case testReverseOperatorWithOverloadsNested] +from typing import Union, overload + +class A: + def __mul__(self, value: A, /) -> A: ... + def __rmul__(self, value: A, /) -> A: ... + +class B: + @overload + def __mul__(self, other: B, /) -> B: ... + @overload + def __mul__(self, other: A, /) -> str: ... + def __mul__(self, other: Union[B, A], /) -> Union[B, str]: pass + + @overload + def __rmul__(self, other: B, /) -> B: ... + @overload + def __rmul__(self, other: A, /) -> str: ... + def __rmul__(self, other: Union[B, A], /) -> Union[B, str]: + class A1: + def __add__(self, other: C1) -> int: ... + + class B1: + def __add__(self, other: C1) -> int: ... + + class C1: + @overload + def __radd__(self, other: A1) -> str: ... # E: Signatures of "__radd__" of "C1" and "__add__" of "A1" are unsafely overlapping + @overload + def __radd__(self, other: B1) -> str: ... # E: Signatures of "__radd__" of "C1" and "__add__" of "B1" are unsafely overlapping + def __radd__(self, other): pass + + return "" + [case testDivReverseOperator] # No error: __div__ has no special meaning in Python 3 class A1: @@ -2486,7 +2817,7 @@ from typing import TypeVar, Type class Real(type): def __add__(self, other: FractionChild) -> str: ... class Fraction(Real): - def __radd__(self, other: Type['A']) -> Real: ... # E: Signatures of "__radd__" of "Fraction" and "__add__" of "Type[A]" are unsafely overlapping + def __radd__(self, other: Type['A']) -> Real: ... # E: Signatures of "__radd__" of "Fraction" and "__add__" of "type[A]" are unsafely overlapping class FractionChild(Fraction): pass class A(metaclass=Real): pass @@ -3003,9 +3334,13 @@ b.bad = 'a' # E: Incompatible types in assignment (expression has type "str", v from typing import Any class Test: - def __setattr__() -> None: ... # E: Method must have at least one argument. Did you forget the "self" argument? # E: Invalid signature "Callable[[], None]" for "__setattr__" + def __setattr__() -> None: ... \ + # E: Invalid signature "Callable[[], None]" for "__setattr__" \ + # E: Method must have at least one argument. Did you forget the "self" argument? + t = Test() -t.crash = 'test' # E: "Test" has no attribute "crash" +t.crash = 'test' # E: Attribute function "__setattr__" with type "Callable[[], None]" does not accept self argument \ + # E: "Test" has no attribute "crash" class A: def __setattr__(self): ... # E: Invalid signature "Callable[[A], Any]" for "__setattr__" @@ -3112,7 +3447,7 @@ class C: def f(x: type) -> None: pass def g(x: int) -> None: pass f(C) -g(C) # E: Argument 1 to "g" has incompatible type "Type[C]"; expected "int" +g(C) # E: Argument 1 to "g" has incompatible type "type[C]"; expected "int" [builtins fixtures/__new__.pyi] [case testClassWith__new__AndCompatibilityWithType2] @@ -3123,7 +3458,7 @@ class C: def f(x: type) -> None: pass def g(x: int) -> None: pass f(C) -g(C) # E: Argument 1 to "g" has incompatible type "Type[C]"; expected "int" +g(C) # E: Argument 1 to "g" has incompatible type "type[C]"; expected "int" [builtins fixtures/__new__.pyi] [case testGenericClassWith__new__] @@ -3208,7 +3543,7 @@ class B: [case testClassVsInstanceDisambiguation] class A: pass def f(x: A) -> None: pass -f(A) # E: Argument 1 to "f" has incompatible type "Type[A]"; expected "A" +f(A) # E: Argument 1 to "f" has incompatible type "type[A]"; expected "A" [out] -- TODO @@ -3262,7 +3597,7 @@ class A(Generic[T]): class B(Generic[T]): a: Type[A[T]] = A -reveal_type(B[int]().a) # N: Revealed type is "Type[__main__.A[builtins.int]]" +reveal_type(B[int]().a) # N: Revealed type is "type[__main__.A[builtins.int]]" B[int]().a('hi') # E: Argument 1 to "A" has incompatible type "str"; expected "int" class C(Generic[T]): @@ -3417,7 +3752,7 @@ class User: pass def new_user(user_class: Type[User]): return user_class() def foo(arg: Type[int]): - new_user(arg) # E: Argument 1 to "new_user" has incompatible type "Type[int]"; expected "Type[User]" + new_user(arg) # E: Argument 1 to "new_user" has incompatible type "type[int]"; expected "type[User]" [out] [case testTypeUsingTypeCUnionOverload] @@ -3456,8 +3791,8 @@ def foo(arg: Type[Any]): arg.new_member_name = 42 # Member access is ok and types as Any reveal_type(x) # N: Revealed type is "Any" - # But Type[Any] is distinct from Any - y: int = arg # E: Incompatible types in assignment (expression has type "Type[Any]", variable has type "int") + # But type[Any] is distinct from Any + y: int = arg # E: Incompatible types in assignment (expression has type "type[Any]", variable has type "int") [out] [case testTypeUsingTypeCTypeAnyMemberFallback] @@ -3498,7 +3833,7 @@ def process(cls: Type[User]): obj = cls() reveal_type(cls.bar(obj)) # N: Revealed type is "builtins.int" cls.mro() # Defined in class type - cls.error # E: "Type[User]" has no attribute "error" + cls.error # E: "type[User]" has no attribute "error" [builtins fixtures/classmethod.pyi] [out] @@ -3515,7 +3850,7 @@ def process(cls: Type[Union[BasicUser, ProUser]]): obj = cls() cls.bar(obj) cls.mro() # Defined in class type - cls.error # E: Item "type" of "Union[Type[BasicUser], Type[ProUser]]" has no attribute "error" + cls.error # E: Item "type" of "Union[type[BasicUser], type[ProUser]]" has no attribute "error" [builtins fixtures/classmethod.pyi] [out] @@ -3531,7 +3866,7 @@ def process(cls: Type[U]): obj = cls() reveal_type(cls.bar(obj)) # N: Revealed type is "builtins.int" cls.mro() # Defined in class type - cls.error # E: "Type[U]" has no attribute "error" + cls.error # E: "type[U]" has no attribute "error" [builtins fixtures/classmethod.pyi] [out] @@ -3550,14 +3885,14 @@ def process(cls: Type[U]): obj = cls() cls.bar(obj) cls.mro() # Defined in class type - cls.error # E: "Type[U]" has no attribute "error" + cls.error # E: "type[U]" has no attribute "error" [builtins fixtures/classmethod.pyi] [out] [case testTypeUsingTypeCErrorUnsupportedType] from typing import Type, Tuple def foo(arg: Type[Tuple[int]]): - arg() # E: Cannot instantiate type "Type[Tuple[int]]" + arg() # E: Cannot instantiate type "type[tuple[int]]" [builtins fixtures/tuple.pyi] [case testTypeUsingTypeCOverloadedClass] @@ -3579,7 +3914,7 @@ def new(uc: Type[U]) -> U: if 1: u = uc(0) u.foo() - u = uc('') # Error + uc('') # Error u.foo(0) # Error return uc() u = new(User) @@ -3601,7 +3936,7 @@ def f(a: T): pass [case testTypeUsingTypeCTuple] from typing import Type, Tuple def f(a: Type[Tuple[int, int]]): - a() # E: Cannot instantiate type "Type[Tuple[int, int]]" + a() # E: Cannot instantiate type "type[tuple[int, int]]" [builtins fixtures/tuple.pyi] [case testTypeUsingTypeCNamedTuple] @@ -3624,7 +3959,7 @@ def foo(c: Type[C], d: Type[D]) -> None: [builtins fixtures/list.pyi] [out] -main:7: note: Revealed type is "builtins.list[Type[__main__.B]]" +main:7: note: Revealed type is "builtins.list[type[__main__.B]]" [case testTypeEquivalentTypeAny] from typing import Type, Any @@ -3761,9 +4096,9 @@ def f(a: int) -> Type[User]: def f(a: str) -> User: return User() -reveal_type(f(User())) # N: Revealed type is "Type[foo.User]" +reveal_type(f(User())) # N: Revealed type is "type[foo.User]" reveal_type(f(User)) # N: Revealed type is "foo.User" -reveal_type(f(3)) # N: Revealed type is "Type[foo.User]" +reveal_type(f(3)) # N: Revealed type is "type[foo.User]" reveal_type(f("hi")) # N: Revealed type is "foo.User" [builtins fixtures/classmethod.pyi] [out] @@ -3803,7 +4138,7 @@ def f(a: type) -> None: pass f(3) # E: No overload variant of "f" matches argument type "int" \ # N: Possible overload variants: \ - # N: def f(a: Type[User]) -> None \ + # N: def f(a: type[User]) -> None \ # N: def f(a: type) -> None [builtins fixtures/classmethod.pyi] [out] @@ -3823,7 +4158,7 @@ def f(a: int) -> None: pass f(User) f(User()) # E: No overload variant of "f" matches argument type "User" \ # N: Possible overload variants: \ - # N: def f(a: Type[User]) -> None \ + # N: def f(a: type[User]) -> None \ # N: def f(a: int) -> None [builtins fixtures/classmethod.pyi] [out] @@ -3845,10 +4180,10 @@ def f(a: Type[B]) -> None: pass @overload def f(a: int) -> None: pass -f(A) # E: Argument 1 to "f" has incompatible type "Type[A]"; expected "Type[B]" +f(A) # E: Argument 1 to "f" has incompatible type "type[A]"; expected "type[B]" f(B) f(C) -f(AType) # E: Argument 1 to "f" has incompatible type "Type[A]"; expected "Type[B]" +f(AType) # E: Argument 1 to "f" has incompatible type "type[A]"; expected "type[B]" f(BType) f(CType) [builtins fixtures/classmethod.pyi] @@ -4077,7 +4412,7 @@ class User: u = User() -reveal_type(type(u)) # N: Revealed type is "Type[__main__.User]" +reveal_type(type(u)) # N: Revealed type is "type[__main__.User]" reveal_type(type(u).test_class_method()) # N: Revealed type is "builtins.int" reveal_type(type(u).test_static_method()) # N: Revealed type is "builtins.str" type(u).test_instance_method() # E: Missing positional argument "self" in call to "test_instance_method" of "User" @@ -4096,8 +4431,8 @@ def f2(func: A) -> A: u = User() -reveal_type(f1(u)) # N: Revealed type is "Type[__main__.User]" -reveal_type(f2(type)(u)) # N: Revealed type is "Type[__main__.User]" +reveal_type(f1(u)) # N: Revealed type is "type[__main__.User]" +reveal_type(f2(type)(u)) # N: Revealed type is "type[__main__.User]" [builtins fixtures/classmethod.pyi] [out] @@ -4109,7 +4444,7 @@ def fake1(a: object) -> type: def fake2(a: int) -> type: return User -reveal_type(type(User())) # N: Revealed type is "Type[__main__.User]" +reveal_type(type(User())) # N: Revealed type is "type[__main__.User]" reveal_type(fake1(User())) # N: Revealed type is "builtins.type" reveal_type(fake2(3)) # N: Revealed type is "builtins.type" [builtins fixtures/classmethod.pyi] @@ -4161,7 +4496,7 @@ int.__eq__(3, 4) [builtins fixtures/args.pyi] [out] main:33: error: Too few arguments for "__eq__" of "int" -main:33: error: Unsupported operand types for == ("int" and "Type[int]") +main:33: error: Unsupported operand types for == ("type[int]" and "type[int]") [case testDupBaseClasses] class A: @@ -4323,7 +4658,7 @@ class A: def a(self) -> None: pass b = 1 class B(A): - a = 1 # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[A], None]") + a = 1 # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[], None]") def b(self) -> None: pass # E: Signature of "b" incompatible with supertype "A" \ # N: Superclass: \ # N: int \ @@ -4416,20 +4751,20 @@ main:7: error: Incompatible types in assignment (expression has type "Callable[[ [case testClassSpec] from typing import Callable class A(): - b = None # type: Callable[[A, int], int] + b = None # type: Callable[[int], int] class B(A): def c(self, a: int) -> int: pass b = c +reveal_type(A().b) # N: Revealed type is "def (builtins.int) -> builtins.int" +reveal_type(B().b) # N: Revealed type is "def (a: builtins.int) -> builtins.int" [case testClassSpecError] from typing import Callable class A(): - b = None # type: Callable[[A, int], int] + b = None # type: Callable[[int], int] class B(A): def c(self, a: str) -> int: pass - b = c -[out] -main:6: error: Incompatible types in assignment (expression has type "Callable[[str], int]", base class "A" defined the type as "Callable[[int], int]") + b = c # E: Incompatible types in assignment (expression has type "Callable[[str], int]", base class "A" defined the type as "Callable[[int], int]") [case testClassStaticMethod] class A(): @@ -4451,10 +4786,28 @@ class A(): class B(A): @staticmethod def b(a: str) -> None: pass - c = b + c = b # E: Incompatible types in assignment (expression has type "Callable[[str], None]", base class "A" defined the type as "Callable[[int], None]") +a: A +reveal_type(a.a) # N: Revealed type is "def (a: builtins.int)" +reveal_type(a.c) # N: Revealed type is "def (a: builtins.int)" +[builtins fixtures/staticmethod.pyi] + +[case testClassStaticMethodIndirectOverloaded] +from typing import overload +class A: + @overload + @staticmethod + def a(x: int) -> int: ... + @overload + @staticmethod + def a(x: str) -> str: ... + @staticmethod + def a(x): + ... + c = a +reveal_type(A.c) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)" +reveal_type(A().c) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)" [builtins fixtures/staticmethod.pyi] -[out] -main:8: error: Incompatible types in assignment (expression has type "Callable[[str], None]", base class "A" defined the type as "Callable[[int], None]") [case testClassStaticMethodSubclassing] class A: @@ -4519,22 +4872,20 @@ class B(A): class A: x = 1 class B(A): - x = "a" + x = "a" # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") class C(B): - x = object() -[out] -main:4: error: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") -main:6: error: Incompatible types in assignment (expression has type "object", base class "A" defined the type as "int") + x = object() # E: Incompatible types in assignment (expression has type "object", base class "B" defined the type as "str") [case testClassOneErrorPerLine] class A: - x = 1 + x = 1 class B(A): - x = "" - x = 1.0 -[out] -main:4: error: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") -main:5: error: Incompatible types in assignment (expression has type "float", base class "A" defined the type as "int") + x: str = "" # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") + x = 1.0 # E: Incompatible types in assignment (expression has type "float", variable has type "str") +class BInfer(A): + x = "" # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") + x = 1.0 # E: Incompatible types in assignment (expression has type "float", variable has type "str") \ + # E: Incompatible types in assignment (expression has type "float", base class "A" defined the type as "int") [case testClassIgnoreType_RedefinedAttributeAndGrandparentAttributeTypesNotIgnored] class A: @@ -4542,8 +4893,7 @@ class A: class B(A): x = '' # type: ignore class C(B): - x = '' # E: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") -[out] + x = '' [case testClassIgnoreType_RedefinedAttributeTypeIgnoredInChildren] class A: @@ -4552,21 +4902,20 @@ class B(A): x = '' # type: ignore class C(B): x = '' # type: ignore -[out] [case testInvalidMetaclassStructure] class X(type): pass class Y(type): pass class A(metaclass=X): pass -class B(A, metaclass=Y): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases - +class B(A, metaclass=Y): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.Y" (metaclass of "__main__.B") conflicts with "__main__.X" (metaclass of "__main__.A") [case testMetaclassNoTypeReveal] class M: x = 0 # type: int class A(metaclass=M): pass # E: Metaclasses not inheriting from "type" are not supported -A.x # E: "Type[A]" has no attribute "x" +A.x # E: "type[A]" has no attribute "x" [case testMetaclassTypeReveal] from typing import Type @@ -4576,7 +4925,7 @@ class M(type): class A(metaclass=M): pass def f(TA: Type[A]): - reveal_type(TA) # N: Revealed type is "Type[__main__.A]" + reveal_type(TA) # N: Revealed type is "type[__main__.A]" reveal_type(TA.x) # N: Revealed type is "builtins.int" [case testMetaclassConflictingInstanceVars] @@ -4629,7 +4978,7 @@ class A(metaclass=M): pass class B(A): pass def f(TB: Type[B]): - reveal_type(TB) # N: Revealed type is "Type[__main__.B]" + reveal_type(TB) # N: Revealed type is "type[__main__.B]" reveal_type(TB.x) # N: Revealed type is "builtins.int" [case testMetaclassAsAny] @@ -4770,7 +5119,7 @@ class Concrete(metaclass=Meta): pass reveal_type(Concrete + X()) # N: Revealed type is "builtins.str" -Concrete + "hello" # E: Unsupported operand types for + ("Type[Concrete]" and "str") +Concrete + "hello" # E: Unsupported operand types for + ("type[Concrete]" and "str") [case testMetaclassOperatorTypeVar] from typing import Type, TypeVar @@ -4880,7 +5229,7 @@ class A(metaclass=M): # E: Invalid metaclass "M" class B(metaclass=MM): # E: Invalid metaclass "MM" y = 0 reveal_type(A.y) # N: Revealed type is "builtins.int" -A.x # E: "Type[A]" has no attribute "x" +A.x # E: "type[A]" has no attribute "x" [case testAnyAsBaseOfMetaclass] from typing import Any, Type @@ -4895,7 +5244,7 @@ class A(metaclass=MM): def h(a: Type[A], b: Type[object]) -> None: h(a, a) - h(b, a) # E: Argument 1 to "h" has incompatible type "Type[object]"; expected "Type[A]" + h(b, a) # E: Argument 1 to "h" has incompatible type "type[object]"; expected "type[A]" a.f(1) # E: Too many arguments for "f" of "A" reveal_type(a.y) # N: Revealed type is "builtins.int" @@ -4920,9 +5269,9 @@ TTA = TypeVar('TTA', bound='Type[A]') TM = TypeVar('TM', bound='M') class M(type): - def g1(cls: 'Type[A]') -> A: pass # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "__main__.M" - def g2(cls: Type[TA]) -> TA: pass # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "__main__.M" - def g3(cls: TTA) -> TTA: pass # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "__main__.M" + def g1(cls: 'Type[A]') -> A: pass # E: The erased type of self "type[__main__.A]" is not a supertype of its class "__main__.M" + def g2(cls: Type[TA]) -> TA: pass # E: The erased type of self "type[__main__.A]" is not a supertype of its class "__main__.M" + def g3(cls: TTA) -> TTA: pass # E: The erased type of self "type[__main__.A]" is not a supertype of its class "__main__.M" def g4(cls: TM) -> TM: pass m: M @@ -4937,23 +5286,23 @@ reveal_type(A.g4) # N: Revealed type is "def () -> def () -> __main__.A" class B(metaclass=M): def foo(self): pass -B.g1 # E: Invalid self argument "Type[B]" to attribute function "g1" with type "Callable[[Type[A]], A]" -B.g2 # E: Invalid self argument "Type[B]" to attribute function "g2" with type "Callable[[Type[TA]], TA]" -B.g3 # E: Invalid self argument "Type[B]" to attribute function "g3" with type "Callable[[TTA], TTA]" +B.g1 # E: Invalid self argument "type[B]" to attribute function "g1" with type "Callable[[type[A]], A]" +B.g2 # E: Invalid self argument "type[B]" to attribute function "g2" with type "Callable[[type[TA]], TA]" +B.g3 # E: Invalid self argument "type[B]" to attribute function "g3" with type "Callable[[TTA], TTA]" reveal_type(B.g4) # N: Revealed type is "def () -> def () -> __main__.B" # 4 examples of unsoundness - instantiation, classmethod, staticmethod and ClassVar: -ta: Type[A] = m # E: Incompatible types in assignment (expression has type "M", variable has type "Type[A]") +ta: Type[A] = m # E: Incompatible types in assignment (expression has type "M", variable has type "type[A]") a: A = ta() reveal_type(ta.g1) # N: Revealed type is "def () -> __main__.A" reveal_type(ta.g2) # N: Revealed type is "def () -> __main__.A" -reveal_type(ta.g3) # N: Revealed type is "def () -> Type[__main__.A]" -reveal_type(ta.g4) # N: Revealed type is "def () -> Type[__main__.A]" +reveal_type(ta.g3) # N: Revealed type is "def () -> type[__main__.A]" +reveal_type(ta.g4) # N: Revealed type is "def () -> type[__main__.A]" x: M = ta -x.g1 # E: Invalid self argument "M" to attribute function "g1" with type "Callable[[Type[A]], A]" -x.g2 # E: Invalid self argument "M" to attribute function "g2" with type "Callable[[Type[TA]], TA]" +x.g1 # E: Invalid self argument "M" to attribute function "g1" with type "Callable[[type[A]], A]" +x.g2 # E: Invalid self argument "M" to attribute function "g2" with type "Callable[[type[TA]], TA]" x.g3 # E: Invalid self argument "M" to attribute function "g3" with type "Callable[[TTA], TTA]" reveal_type(x.g4) # N: Revealed type is "def () -> __main__.M" @@ -4966,7 +5315,7 @@ class Class(metaclass=M): def f1(cls: Type[Class]) -> None: pass @classmethod def f2(cls: M) -> None: pass -cl: Type[Class] = m # E: Incompatible types in assignment (expression has type "M", variable has type "Type[Class]") +cl: Type[Class] = m # E: Incompatible types in assignment (expression has type "M", variable has type "type[Class]") reveal_type(cl.f1) # N: Revealed type is "def ()" reveal_type(cl.f2) # N: Revealed type is "def ()" x1: M = cl @@ -4974,14 +5323,14 @@ x1: M = cl class Static(metaclass=M): @staticmethod def f() -> None: pass -s: Type[Static] = m # E: Incompatible types in assignment (expression has type "M", variable has type "Type[Static]") +s: Type[Static] = m # E: Incompatible types in assignment (expression has type "M", variable has type "type[Static]") reveal_type(s.f) # N: Revealed type is "def ()" x2: M = s from typing import ClassVar class Cvar(metaclass=M): x = 1 # type: ClassVar[int] -cv: Type[Cvar] = m # E: Incompatible types in assignment (expression has type "M", variable has type "Type[Cvar]") +cv: Type[Cvar] = m # E: Incompatible types in assignment (expression has type "M", variable has type "type[Cvar]") cv.x x3: M = cv @@ -5050,15 +5399,16 @@ def test() -> None: N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition) \ # N: Recursive types are not allowed at function scope n: N - reveal_type(n) # N: Revealed type is "Tuple[Any, fallback=__main__.N@4]" + reveal_type(n) # N: Revealed type is "tuple[Any, fallback=__main__.N@4]" [builtins fixtures/tuple.pyi] [case testCrashOnSelfRecursiveTypedDictVar] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'a': 'A'}) # type: ignore a: A [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCrashInJoinOfSelfRecursiveNamedTuples] @@ -5075,7 +5425,7 @@ lst = [n, m] [builtins fixtures/isinstancelist.pyi] [case testCorrectJoinOfSelfRecursiveTypedDicts] -from mypy_extensions import TypedDict +from typing import TypedDict def test() -> None: class N(TypedDict): @@ -5090,6 +5440,7 @@ def test() -> None: lst = [n, m] reveal_type(lst[0]['x']) # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCrashInForwardRefToNamedTupleWithIsinstance] from typing import Dict, NamedTuple @@ -5101,13 +5452,12 @@ class NameInfo(NamedTuple): def parse_ast(name_dict: NameDict) -> None: if isinstance(name_dict[''], int): pass - reveal_type(name_dict['test']) # N: Revealed type is "Tuple[builtins.bool, fallback=__main__.NameInfo]" + reveal_type(name_dict['test']) # N: Revealed type is "tuple[builtins.bool, fallback=__main__.NameInfo]" [builtins fixtures/isinstancelist.pyi] [typing fixtures/typing-medium.pyi] [case testCrashInForwardRefToTypedDictWithIsinstance] -from mypy_extensions import TypedDict -from typing import Dict +from typing import Dict, TypedDict NameDict = Dict[str, 'NameInfo'] class NameInfo(TypedDict): @@ -5118,7 +5468,7 @@ def parse_ast(name_dict: NameDict) -> None: pass reveal_type(name_dict['']['ast']) # N: Revealed type is "builtins.bool" [builtins fixtures/isinstancelist.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCorrectIsinstanceInForwardRefToNewType] from typing import Dict, NewType @@ -5183,13 +5533,13 @@ x = NT(N(1)) [case testNewTypeFromForwardTypedDict] -from typing import NewType, Tuple -from mypy_extensions import TypedDict +from typing import NewType, Tuple, TypedDict NT = NewType('NT', 'N') # E: Argument 2 to NewType(...) must be subclassable (got "N") class N(TypedDict): x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCorrectAttributeInForwardRefToNamedTuple] @@ -5205,7 +5555,7 @@ class Process(NamedTuple): [out] [case testCorrectItemTypeInForwardRefToTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict proc: Process reveal_type(proc['state']) # N: Revealed type is "builtins.int" @@ -5214,6 +5564,7 @@ def get_state(proc: 'Process') -> int: class Process(TypedDict): state: int [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCorrectDoubleForwardNamedTuple] @@ -5232,7 +5583,7 @@ reveal_type(x.one.attr) # N: Revealed type is "builtins.str" [out] [case testCrashOnDoubleForwardTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict x: A class A(TypedDict): @@ -5243,6 +5594,7 @@ class B(TypedDict): reveal_type(x['one']['attr']) # N: Revealed type is "builtins.str" [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashOnForwardUnionOfNamedTuples] @@ -5256,14 +5608,13 @@ class Bar(NamedTuple): def foo(node: Node) -> int: x = node - reveal_type(node) # N: Revealed type is "Union[Tuple[builtins.int, fallback=__main__.Foo], Tuple[builtins.int, fallback=__main__.Bar]]" + reveal_type(node) # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.Foo], tuple[builtins.int, fallback=__main__.Bar]]" return x.x [builtins fixtures/tuple.pyi] [out] [case testCrashOnForwardUnionOfTypedDicts] -from mypy_extensions import TypedDict -from typing import Union +from typing import TypedDict, Union NodeType = Union['Foo', 'Bar'] class Foo(TypedDict): @@ -5275,6 +5626,7 @@ def foo(node: NodeType) -> int: x = node return x['x'] [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testSupportForwardUnionOfNewTypes] @@ -5334,15 +5686,14 @@ ForwardUnion = Union['TP', int] class TP(NamedTuple('TP', [('x', int)])): pass def f(x: ForwardUnion) -> None: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, fallback=__main__.TP], builtins.int]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.TP], builtins.int]" if isinstance(x, TP): - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.TP]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.TP]" [builtins fixtures/isinstance.pyi] [out] [case testCrashInvalidArgsSyntheticClassSyntax] -from typing import List, NamedTuple -from mypy_extensions import TypedDict +from typing import List, NamedTuple, TypedDict class TD(TypedDict): x: List[int, str] # E: "list" expects 1 type argument, but 2 given class NM(NamedTuple): @@ -5352,11 +5703,11 @@ class NM(NamedTuple): TD({'x': []}) NM(x=[]) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashInvalidArgsSyntheticClassSyntaxReveals] -from typing import List, NamedTuple -from mypy_extensions import TypedDict +from typing import List, NamedTuple, TypedDict class TD(TypedDict): x: List[int, str] # E: "list" expects 1 type argument, but 2 given class NM(NamedTuple): @@ -5368,14 +5719,14 @@ y: NM y1 = NM(x=[]) reveal_type(x) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})" reveal_type(x1) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})" -reveal_type(y) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]" -reveal_type(y1) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]" +reveal_type(y) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.NM]" +reveal_type(y1) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.NM]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashInvalidArgsSyntheticFunctionSyntax] -from typing import List, NewType, NamedTuple -from mypy_extensions import TypedDict +from typing import List, NewType, NamedTuple, TypedDict TD = TypedDict('TD', {'x': List[int, str]}) # E: "list" expects 1 type argument, but 2 given NM = NamedTuple('NM', [('x', List[int, str])]) # E: "list" expects 1 type argument, but 2 given NT = NewType('NT', List[int, str]) # E: "list" expects 1 type argument, but 2 given @@ -5385,11 +5736,11 @@ TD({'x': []}) NM(x=[]) NT([]) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashForwardSyntheticClassSyntax] -from typing import NamedTuple -from mypy_extensions import TypedDict +from typing import NamedTuple, TypedDict class A1(NamedTuple): b: 'B' x: int @@ -5403,11 +5754,11 @@ y: A2 reveal_type(x.b) # N: Revealed type is "__main__.B" reveal_type(y['b']) # N: Revealed type is "__main__.B" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testCrashForwardSyntheticFunctionSyntax] -from typing import NamedTuple -from mypy_extensions import TypedDict +from typing import NamedTuple, TypedDict A1 = NamedTuple('A1', [('b', 'B'), ('x', int)]) A2 = TypedDict('A2', {'b': 'B', 'x': int}) class B: @@ -5417,6 +5768,7 @@ y: A2 reveal_type(x.b) # N: Revealed type is "__main__.B" reveal_type(y['b']) # N: Revealed type is "__main__.B" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -- Special support for six @@ -5535,8 +5887,8 @@ def f() -> type: return M class C1(six.with_metaclass(M), object): pass # E: Unsupported dynamic base class "six.with_metaclass" class C2(C1, six.with_metaclass(M)): pass # E: Unsupported dynamic base class "six.with_metaclass" class C3(six.with_metaclass(A)): pass # E: Metaclasses not inheriting from "type" are not supported -@six.add_metaclass(A) # E: Metaclasses not inheriting from "type" are not supported \ - # E: Argument 1 to "add_metaclass" has incompatible type "Type[A]"; expected "Type[type]" +@six.add_metaclass(A) # E: Metaclasses not inheriting from "type" are not supported \ + # E: Argument 1 to "add_metaclass" has incompatible type "type[A]"; expected "type[type]" class D3(A): pass class C4(six.with_metaclass(M), metaclass=M): pass # E: Multiple metaclass definitions @@ -5552,8 +5904,10 @@ class CD(six.with_metaclass(M)): pass # E: Multiple metaclass definitions class M1(type): pass class Q1(metaclass=M1): pass @six.add_metaclass(M) -class CQA(Q1): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases -class CQW(six.with_metaclass(M, Q1)): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +class CQA(Q1): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.M" (metaclass of "__main__.CQA") conflicts with "__main__.M1" (metaclass of "__main__.Q1") +class CQW(six.with_metaclass(M, Q1)): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.M" (metaclass of "__main__.CQW") conflicts with "__main__.M1" (metaclass of "__main__.Q1") [builtins fixtures/tuple.pyi] [case testSixMetaclassAny] @@ -5671,7 +6025,8 @@ class C5(future.utils.with_metaclass(f())): pass # E: Dynamic metaclass not sup class M1(type): pass class Q1(metaclass=M1): pass -class CQW(future.utils.with_metaclass(M, Q1)): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +class CQW(future.utils.with_metaclass(M, Q1)): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.M" (metaclass of "__main__.CQW") conflicts with "__main__.M1" (metaclass of "__main__.Q1") [builtins fixtures/tuple.pyi] [case testFutureMetaclassAny] @@ -5755,7 +6110,7 @@ T = TypeVar('T') class C(Any): def bar(self: T) -> Type[T]: pass def foo(self) -> None: - reveal_type(self.bar()) # N: Revealed type is "Type[__main__.C]" + reveal_type(self.bar()) # N: Revealed type is "type[__main__.C]" reveal_type(self.bar().__name__) # N: Revealed type is "builtins.str" [builtins fixtures/type.pyi] [out] @@ -5773,7 +6128,7 @@ def decorate_forward_ref() -> Callable[[Type[A]], Type[A]]: @decorate(11) class A: pass -@decorate # E: Argument 1 to "decorate" has incompatible type "Type[A2]"; expected "int" +@decorate # E: Argument 1 to "decorate" has incompatible type "type[A2]"; expected "int" class A2: pass [case testClassDecoratorIncorrect] @@ -5802,7 +6157,7 @@ class B(A): __slots__ = ('a', 'b') class C: __slots__ = ('x',) -class D(B, C): +class D(B, C): # E: Class "D" has incompatible disjoint bases __slots__ = ('aa', 'bb', 'cc') [builtins fixtures/tuple.pyi] @@ -5945,7 +6300,7 @@ d: D reveal_type(d.normal) # N: Revealed type is "builtins.int" reveal_type(d.dynamic) # N: Revealed type is "__main__.Descr" reveal_type(D.other) # N: Revealed type is "builtins.int" -D.dynamic # E: "Type[D]" has no attribute "dynamic" +D.dynamic # E: "type[D]" has no attribute "dynamic" [out] [case testSelfDescriptorAssign] @@ -5996,8 +6351,8 @@ A = G x: A[B[int]] # E B = G [out] -main:8:4: error: Type argument "G[int]" of "G" must be a subtype of "str" -main:8:6: error: Type argument "int" of "G" must be a subtype of "str" +main:8:6: error: Type argument "G[int]" of "G" must be a subtype of "str" +main:8:8: error: Type argument "int" of "G" must be a subtype of "str" [case testExtremeForwardReferencing] from typing import TypeVar, Generic @@ -6332,7 +6687,7 @@ class Sub(a.Base): # N: Superclass: \ # N: int \ # N: Subclass: \ - # N: def x(*Any, **Any) -> Tuple[int, int] + # N: def x(*Any, **Any) -> tuple[int, int] [file a.py] import b @@ -6358,7 +6713,7 @@ class Sub(a.Base): # N: Superclass: \ # N: int \ # N: Subclass: \ - # N: def x(*Any, **Any) -> Tuple[int, int] + # N: def x(*Any, **Any) -> tuple[int, int] [file a.py] import b @@ -6439,7 +6794,7 @@ class A(b.B): @c.deco def meth(self) -> int: y = super().meth() - reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int]" + reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.int]" return 0 [file b.py] from a import A @@ -6498,7 +6853,7 @@ class A(b.B): @c.deco def meth(self) -> int: y = super().meth() - reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int]" + reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.int]" reveal_type(other.x) # N: Revealed type is "builtins.int" return 0 @@ -6520,7 +6875,51 @@ from typing import TypeVar, Tuple, Callable T = TypeVar('T') def deco(f: Callable[..., T]) -> Callable[..., Tuple[T, int]]: ... [builtins fixtures/tuple.pyi] -[out] + +[case testOverrideWithUntypedNotChecked] +class Parent: + def foo(self, x): + ... + def bar(self, x): + ... + def baz(self, x: int) -> str: + return "" + +class Child(Parent): + def foo(self, y): # OK: names not checked + ... + def bar(self, x, y): + ... + def baz(self, x, y): + return "" +[builtins fixtures/tuple.pyi] + +[case testOverrideWithUntypedCheckedWithCheckUntypedDefs] +# flags: --check-untyped-defs +class Parent: + def foo(self, x): + ... + def bar(self, x): + ... + def baz(self, x: int) -> str: + return "" + +class Child(Parent): + def foo(self, y): # OK: names not checked + ... + def bar(self, x, y) -> None: # E: Signature of "bar" incompatible with supertype "Parent" \ + # N: Superclass: \ + # N: def bar(self, x: Any) -> Any \ + # N: Subclass: \ + # N: def bar(self, x: Any, y: Any) -> None + ... + def baz(self, x, y): # E: Signature of "baz" incompatible with supertype "Parent" \ + # N: Superclass: \ + # N: def baz(self, x: int) -> str \ + # N: Subclass: \ + # N: def baz(self, x: Any, y: Any) -> Any + return "" +[builtins fixtures/tuple.pyi] [case testOptionalDescriptorsBinder] from typing import Type, TypeVar, Optional @@ -6703,7 +7102,7 @@ class C: ... x: Union[C, Type[C]] if isinstance(x, type) and issubclass(x, C): - reveal_type(x) # N: Revealed type is "Type[__main__.C]" + reveal_type(x) # N: Revealed type is "type[__main__.C]" [builtins fixtures/isinstancelist.pyi] [case testIsInstanceTypeByAssert] @@ -6716,10 +7115,11 @@ reveal_type(i.x) # N: Revealed type is "builtins.int" [builtins fixtures/isinstancelist.pyi] [case testIsInstanceTypeTypeVar] -from typing import Type, TypeVar, Generic +from typing import Type, TypeVar, Generic, ClassVar class Base: ... -class Sub(Base): ... +class Sub(Base): + other: ClassVar[int] T = TypeVar('T', bound=Base) @@ -6727,13 +7127,9 @@ class C(Generic[T]): def meth(self, cls: Type[T]) -> None: if not issubclass(cls, Sub): return - reveal_type(cls) # N: Revealed type is "Type[__main__.Sub]" - def other(self, cls: Type[T]) -> None: - if not issubclass(cls, Sub): - return - reveal_type(cls) # N: Revealed type is "Type[__main__.Sub]" - -[builtins fixtures/isinstancelist.pyi] + reveal_type(cls) # N: Revealed type is "type[T`1]" + reveal_type(cls.other) # N: Revealed type is "builtins.int" +[builtins fixtures/isinstance.pyi] [case testIsInstanceTypeSubclass] from typing import Type, Optional @@ -6779,9 +7175,9 @@ class C(B): def __init__(self, a: int) -> None: self.c = a a = A(1) # E: Cannot instantiate abstract class "A" with abstract attribute "__init__" -A.c # E: "Type[A]" has no attribute "c" +A.c # E: "type[A]" has no attribute "c" b = B(2) # E: Cannot instantiate abstract class "B" with abstract attribute "__init__" -B.c # E: "Type[B]" has no attribute "c" +B.c # E: "type[B]" has no attribute "c" c = C(3) c.c C.c @@ -6874,11 +7270,10 @@ class C: [case testAttributeDefOrder2] class D(C): def g(self) -> None: - self.x = '' + self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") def f(self) -> None: - # https://github.com/python/mypy/issues/7162 - reveal_type(self.x) # N: Revealed type is "builtins.str" + reveal_type(self.x) # N: Revealed type is "builtins.int" class C: @@ -6892,7 +7287,7 @@ class E(C): def f(self) -> None: reveal_type(self.x) # N: Revealed type is "builtins.int" -[targets __main__, __main__, __main__.D.g, __main__.D.f, __main__.C.__init__, __main__.E.g, __main__.E.f] +[targets __main__, __main__, __main__.C.__init__, __main__.D.g, __main__.D.f, __main__.E.g, __main__.E.f] [case testNewReturnType1] class A: @@ -6985,7 +7380,7 @@ class A: N = NamedTuple('N', [('x', int)]) class B(A, N): pass -reveal_type(A()) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.B]" +reveal_type(A()) # N: Revealed type is "tuple[builtins.int, fallback=__main__.B]" [builtins fixtures/tuple.pyi] [case testNewReturnType8] @@ -7100,19 +7495,57 @@ class ChildOfCorrectSubclass1(CorrectSubclass1): ... class CorrectWithType1(C, A1): ... class CorrectWithType2(B, C): ... -class Conflict1(A1, B, E): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases -class Conflict2(A, B): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases -class Conflict3(B, A): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +class Conflict1(A1, B, E): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.MyMeta1" (metaclass of "__main__.A") conflicts with "__main__.MyMeta2" (metaclass of "__main__.B") +class Conflict2(A, B): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.MyMeta1" (metaclass of "__main__.A") conflicts with "__main__.MyMeta2" (metaclass of "__main__.B") +class Conflict3(B, A): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.MyMeta2" (metaclass of "__main__.B") conflicts with "__main__.MyMeta1" (metaclass of "__main__.A") -class ChildOfConflict1(Conflict3): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +class ChildOfConflict1(Conflict3): ... class ChildOfConflict2(Conflict3, metaclass=CorrectMeta): ... class ConflictingMeta(MyMeta1, MyMeta3): ... -class Conflict4(A1, B, E, metaclass=ConflictingMeta): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +class Conflict4(A1, B, E, metaclass=ConflictingMeta): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.ConflictingMeta" (metaclass of "__main__.Conflict4") conflicts with "__main__.MyMeta2" (metaclass of "__main__.B") -class ChildOfCorrectButWrongMeta(CorrectSubclass1, metaclass=ConflictingMeta): # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +class ChildOfCorrectButWrongMeta(CorrectSubclass1, metaclass=ConflictingMeta): # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.ConflictingMeta" (metaclass of "__main__.ChildOfCorrectButWrongMeta") conflicts with "__main__.CorrectMeta" (metaclass of "__main__.CorrectSubclass1") ... +[case testMetaClassConflictIssue14033] +class M1(type): pass +class M2(type): pass +class Mx(M1, M2): pass + +class A1(metaclass=M1): pass +class A2(A1): pass + +class B1(metaclass=M2): pass + +class C1(metaclass=Mx): pass + +class TestABC(A2, B1, C1): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.M1" (metaclass of "__main__.A1") conflicts with "__main__.M2" (metaclass of "__main__.B1") +class TestBAC(B1, A2, C1): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases \ + # N: "__main__.M2" (metaclass of "__main__.B1") conflicts with "__main__.M1" (metaclass of "__main__.A1") + +# should not warn again for children +class ChildOfTestABC(TestABC): pass + +# no metaclass is assumed if super class has a metaclass conflict +class ChildOfTestABCMetaMx(TestABC, metaclass=Mx): pass +class ChildOfTestABCMetaM1(TestABC, metaclass=M1): pass + +class TestABCMx(A2, B1, C1, metaclass=Mx): pass +class TestBACMx(B1, A2, C1, metaclass=Mx): pass + +class TestACB(A2, C1, B1): pass +class TestBCA(B1, C1, A2): pass + +class TestCAB(C1, A2, B1): pass +class TestCBA(C1, B1, A2): pass + [case testGenericOverride] from typing import Generic, TypeVar, Any @@ -7159,7 +7592,7 @@ class B(Generic[T]): class C(B[T]): def __init__(self) -> None: - self.x: List[T] # E: Incompatible types in assignment (expression has type "List[T]", base class "B" defined the type as "T") + self.x: List[T] # E: Incompatible types in assignment (expression has type "list[T]", base class "B" defined the type as "T") [builtins fixtures/list.pyi] [case testGenericOverrideGenericChained] @@ -7176,7 +7609,7 @@ class B(A[Tuple[T, S]]): ... class C(B[int, T]): def __init__(self) -> None: # TODO: error message could be better. - self.x: Tuple[str, T] # E: Incompatible types in assignment (expression has type "Tuple[str, T]", base class "A" defined the type as "Tuple[int, T]") + self.x: Tuple[str, T] # E: Incompatible types in assignment (expression has type "tuple[str, T]", base class "A" defined the type as "tuple[int, T]") [builtins fixtures/tuple.pyi] [case testInitSubclassWrongType] @@ -7315,7 +7748,7 @@ class C: def meth(cls): ... reveal_type(C.meth) # N: Revealed type is "def () -> Any" -reveal_type(C.__new__) # N: Revealed type is "def (cls: Type[__main__.C]) -> Any" +reveal_type(C.__new__) # N: Revealed type is "def (cls: type[__main__.C]) -> Any" [builtins fixtures/classmethod.pyi] [case testOverrideGenericSelfClassMethod] @@ -7358,7 +7791,7 @@ class Foo: @classmethod def bar(cls): - cls.baz() # E: "Type[Foo]" has no attribute "baz" + cls.baz() # E: "type[Foo]" has no attribute "baz" class C(Generic[T]): x: T @@ -7385,28 +7818,253 @@ class Foo: def bad(): # E: Method must have at least one argument. Did you forget the "self" argument? self.x = 0 # E: Name "self" is not defined -[case testTypeAfterAttributeAccessWithDisallowAnyExpr] -# flags: --disallow-any-expr +[case testMethodSelfArgumentChecks] +from typing import Callable, ParamSpec, TypeVar -def access_before_declaration(self) -> None: - obj = Foo('bar') - obj.value - x = 1 +T = TypeVar("T") +P = ParamSpec("P") - reveal_type(x) # N: Revealed type is "builtins.int" - x = x + 1 +def to_number_1(fn: Callable[[], int]) -> int: + return 0 -class Foo: - def __init__(self, value: str) -> None: - self.value = value +def to_number_2(fn: Callable[[int], int]) -> int: + return 0 -def access_after_declaration(self) -> None: - obj = Foo('bar') - obj.value - x = 1 +def to_same_callable(fn: Callable[P, T]) -> Callable[P, T]: + return fn - reveal_type(x) # N: Revealed type is "builtins.int" - x = x + 1 +class A: + def undecorated() -> None: ... # E: Method must have at least one argument. Did you forget the "self" argument? + + def undecorated_not_self(x: int) -> None: ... # E: Self argument missing for a non-static method (or an invalid type for self) + + def undecorated_not_self_2(self: int) -> None: ... # E: The erased type of self "builtins.int" is not a supertype of its class "__main__.A" + + @to_number_1 + def fn1() -> int: + return 0 + + @to_number_1 # E: Argument 1 to "to_number_1" has incompatible type "Callable[[int], int]"; expected "Callable[[], int]" + def fn2(_x: int) -> int: + return 0 + + @to_number_2 # E: Argument 1 to "to_number_2" has incompatible type "Callable[[], int]"; expected "Callable[[int], int]" + def fn3() -> int: + return 0 + + @to_number_2 + def fn4(_x: int) -> int: + return 0 + + @to_number_2 # E: Argument 1 to "to_number_2" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" + def fn5(_x: str) -> int: + return 0 + + @to_same_callable + def g1() -> None: ... # E: Method must have at least one argument. Did you forget the "self" argument? + + @to_same_callable + def g2(x: int) -> None: ... # E: Self argument missing for a non-static method (or an invalid type for self) + + @to_same_callable + def g3(self: int) -> None: ... # E: The erased type of self "builtins.int" is not a supertype of its class "__main__.A" + +reveal_type(A().fn1) # N: Revealed type is "builtins.int" +reveal_type(A().fn2) # N: Revealed type is "builtins.int" +reveal_type(A().fn3) # N: Revealed type is "builtins.int" +reveal_type(A().fn4) # N: Revealed type is "builtins.int" +reveal_type(A().fn5) # N: Revealed type is "builtins.int" + +reveal_type(A().g1) # E: Attribute function "g1" with type "Callable[[], None]" does not accept self argument \ + # N: Revealed type is "def ()" +reveal_type(A().g2) # E: Invalid self argument "A" to attribute function "g2" with type "Callable[[int], None]" \ + # N: Revealed type is "def ()" +reveal_type(A().g3) # E: Invalid self argument "A" to attribute function "g3" with type "Callable[[int], None]" \ + # N: Revealed type is "def ()" +[builtins fixtures/tuple.pyi] + +[case testMethodSelfArgumentChecksConcatenate] +from typing import Callable, ParamSpec, TypeVar +from typing_extensions import Concatenate + +T = TypeVar("T") +P = ParamSpec("P") +R = TypeVar("R") + +def to_same_callable(fn: Callable[Concatenate[T, P], R]) -> Callable[Concatenate[T, P], R]: + return fn + +def remove_first(fn: Callable[Concatenate[T, P], R]) -> Callable[P, R]: + ... + +def add_correct_first(fn: Callable[P, R]) -> Callable[Concatenate["C", P], R]: + ... + +def add_wrong_first(fn: Callable[P, R]) -> Callable[Concatenate[int, P], R]: + ... + +class A: + @to_same_callable # E: Argument 1 to "to_same_callable" has incompatible type "Callable[[], int]"; expected "Callable[[T], int]" + def fn1() -> int: + return 0 + + @to_same_callable + def fn2(_x: int) -> int: # E: Self argument missing for a non-static method (or an invalid type for self) + return 0 + + @to_same_callable + def fn3(self, _x: int) -> int: + return 0 + +reveal_type(A().fn1) # N: Revealed type is "def () -> builtins.int" +reveal_type(A().fn2) # E: Invalid self argument "A" to attribute function "fn2" with type "Callable[[int], int]" \ + # N: Revealed type is "def () -> builtins.int" +reveal_type(A().fn3) # N: Revealed type is "def (_x: builtins.int) -> builtins.int" + +class B: + @remove_first # E: Argument 1 to "remove_first" has incompatible type "Callable[[], int]"; expected "Callable[[T], int]" + def fn1() -> int: # E: Method must have at least one argument. Did you forget the "self" argument? + return 0 + + @remove_first + def fn2(_x: int) -> int: # E: Method must have at least one argument. Did you forget the "self" argument? + return 0 + + @remove_first + def fn3(self, _x: int) -> int: # E: Self argument missing for a non-static method (or an invalid type for self) + return 0 + + @remove_first + def fn4(self, new_self: 'B') -> int: + return 0 + +reveal_type(B().fn1) # E: Attribute function "fn1" with type "Callable[[], int]" does not accept self argument \ + # N: Revealed type is "def () -> builtins.int" +reveal_type(B().fn2) # E: Attribute function "fn2" with type "Callable[[], int]" does not accept self argument \ + # N: Revealed type is "def () -> builtins.int" +reveal_type(B().fn3) # E: Invalid self argument "B" to attribute function "fn3" with type "Callable[[int], int]" \ + # N: Revealed type is "def () -> builtins.int" +reveal_type(B().fn4) # N: Revealed type is "def () -> builtins.int" + +class C: + @add_correct_first + def fn1() -> int: + return 0 + + @add_correct_first + def fn2(_x: int) -> int: + return 0 + + @add_correct_first + def fn3(self, _x: int) -> int: + return 0 + +reveal_type(C().fn1) # N: Revealed type is "def () -> builtins.int" +reveal_type(C().fn2) # N: Revealed type is "def (_x: builtins.int) -> builtins.int" +reveal_type(C().fn3) # N: Revealed type is "def (self: __main__.C, _x: builtins.int) -> builtins.int" + +class D: + @add_wrong_first + def fn1() -> int: # E: Self argument missing for a non-static method (or an invalid type for self) + return 0 + + @add_wrong_first + def fn2(_x: int) -> int: # E: Self argument missing for a non-static method (or an invalid type for self) + return 0 + + @add_wrong_first + def fn3(self, _x: int) -> int: # E: Self argument missing for a non-static method (or an invalid type for self) + return 0 + +reveal_type(D().fn1) # E: Invalid self argument "D" to attribute function "fn1" with type "Callable[[int], int]" \ + # N: Revealed type is "def () -> builtins.int" +reveal_type(D().fn2) # E: Invalid self argument "D" to attribute function "fn2" with type "Callable[[int, int], int]" \ + # N: Revealed type is "def (_x: builtins.int) -> builtins.int" +reveal_type(D().fn3) # E: Invalid self argument "D" to attribute function "fn3" with type "Callable[[int, D, int], int]" \ + # N: Revealed type is "def (self: __main__.D, _x: builtins.int) -> builtins.int" +[builtins fixtures/tuple.pyi] + +[case testMethodSelfArgumentChecksInUntyped] +from typing import Callable, ParamSpec, TypeVar + +T = TypeVar("T") +P = ParamSpec("P") + +def to_same_callable(fn: Callable[P, T]) -> Callable[P, T]: + return fn + +def unchecked(): + class Bad: + def fn() -> None: ... # E: Method must have at least one argument. Did you forget the "self" argument? + def fn2(x: int) -> None: ... # E: Self argument missing for a non-static method (or an invalid type for self) + + # TODO: would be nice to make this error, but now we see the func + # being decorated as Any, not as a callable + @to_same_callable + def gaaa() -> None: ... + @to_same_callable + def gaaa2(x: int) -> None: ... + + class Ok: + def fn(): ... + def fn2(x): ... + + @to_same_callable + def g(): ... + @to_same_callable + def g2(x): ... + +def checked() -> None: + class Bad: + def fn() -> None: ... # E: Method must have at least one argument. Did you forget the "self" argument? + def fn2(x: int) -> None: ... # E: Self argument missing for a non-static method (or an invalid type for self) + + @to_same_callable + def g() -> None: ... # E: Method must have at least one argument. Did you forget the "self" argument? + @to_same_callable + def g2(x: int) -> None: ... # E: Self argument missing for a non-static method (or an invalid type for self) + + class AlsoBad: + def fn(): ... # E: Method must have at least one argument. Did you forget the "self" argument? + def fn2(x): ... + + @to_same_callable + def g(): ... # E: Method must have at least one argument. Did you forget the "self" argument? + @to_same_callable + def g2(x): ... + +class Ok: + def fn(): ... # E: Method must have at least one argument. Did you forget the "self" argument? + def fn2(x): ... + + @to_same_callable + def g(): ... # E: Method must have at least one argument. Did you forget the "self" argument? + @to_same_callable + def g2(x): ... +[builtins fixtures/tuple.pyi] + +[case testTypeAfterAttributeAccessWithDisallowAnyExpr] +# flags: --disallow-any-expr + +def access_before_declaration(self) -> None: + obj = Foo('bar') + obj.value + x = 1 + + reveal_type(x) # N: Revealed type is "builtins.int" + x = x + 1 + +class Foo: + def __init__(self, value: str) -> None: + self.value = value + +def access_after_declaration(self) -> None: + obj = Foo('bar') + obj.value + x = 1 + + reveal_type(x) # N: Revealed type is "builtins.int" + x = x + 1 [case testIsSubClassNarrowDownTypesOfTypeVariables] from typing import Type, TypeVar, Generic @@ -7421,14 +8079,14 @@ TypeT1 = TypeVar("TypeT1", bound=Type[Base]) class C1: def method(self, other: type) -> int: if issubclass(other, Base): - reveal_type(other) # N: Revealed type is "Type[__main__.Base]" + reveal_type(other) # N: Revealed type is "type[__main__.Base]" return other.field return 0 class C2(Generic[TypeT]): def method(self, other: TypeT) -> int: if issubclass(other, Base): - reveal_type(other) # N: Revealed type is "Type[__main__.Base]" + reveal_type(other) # N: Revealed type is "TypeT`1" return other.field return 0 @@ -7456,7 +8114,7 @@ class A: def y(self) -> int: ... @y.setter def y(self, value: int) -> None: ... - @dec # E: Only supported top decorator is @y.setter + @dec # E: Only supported top decorators are "@y.setter" and "@y.deleter" def y(self) -> None: ... reveal_type(A().y) # N: Revealed type is "builtins.int" @@ -7663,7 +8321,7 @@ class Foo: reveal_type(Foo.foo) # N: Revealed type is "builtins.int" reveal_type(Foo.bar) # N: Revealed type is "Any" -reveal_type(Foo.baz) # E: "Type[Foo]" has no attribute "baz" \ +reveal_type(Foo.baz) # E: "type[Foo]" has no attribute "baz" \ # N: Revealed type is "Any" [file mod.py] @@ -7731,8 +8389,7 @@ class Foo: from mod import meth2 # E: Unsupported class scoped import from mod import T -reveal_type(Foo.T) # E: Type variable "Foo.T" cannot be used as an expression \ - # N: Revealed type is "Any" +reveal_type(Foo.T) # N: Revealed type is "typing.TypeVar" [file mod.pyi] from typing import Any, TypeVar, overload @@ -7744,6 +8401,8 @@ def meth1(self: Any, y: str) -> str: ... T = TypeVar("T") def meth2(self: Any, y: T) -> T: ... +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testNewAndInitNoReturn] from typing import NoReturn @@ -7852,25 +8511,25 @@ class Parent: class Child(Parent): def foo(self, val: int) -> int: # E: Signature of "foo" incompatible with supertype "Parent" \ # N: Superclass: \ - # N: None \ + # N: \ # N: Subclass: \ # N: def foo(self, val: int) -> int return val def bar(self, val: str) -> str: # E: Signature of "bar" incompatible with supertype "Parent" \ # N: Superclass: \ - # N: None \ + # N: def __init__(self) -> bar \ # N: Subclass: \ # N: def bar(self, val: str) -> str return val def baz(self, val: float) -> float: # E: Signature of "baz" incompatible with supertype "Parent" \ # N: Superclass: \ - # N: None \ + # N: Module \ # N: Subclass: \ # N: def baz(self, val: float) -> float return val def foobar(self) -> bool: # E: Signature of "foobar" incompatible with supertype "Parent" \ # N: Superclass: \ - # N: None \ + # N: TypeVar \ # N: Subclass: \ # N: def foobar(self) -> bool return False @@ -7883,6 +8542,8 @@ a: int = child.foo(1) b: str = child.bar("abc") c: float = child.baz(3.4) d: bool = child.foobar() +[builtins fixtures/module.pyi] +[typing fixtures/typing-full.pyi] [case testGenericTupleTypeCreation] from typing import Generic, Tuple, TypeVar @@ -7894,9 +8555,9 @@ class C(Tuple[T, S]): def foo(self, arg: T) -> S: ... cis: C[int, str] -reveal_type(cis) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C[builtins.int, builtins.str]]" +reveal_type(cis) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C[builtins.int, builtins.str]]" cii = C(0, 1) -reveal_type(cii) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.C[builtins.int, builtins.int]]" +reveal_type(cii) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.C[builtins.int, builtins.int]]" reveal_type(cis.foo) # N: Revealed type is "def (arg: builtins.int) -> builtins.str" [builtins fixtures/tuple.pyi] @@ -7908,7 +8569,7 @@ class C(Tuple[T, T]): ... class D(C[List[T]]): ... di: D[int] -reveal_type(di) # N: Revealed type is "Tuple[builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.D[builtins.int]]" +reveal_type(di) # N: Revealed type is "tuple[builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.D[builtins.int]]" [builtins fixtures/tuple.pyi] [case testOverrideAttrWithSettableProperty] @@ -7934,6 +8595,217 @@ class Bar(Foo): def x(self, value: int) -> None: ... [builtins fixtures/property.pyi] +[case testOverridePropertyDifferentSetterBoth] +class B: ... +class C(B): ... + +class B1: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C1(B1): + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: B) -> None: ... + +class B2: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C2(B2): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B2" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly + def foo(self, x: C) -> None: ... + +class B3: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C3(B3): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... + +class B4: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C4(B4): + @property + def foo(self) -> C: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B4" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly + def foo(self, x: C) -> None: ... + +class B5: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C5(B5): + @property # E: Signature of "foo" incompatible with supertype "B5" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: C + def foo(self) -> C: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B5" defined the type as "B", \ + # N: override has type "str") + def foo(self, x: str) -> None: ... + +class B6: + @property + def foo(self) -> B: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C6(B6): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +[builtins fixtures/property.pyi] + +[case testOverridePropertyDifferentSetterVarSuper] +class B: ... +class C(B): ... + +class B1: + foo: B +class C1(B1): + @property + def foo(self) -> B: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B1" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly + def foo(self, x: C) -> None: ... + +class B2: + foo: C +class C2(B2): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... + +class B3: + foo: B +class C3(B3): + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +[builtins fixtures/property.pyi] + +[case testOverridePropertyDifferentSetterVarSub] +class B: ... +class C(B): ... + +class B1: + @property + def foo(self) -> B: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C1(B1): + foo: C + +class B2: + @property + def foo(self) -> B: ... + @foo.setter + def foo(self, x: C) -> None: ... +class C2(B2): + foo: B + +class B3: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, x: B) -> None: ... +class C3(B3): + foo: C # E: Incompatible override of a setter type \ + # N: (base class "B3" defined the type as "B", \ + # N: override has type "C") \ + # N: Setter types should behave contravariantly +[builtins fixtures/property.pyi] + +[case testOverridePropertyInvalidSetter] +class B1: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: str) -> None: ... +class C1(B1): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self) -> None: ... # E: Invalid property setter signature + +class B2: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self) -> None: ... # E: Invalid property setter signature +class C2(B2): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: str) -> None: ... + +class B3: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self) -> None: ... # E: Invalid property setter signature +class C3(B3): + foo: int +[builtins fixtures/property.pyi] + +[case testOverridePropertyGeneric] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class B1(Generic[T]): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: T) -> None: ... +class C1(B1[str]): + @property + def foo(self) -> int: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B1" defined the type as "str", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... + +class B2: + @property + def foo(self) -> int: ... + @foo.setter + def foo(self: T, x: T) -> None: ... +class C2(B2): + @property + def foo(self) -> int: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B2" defined the type as "C2", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... +[builtins fixtures/property.pyi] + [case testOverrideMethodProperty] class B: def foo(self) -> int: @@ -8054,3 +8926,439 @@ class C: def f(self) -> None: __module__ # E: Name "__module__" is not defined __qualname__ # E: Name "__qualname__" is not defined + +[case testPropertySetterType] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: str) -> None: + pass +a = A() +a.f = '' # OK +reveal_type(a.f) # N: Revealed type is "builtins.int" +a.f = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +reveal_type(a.f) # N: Revealed type is "builtins.int" +[builtins fixtures/property.pyi] + +[case testPropertySetterTypeGeneric] +from typing import TypeVar, Generic, List + +T = TypeVar("T") + +class B(Generic[T]): + @property + def foo(self) -> int: ... + @foo.setter + def foo(self, x: T) -> None: ... + +class C(B[List[T]]): ... + +a = C[str]() +a.foo = ["foo", "bar"] +reveal_type(a.foo) # N: Revealed type is "builtins.int" +a.foo = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "list[str]") +reveal_type(a.foo) # N: Revealed type is "builtins.int" +[builtins fixtures/property.pyi] + +[case testPropertyDeleterNoSetterOK] +class C: + @property + def x(self) -> int: + return 0 + @x.deleter + def x(self) -> None: + pass +[builtins fixtures/property.pyi] + +[case testPropertySetterSuperclassDeferred] +from typing import Callable, TypeVar + +class B: + def __init__(self) -> None: + self.foo = f() + +class C(B): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B" defined the type as "str", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... + +T = TypeVar("T") +def deco(fn: Callable[[], list[T]]) -> Callable[[], T]: ... + +@deco +def f() -> list[str]: ... +[builtins fixtures/property.pyi] + +[case testPropertySetterSuperclassDeferred2] +import a +[file a.py] +import b +class D(b.C): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "C" defined the type as "str", \ + # N: override has type "int") + def foo(self, x: int) -> None: ... +[file b.py] +from a import D +class C: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... +[builtins fixtures/property.pyi] + +[case testPropertySetterDecorated] +from typing import Callable, TypeVar, Generic + +class B: + def __init__(self) -> None: + self.foo: str + self.bar: int + +class C(B): + @property + def foo(self) -> str: ... + @foo.setter # E: Incompatible override of a setter type \ + # N: (base class "B" defined the type as "str", \ + # N: override has type "int") + @deco + def foo(self, x: int, y: int) -> None: ... + + @property + def bar(self) -> int: ... + @bar.setter + @deco + def bar(self, x: int, y: int) -> None: ... + + @property + def baz(self) -> int: ... + @baz.setter + @deco_untyped + def baz(self, x: int) -> None: ... + + @property + def tricky(self) -> int: ... + @tricky.setter + @deco_instance + def tricky(self, x: int) -> None: ... + +c: C +c.baz = "yes" # OK, because of untyped decorator +c.tricky = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "list[int]") + +T = TypeVar("T") +def deco(fn: Callable[[T, int, int], None]) -> Callable[[T, int], None]: ... +def deco_untyped(fn): ... + +class Wrapper(Generic[T]): + def __call__(self, s: T, x: list[int]) -> None: ... +def deco_instance(fn: Callable[[T, int], None]) -> Wrapper[T]: ... +[builtins fixtures/property.pyi] + +[case testPropertyDeleterBodyChecked] +class C: + @property + def foo(self) -> int: ... + @foo.deleter + def foo(self) -> None: + 1() # E: "int" not callable + + @property + def bar(self) -> int: ... + @bar.setter + def bar(self, x: str) -> None: ... + @bar.deleter + def bar(self) -> None: + 1() # E: "int" not callable +[builtins fixtures/property.pyi] + +[case testSettablePropertyGetterDecorated] +from typing import Callable, TypeVar, Generic + +class C: + @property + @deco + def foo(self, ok: int) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... + + @property + @deco_instance + def bar(self, ok: int) -> int: ... + @bar.setter + def bar(self, x: int) -> None: ... + + @property + @deco_untyped + def baz(self) -> int: ... + @baz.setter + def baz(self, x: int) -> None: ... + +c: C +reveal_type(c.foo) # N: Revealed type is "builtins.list[builtins.str]" +reveal_type(c.bar) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(c.baz) # N: Revealed type is "Any" + +T = TypeVar("T") +R = TypeVar("R") +def deco(fn: Callable[[T, int], R]) -> Callable[[T], list[R]]: ... +def deco_untyped(fn): ... + +class Wrapper(Generic[T, R]): + def __call__(self, s: T) -> list[R]: ... +def deco_instance(fn: Callable[[T, int], R]) -> Wrapper[T, R]: ... +[builtins fixtures/property.pyi] + +[case testOverridePropertyWithDescriptor] +from typing import Any + +class StrProperty: + def __get__(self, instance: Any, owner: Any) -> str: ... + +class Base: + @property + def id(self) -> str: ... + +class BadBase: + @property + def id(self) -> int: ... + +class Derived(Base): + id = StrProperty() + +class BadDerived(BadBase): + id = StrProperty() # E: Incompatible types in assignment (expression has type "str", base class "BadBase" defined the type as "int") +[builtins fixtures/property.pyi] + +[case testLambdaInOverrideInference] +class B: + def f(self, x: int) -> int: ... +class C(B): + f = lambda s, x: x + +reveal_type(C().f) # N: Revealed type is "def (x: builtins.int) -> builtins.int" + +[case testGenericDecoratorInOverrideInference] +from typing import Any, Callable, TypeVar +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +T = TypeVar("T") +def wrap(f: Callable[Concatenate[Any, P], T]) -> Callable[Concatenate[Any, P], T]: ... + +class Base: + def g(self, a: int) -> int: + return a + 1 + +class Derived(Base): + def _g(self, a: int) -> int: + return a + 2 + g = wrap(_g) + +reveal_type(Derived().g) # N: Revealed type is "def (a: builtins.int) -> builtins.int" +[builtins fixtures/paramspec.pyi] + +[case testClassVarOverrideWithSubclass] +class A: ... +class B(A): ... +class AA: + cls = A +class BB(AA): + cls = B + +[case testSelfReferenceWithinMethodFunction] +class B: + x: str +class C(B): + def meth(self) -> None: + def cb() -> None: + self.x: int = 1 # E: Incompatible types in assignment (expression has type "int", base class "B" defined the type as "str") + +[case testOverloadedDescriptorSelected] +from typing import Generic, TypeVar, Any, overload + +T_co = TypeVar("T_co", covariant=True) +class Field(Generic[T_co]): + @overload + def __get__(self: Field[bool], instance: None, owner: Any) -> BoolField: ... + @overload + def __get__(self: Field[int], instance: None, owner: Any) -> NumField: ... + @overload + def __get__(self: Field[Any], instance: None, owner: Any) -> AnyField[T_co]: ... + @overload + def __get__(self, instance: Any, owner: Any) -> T_co: ... + + def __get__(self, instance: Any, owner: Any) -> Any: + pass + +class BoolField(Field[bool]): ... +class NumField(Field[int]): ... +class AnyField(Field[T_co]): ... +class Custom: ... + +class Fields: + bool_f: Field[bool] + int_f: Field[int] + custom_f: Field[Custom] + +reveal_type(Fields.bool_f) # N: Revealed type is "__main__.BoolField" +reveal_type(Fields.int_f) # N: Revealed type is "__main__.NumField" +reveal_type(Fields.custom_f) # N: Revealed type is "__main__.AnyField[__main__.Custom]" + +[case testRecursivePropertyWithInvalidSetterNoCrash] +class NoopPowerResource: + _hardware_type: int + + @property + def hardware_type(self) -> int: + return self._hardware_type + + @hardware_type.setter + def hardware_type(self) -> None: # E: Invalid property setter signature + self.hardware_type = None # Note: intentionally recursive +[builtins fixtures/property.pyi] + +[case testOverrideErrorReportingNoDuplicates] +from typing import Callable, TypeVar + +def nested() -> None: + class B: + def meth(self, x: str) -> int: ... + class C(B): + def meth(self) -> str: # E: Signature of "meth" incompatible with supertype "B" \ + # N: Superclass: \ + # N: def meth(self, x: str) -> int \ + # N: Subclass: \ + # N: def meth(self) -> str + pass + x = defer() + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ... + +@deco +def defer() -> int: ... +[builtins fixtures/list.pyi] + +[case testPropertyAllowsDeleterBeforeSetter] +class C: + @property + def foo(self) -> str: ... + @foo.deleter + def foo(self) -> None: ... + @foo.setter + def foo(self, val: int) -> None: ... + + @property + def bar(self) -> int: ... + @bar.deleter + def bar(self) -> None: ... + @bar.setter + def bar(self, value: int, val: int) -> None: ... # E: Invalid property setter signature + +C().foo = "no" # E: Incompatible types in assignment (expression has type "str", variable has type "int") +C().bar = "fine" +[builtins fixtures/property.pyi] + +[case testCorrectConstructorTypeWithAnyFallback] +from typing import Generic, TypeVar + +class B(Unknown): # type: ignore + def __init__(self) -> None: ... +class C(B): ... + +reveal_type(C) # N: Revealed type is "def () -> __main__.C" + +T = TypeVar("T") +class BG(Generic[T], Unknown): # type: ignore + def __init__(self) -> None: ... +class CGI(BG[int]): ... +class CGT(BG[T]): ... + +reveal_type(CGI) # N: Revealed type is "def () -> __main__.CGI" +reveal_type(CGT) # N: Revealed type is "def [T] () -> __main__.CGT[T`1]" + +[case testSettablePropertyAlias] +from typing import Any, TypeVar + +class A: + @property + def prop(self: Any) -> str: ... + @prop.setter + def prop(self, val: str) -> None: ... + +T = TypeVar("T") +class AT: + @property + def prop(self: T) -> T: ... + @prop.setter + def prop(self: T, val: list[T]) -> None: ... + +class B: + prop: str + prop_t: str + +class C(B): + prop = A.prop + prop_t = AT.prop # E: Incompatible types in assignment (expression has type "C", base class "B" defined the type as "str") + +reveal_type(C().prop) # N: Revealed type is "builtins.str" +C().prop = "no" # E: Invalid self argument "C" to attribute function "prop" with type "Callable[[A, str], None]" +reveal_type(C().prop_t) # N: Revealed type is "__main__.C" +C().prop_t = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "list[C]") +[builtins fixtures/property.pyi] + +[case testClassEqDecoratedAbstractNote] +from abc import abstractmethod + +class C: + @abstractmethod + def __eq__(self, other: C) -> bool: ... +[builtins fixtures/plugin_attrs.pyi] +[out] +main:5: error: Argument 1 of "__eq__" is incompatible with supertype "builtins.object"; supertype defines the argument type as "object" +main:5: note: This violates the Liskov substitution principle +main:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +main:5: note: It is recommended for "__eq__" to work with arbitrary objects, for example: +main:5: note: def __eq__(self, other: object) -> bool: +main:5: note: if not isinstance(other, C): +main:5: note: return NotImplemented +main:5: note: return + +[case testLambdaInAttributeCallValue] +# https://github.com/python/mypy/issues/19632 +import foo + +def nop(fn: object) -> foo.Bar: + return foo.Bar() + +class Bar: + foo: foo.Bar = nop( + lambda: 0 + ) +[file foo.py] +class Bar: + ... + +[case testConstructorWithoutStrictOptionalNoCache] +import mod +a = mod.NT(x=None) # OK + +[file typ.py] +from typing import NamedTuple, Optional +NT = NamedTuple("NT", [("x", Optional[str])]) + +[file mod.py] +# mypy: no-strict-optional +from typ import NT + +def f() -> NT: + return NT(x='') +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test index 1e87e441dea28..7918ccded2fe0 100644 --- a/test-data/unit/check-classvar.test +++ b/test-data/unit/check-classvar.test @@ -200,7 +200,7 @@ f().x = 0 [out] main:6: error: Cannot assign to class variable "x" via instance -[case testOverrideWithIncomatibleType] +[case testOverrideWithIncompatibleType] from typing import ClassVar class A: x = None # type: ClassVar[int] @@ -285,7 +285,7 @@ main:3: error: Cannot assign to class variable "x" via instance from typing import ClassVar, Generic, TypeVar T = TypeVar('T') class A(Generic[T]): - x: ClassVar[T] # E: ClassVar cannot contain type variables + x: ClassVar[T] # Error reported at access site @classmethod def foo(cls) -> T: return cls.x # OK @@ -308,7 +308,7 @@ from typing import ClassVar, Generic, Tuple, TypeVar, Union, Type T = TypeVar('T') U = TypeVar('U') class A(Generic[T, U]): - x: ClassVar[Union[T, Tuple[U, Type[U]]]] # E: ClassVar cannot contain type variables + x: ClassVar[Union[T, Tuple[U, Type[U]]]] # Error reported at access site @classmethod def foo(cls) -> Union[T, Tuple[U, Type[U]]]: return cls.x # OK @@ -319,7 +319,9 @@ A[int, str].x # E: Access to generic class variables is ambiguous class Bad(A[int, str]): pass -Bad.x # E: Access to generic class variables is ambiguous +reveal_type(Bad.x) # E: Access to generic class variables is ambiguous \ + # N: Revealed type is "Union[builtins.int, tuple[builtins.str, type[builtins.str]]]" +reveal_type(Bad().x) # N: Revealed type is "Union[builtins.int, tuple[builtins.str, type[builtins.str]]]" class Good(A[int, str]): x = 42 @@ -334,3 +336,47 @@ class C: c:C c.foo() # E: Too few arguments \ # N: "foo" is considered instance variable, to make it class variable use ClassVar[...] + +[case testClassVarUnionBoundOnInstance] +from typing import Union, Callable, ClassVar + +class C: + def f(self) -> int: ... + g: ClassVar[Union[Callable[[C], int], int]] = f + +reveal_type(C().g) # N: Revealed type is "Union[def () -> builtins.int, builtins.int]" + +[case testGenericSubclassAccessNoLeak] +from typing import ClassVar, Generic, TypeVar + +T = TypeVar("T") +class B(Generic[T]): + x: T + y: ClassVar[T] + +class C(B[T]): ... + +reveal_type(C.x) # E: Access to generic instance variables via class is ambiguous \ + # N: Revealed type is "Any" +reveal_type(C.y) # E: Access to generic class variables is ambiguous \ + # N: Revealed type is "Any" + +[case testClassVarBareAnnotation] +from typing import ClassVar + +class C: + x: ClassVar = 1 + y: ClassVar + +reveal_type(C.x) # N: Revealed type is "builtins.int" +reveal_type(C().x) # N: Revealed type is "builtins.int" +reveal_type(C.y) # N: Revealed type is "Any" +reveal_type(C().y) # N: Revealed type is "Any" + +[case testClassVarBareAnnotationDisabled] +# flags: --disallow-any-generics +from typing import ClassVar + +class C: + x: ClassVar = 1 + y: ClassVar # E: ClassVar without type argument becomes Any diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 44524b9df9435..c822c7c44f419 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -4,7 +4,7 @@ f() 1 + [out] -main:2:5: error: invalid syntax +main:2:5: error: Invalid syntax [case testColumnsNestedFunctions] import typing @@ -47,13 +47,13 @@ aaa: str h(x=1, y=aaa, z=2) # E:10: Argument "y" to "h" has incompatible type "str"; expected "int" a: A ff(a.x) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int" -ff([1]) # E:4: Argument 1 to "ff" has incompatible type "List[int]"; expected "int" +ff([1]) # E:4: Argument 1 to "ff" has incompatible type "list[int]"; expected "int" # TODO: Different column in Python 3.8+ -#ff([1 for x in [1]]) # Argument 1 to "ff" has incompatible type "List[int]"; expected "int" -ff({1: 2}) # E:4: Argument 1 to "ff" has incompatible type "Dict[int, int]"; expected "int" +#ff([1 for x in [1]]) # Argument 1 to "ff" has incompatible type "list[int]"; expected "int" +ff({1: 2}) # E:4: Argument 1 to "ff" has incompatible type "dict[int, int]"; expected "int" ff(1.1) # E:4: Argument 1 to "ff" has incompatible type "float"; expected "int" # TODO: Different column in Python 3.8+ -#ff( ( 1, 1)) # Argument 1 to "ff" has incompatible type "Tuple[int, int]"; expected "int" +#ff( ( 1, 1)) # Argument 1 to "ff" has incompatible type "tuple[int, int]"; expected "int" ff(-a) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int" ff(a + 1) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int" ff(a < 1) # E:4: Argument 1 to "ff" has incompatible type "str"; expected "int" @@ -69,9 +69,9 @@ def f(*x: int) -> None: pass def g(**x: int) -> None: pass a = [''] -f(*a) # E:4: Argument 1 to "f" has incompatible type "*List[str]"; expected "int" +f(*a) # E:4: Argument 1 to "f" has incompatible type "*list[str]"; expected "int" b = {'x': 'y'} -g(**b) # E:5: Argument 1 to "g" has incompatible type "**Dict[str, str]"; expected "int" +g(**b) # E:5: Argument 1 to "g" has incompatible type "**dict[str, str]"; expected "int" [builtins fixtures/dict.pyi] [case testColumnsMultipleStatementsPerLine] @@ -183,7 +183,7 @@ if int(): [case testColumnNeedTypeAnnotation] if 1: - x = [] # E:5: Need type annotation for "x" (hint: "x: List[] = ...") + x = [] # E:5: Need type annotation for "x" (hint: "x: list[] = ...") [builtins fixtures/list.pyi] [case testColumnCallToUntypedFunction] @@ -210,12 +210,13 @@ y: Dict[int, int] = { [builtins fixtures/dict.pyi] [case testColumnCannotDetermineType] +# flags: --no-local-partial-types (x) # E:2: Cannot determine type of "x" # E:2: Name "x" is used before definition x = None [case testColumnInvalidIndexing] from typing import List -([1]['']) # E:6: Invalid index type "str" for "List[int]"; expected type "int" +([1]['']) # E:6: Invalid index type "str" for "list[int]"; expected type "int" (1[1]) # E:2: Value of type "int" is not indexable def f() -> None: 1[1] = 1 # E:5: Unsupported target for indexed assignment ("int") @@ -227,9 +228,19 @@ class D(TypedDict): x: int t: D = {'x': 'y'} # E:5: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +s: str if int(): - del t['y'] # E:5: TypedDict "D" has no key "y" + del t[s] # E:11: Expected TypedDict key to be string literal + del t["x"] # E:11: Key "x" of TypedDict "D" cannot be deleted + del t["y"] # E:11: TypedDict "D" has no key "y" + +t.pop(s) # E:7: Expected TypedDict key to be string literal +t.pop("y") # E:7: TypedDict "D" has no key "y" + +t.setdefault(s, 123) # E:14: Expected TypedDict key to be string literal +t.setdefault("x", "a") # E:19: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "int" +t.setdefault("y", 123) # E:14: TypedDict "D" has no key "y" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -253,7 +264,7 @@ class D(A): # flags: --disallow-any-generics from typing import List, Callable def f(x: List) -> None: pass # E:10: Missing type parameters for generic type "List" -def g(x: list) -> None: pass # E:10: Implicit generic "Any". Use "typing.List" and specify generic parameters +def g(x: list) -> None: pass # E:10: Missing type parameters for generic type "list" if int(): c: Callable # E:8: Missing type parameters for generic type "Callable" [builtins fixtures/list.pyi] @@ -310,9 +321,18 @@ T = TypeVar('T', int, str) class C(Generic[T]): pass -def f(c: C[object]) -> None: pass # E:10: Value of type variable "T" of "C" cannot be "object" +def f(c: C[object]) -> None: pass # E:12: Value of type variable "T" of "C" cannot be "object" (C[object]()) # E:2: Value of type variable "T" of "C" cannot be "object" +[case testColumnInvalidLocationForParamSpec] +from typing import List +from typing_extensions import ParamSpec + +P = ParamSpec('P') +def foo(x: List[P]): pass # E:17: Invalid location for ParamSpec "P" \ + # N:17: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +[builtins fixtures/list.pyi] + [case testColumnSyntaxErrorInTypeAnnotation] if int(): def f(x # type: int, @@ -388,3 +408,17 @@ x[0] main:2:10:2:17: error: Incompatible types in assignment (expression has type "str", variable has type "int") main:6:3:7:1: error: Argument 1 to "f" has incompatible type "int"; expected "str" main:8:1:8:4: error: Value of type "int" is not indexable + +[case testEndColumnsWithTooManyTypeVars] +# flags: --pretty +import typing + +x1: typing.List[typing.List[int, int]] +x2: list[list[int, int]] +[out] +main:4:17: error: "list" expects 1 type argument, but 2 given + x1: typing.List[typing.List[int, int]] + ^~~~~~~~~~~~~~~~~~~~~ +main:5:10: error: "list" expects 1 type argument, but 2 given + x2: list[list[int, int]] + ^~~~~~~~~~~~~~ diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test index 1eefdd3c66c1b..a0a5c44b2ba54 100644 --- a/test-data/unit/check-ctypes.test +++ b/test-data/unit/check-ctypes.test @@ -16,7 +16,7 @@ a[2] = MyCInt(42) a[3] = b"bytes" # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "bytes" \ # N: Possible overload variants: \ # N: def __setitem__(self, int, Union[c_int, int], /) -> None \ - # N: def __setitem__(self, slice, List[Union[c_int, int]], /) -> None + # N: def __setitem__(self, slice, list[Union[c_int, int]], /) -> None for x in a: reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/floatdict.pyi] @@ -40,12 +40,12 @@ mya[0] = 42 mya[1] = ctypes.c_int(42) # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "c_int" \ # N: Possible overload variants: \ # N: def __setitem__(self, int, Union[MyCInt, int], /) -> None \ - # N: def __setitem__(self, slice, List[Union[MyCInt, int]], /) -> None + # N: def __setitem__(self, slice, list[Union[MyCInt, int]], /) -> None mya[2] = MyCInt(42) mya[3] = b"bytes" # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "bytes" \ # N: Possible overload variants: \ # N: def __setitem__(self, int, Union[MyCInt, int], /) -> None \ - # N: def __setitem__(self, slice, List[Union[MyCInt, int]], /) -> None + # N: def __setitem__(self, slice, list[Union[MyCInt, int]], /) -> None for myx in mya: reveal_type(myx) # N: Revealed type is "__main__.MyCInt" @@ -74,7 +74,7 @@ mya[2] = MyCInt(42) mya[3] = b"bytes" # E: No overload variant of "__setitem__" of "Array" matches argument types "int", "bytes" \ # N: Possible overload variants: \ # N: def __setitem__(self, int, Union[MyCInt, int, c_uint], /) -> None \ - # N: def __setitem__(self, slice, List[Union[MyCInt, int, c_uint]], /) -> None + # N: def __setitem__(self, slice, list[Union[MyCInt, int, c_uint]], /) -> None for myx in mya: reveal_type(myx) # N: Revealed type is "Union[__main__.MyCInt, builtins.int]" [builtins fixtures/floatdict.pyi] @@ -138,6 +138,7 @@ cua.raw # E: Array attribute "raw" is only available with element type "c_char" [case testCtypesAnyArrayAttrs] import ctypes +from typing import Any aa: ctypes.Array[Any] reveal_type(aa.value) # N: Revealed type is "Any" diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 2b3b3f4a86958..0c157510cb342 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] @@ -576,8 +579,7 @@ plugins=/test-data/unit/plugins/method_sig_hook.py [case testMethodSignatureHookNamesFullyQualified] # flags: --config-file tmp/mypy.ini -from mypy_extensions import TypedDict -from typing import NamedTuple +from typing import NamedTuple, TypedDict class FullyQualifiedTestClass: @classmethod @@ -598,6 +600,7 @@ reveal_type(FullyQualifiedTestNamedTuple('')._asdict()) # N: Revealed type is "b \[mypy] plugins=/test-data/unit/plugins/fully_qualified_test_hook.py [builtins fixtures/classmethod.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDynamicClassPlugin] # flags: --config-file tmp/mypy.ini @@ -749,7 +752,7 @@ plugins=/test-data/unit/plugins/common_api_incremental.py [out] [out2] tmp/a.py:3: note: Revealed type is "builtins.str" -tmp/a.py:4: error: "Type[Base]" has no attribute "__magic__" +tmp/a.py:4: error: "type[Base]" has no attribute "__magic__" [case testArgKindsMethod] # flags: --config-file tmp/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 @@ -1003,7 +1007,7 @@ reveal_type(Cls.attr) # N: Revealed type is "builtins.int" plugins=/test-data/unit/plugins/class_attr_hook.py [case testClassAttrPluginPartialType] -# flags: --config-file tmp/mypy.ini +# flags: --config-file tmp/mypy.ini --no-local-partial-types class Cls: attr = None @@ -1094,3 +1098,31 @@ reveal_type(1) # N: Revealed type is "Literal[1]?" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/custom_errorcode.py + + +[case testPyprojectPluginsTrailingComma] +# flags: --config-file tmp/pyproject.toml +[file pyproject.toml] +# This test checks that trailing commas in string-based `plugins` are allowed. +\[tool.mypy] +plugins = """ + /test-data/unit/plugins/function_sig_hook.py, + /test-data/unit/plugins/method_in_decorator.py, +""" +[out] + + + +[case magicMethodReverse] +# flags: --config-file tmp/mypy.ini +from typing import Literal + +op1: Literal[3] = 3 +op2: Literal[4] = 4 +c = op1 + op2 +reveal_type(c) # N: Revealed type is "Literal[7]" + +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/magic_method.py +[builtins fixtures/ops.pyi] diff --git a/test-data/unit/check-dataclass-transform.test b/test-data/unit/check-dataclass-transform.test index 51b2e186214ff..89b8dc88c98f3 100644 --- a/test-data/unit/check-dataclass-transform.test +++ b/test-data/unit/check-dataclass-transform.test @@ -118,7 +118,7 @@ from typing import dataclass_transform, Type BOOLEAN_CONSTANT = True -@dataclass_transform(nonexistant=True) # E: Unrecognized dataclass_transform parameter "nonexistant" +@dataclass_transform(nonexistent=True) # E: Unrecognized dataclass_transform parameter "nonexistent" def foo(cls: Type) -> Type: return cls @@ -265,7 +265,7 @@ class Foo: Foo(a=5, b_=1) # E: Unexpected keyword argument "a" for "Foo" Foo(a_=1, b_=1, noinit=1) # E: Unexpected keyword argument "noinit" for "Foo" -Foo(1, 2, 3) # E: Too many positional arguments for "Foo" +Foo(1, 2, 3) # (a, b, unused1) foo = Foo(1, 2, kwonly=3) reveal_type(foo.noinit) # N: Revealed type is "builtins.int" reveal_type(foo.unused1) # N: Revealed type is "builtins.int" @@ -505,7 +505,7 @@ class FunctionModel: integer_: tuple FunctionModel(string_="abc", integer_=1) -FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[Never, ...]"; expected "int" +FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -528,7 +528,7 @@ class FunctionModel: integer_: int FunctionModel(string_="abc", integer_=1) -FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "Tuple[Never, ...]"; expected "int" +FunctionModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "FunctionModel" has incompatible type "tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -551,7 +551,7 @@ class BaseClassModel(ModelBase): integer_: tuple BaseClassModel(string_="abc", integer_=1) -BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" +BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -573,7 +573,7 @@ class BaseClassModel(ModelBase): integer_: int BaseClassModel(string_="abc", integer_=1) -BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" +BaseClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "BaseClassModel" has incompatible type "tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -598,7 +598,7 @@ class MetaClassModel(ModelBaseWithMeta): integer_: tuple MetaClassModel(string_="abc", integer_=1) -MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" +MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] @@ -623,7 +623,7 @@ class MetaClassModel(ModelBaseWithMeta): integer_: int MetaClassModel(string_="abc", integer_=1) -MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "Tuple[Never, ...]"; expected "int" +MetaClassModel(string_="abc", integer_=tuple()) # E: Argument "integer_" to "MetaClassModel" has incompatible type "tuple[Never, ...]"; expected "int" [typing fixtures/typing-full.pyi] [builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 0f726242b25b6..f43c49c200c86 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -260,7 +260,7 @@ class FrozenBase: pass @dataclass -class BadNormalDerived(FrozenBase): # E: Cannot inherit non-frozen dataclass from a frozen one +class BadNormalDerived(FrozenBase): # E: Non-frozen dataclass cannot inherit from a frozen dataclass pass @dataclass @@ -268,7 +268,7 @@ class NormalBase: pass @dataclass(frozen=True) -class BadFrozenDerived(NormalBase): # E: Cannot inherit frozen dataclass from a non-frozen one +class BadFrozenDerived(NormalBase): # E: Frozen dataclass cannot inherit from a non-frozen dataclass pass [builtins fixtures/dataclasses.pyi] @@ -460,14 +460,16 @@ from dataclasses import dataclass, field, KW_ONLY class Application: _: KW_ONLY name: str = 'Unnamed' - rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one + rating: int = field(kw_only=False) Application(name='name', rating=5) -Application() # E: Missing positional argument "name" in call to "Application" -Application('name') # E: Too many positional arguments for "Application" # E: Too few arguments for "Application" -Application('name', 123) # E: Too many positional arguments for "Application" -Application('name', rating=123) # E: Too many positional arguments for "Application" - +Application() # E: Missing positional argument "rating" in call to "Application" +Application(123) +Application('name') # E: Argument 1 to "Application" has incompatible type "str"; expected "int" +Application('name', 123) # E: Too many positional arguments for "Application" \ + # E: Argument 1 to "Application" has incompatible type "str"; expected "int" \ + # E: Argument 2 to "Application" has incompatible type "int"; expected "str" +Application(123, rating=123) # E: "Application" gets multiple values for keyword argument "rating" [builtins fixtures/dataclasses.pyi] [case testDataclassesOrderingKwOnlyWithSentinelAndSubclass] @@ -549,7 +551,7 @@ class A: @classmethod def foo(cls, x: Union[int, str]) -> Union[int, str]: - reveal_type(cls) # N: Revealed type is "Type[__main__.A]" + reveal_type(cls) # N: Revealed type is "type[__main__.A]" reveal_type(cls.other()) # N: Revealed type is "builtins.str" return x @@ -700,10 +702,10 @@ class A(Generic[T]): return self.z[0] def problem(self) -> T: - return self.z # E: Incompatible return value type (got "List[T]", expected "T") + return self.z # E: Incompatible return value type (got "list[T]", expected "T") reveal_type(A) # N: Revealed type is "def [T] (x: T`1, y: T`1, z: builtins.list[T`1]) -> __main__.A[T`1]" -A(1, 2, ["a", "b"]) # E: Cannot infer type argument 1 of "A" +A(1, 2, ["a", "b"]) # E: Cannot infer value of type parameter "T" of "A" a = A(1, 2, [1, 2]) reveal_type(a) # N: Revealed type is "__main__.A[builtins.int]" reveal_type(a.x) # N: Revealed type is "builtins.int" @@ -836,7 +838,7 @@ class A(Generic[T]): @classmethod def foo(cls) -> None: - reveal_type(cls) # N: Revealed type is "Type[__main__.A[T`1]]" + reveal_type(cls) # N: Revealed type is "type[__main__.A[T`1]]" cls.x # E: Access to generic instance variables via class is ambiguous @classmethod @@ -936,7 +938,7 @@ T = TypeVar('T', bound='A') class A: @classmethod def make(cls: Type[T]) -> T: - reveal_type(cls) # N: Revealed type is "Type[T`-1]" + reveal_type(cls) # N: Revealed type is "type[T`-1]" reveal_type(cls()) # N: Revealed type is "T`-1" return cls() [builtins fixtures/dataclasses.pyi] @@ -1386,7 +1388,7 @@ class Foo: bar: float = field(**{"repr": False}) [out] main:6: error: Unpacking **kwargs in "field()" is not supported -main:6: error: No overload variant of "field" matches argument type "Dict[str, bool]" +main:6: error: No overload variant of "field" matches argument type "dict[str, bool]" main:6: note: Possible overload variants: main:6: note: def [_T] field(*, default: _T, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T main:6: note: def [_T] field(*, default_factory: Callable[[], _T], init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T @@ -1408,7 +1410,7 @@ class C: [case testDataclassFieldWithTypedDictUnpacking] from dataclasses import dataclass, field -from typing_extensions import TypedDict +from typing import TypedDict class FieldKwargs(TypedDict): repr: bool @@ -1421,6 +1423,7 @@ class Foo: reveal_type(Foo(bar=1.5)) # N: Revealed type is "__main__.Foo" [builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDataclassWithSlotsArg] # flags: --python-version 3.10 @@ -1519,14 +1522,14 @@ class Some: y: str z: bool -reveal_type(Some.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]" +reveal_type(Some.__slots__) # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.str]" @dataclass(slots=True) class Other: x: int y: str -reveal_type(Other.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(Other.__slots__) # N: Revealed type is "tuple[builtins.str, builtins.str]" @dataclass @@ -1534,7 +1537,7 @@ class NoSlots: x: int y: str -NoSlots.__slots__ # E: "Type[NoSlots]" has no attribute "__slots__" +NoSlots.__slots__ # E: "type[NoSlots]" has no attribute "__slots__" [builtins fixtures/dataclasses.pyi] @@ -1833,17 +1836,33 @@ class One: bar: int baz: str o: One -reveal_type(o.__match_args__) # N: Revealed type is "Tuple[Literal['bar'], Literal['baz']]" +reveal_type(o.__match_args__) # N: Revealed type is "tuple[Literal['bar'], Literal['baz']]" @dataclass(match_args=True) class Two: bar: int t: Two -reveal_type(t.__match_args__) # N: Revealed type is "Tuple[Literal['bar']]" +reveal_type(t.__match_args__) # N: Revealed type is "tuple[Literal['bar']]" @dataclass class Empty: ... e: Empty -reveal_type(e.__match_args__) # N: Revealed type is "Tuple[()]" +reveal_type(e.__match_args__) # N: Revealed type is "tuple[()]" +[builtins fixtures/dataclasses.pyi] + +[case testDataclassWithMatchArgsAndKwOnly] +# flags: --python-version 3.10 +from dataclasses import dataclass, field +@dataclass(kw_only=True) +class One: + a: int + b: str +reveal_type(One.__match_args__) # N: Revealed type is "tuple[()]" + +@dataclass(kw_only=True) +class Two: + a: int = field(kw_only=False) + b: str +reveal_type(Two.__match_args__) # N: Revealed type is "tuple[Literal['a']]" [builtins fixtures/dataclasses.pyi] [case testDataclassWithoutMatchArgs] @@ -1894,7 +1913,6 @@ SecondClass().SECOND_CONST = 42 # E: Cannot assign to final attribute "SECOND_C [builtins fixtures/dataclasses.pyi] [case testDataclassFieldsProtocol] -# flags: --python-version 3.9 from dataclasses import dataclass from typing import Any, Protocol @@ -2037,7 +2055,7 @@ from dataclasses import dataclass, replace, InitVar from typing import ClassVar @dataclass -class A: +class A: # N: "replace" of "A" defined here x: int q: InitVar[int] q2: InitVar[int] = 0 @@ -2081,7 +2099,7 @@ a_or_b: Union[A[int], B] _ = replace(a_or_b, x=42, y=True, init_var=42) _ = replace(a_or_b, x=42, y=True) # E: Missing named argument "init_var" for "replace" of "Union[A[int], B]" _ = replace(a_or_b, x=42, y=True, z='42', init_var=42) # E: Argument "z" to "replace" of "Union[A[int], B]" has incompatible type "str"; expected "Never" -_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never" +_ = replace(a_or_b, x=42, y=True, w={}, init_var=42) # E: Argument "w" to "replace" of "Union[A[int], B]" has incompatible type "dict[Never, Never]"; expected "Never" _ = replace(a_or_b, y=42, init_var=42) # E: Argument "y" to "replace" of "Union[A[int], B]" has incompatible type "int"; expected "bool" [builtins fixtures/tuple.pyi] @@ -2186,7 +2204,7 @@ from dataclasses import is_dataclass, replace def f(x: object) -> None: _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "object" if is_dataclass(x): - _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, Type[DataclassInstance]]" + _ = replace(x) # E: Value of type variable "_DataclassT" of "replace" cannot be "Union[DataclassInstance, type[DataclassInstance]]" if not isinstance(x, type): _ = replace(x) @@ -2407,7 +2425,7 @@ main:7: note: Superclass: main:7: note: def __post_init__(self: Test, y: str) -> None main:7: note: Subclass: main:7: note: @classmethod -main:7: note: def __post_init__(cls: Type[Test]) -> None +main:7: note: def __post_init__(cls: type[Test]) -> None [case testPostInitStaticMethod] from dataclasses import dataclass, InitVar @@ -2492,12 +2510,14 @@ class Child(Base): [case testDunderReplacePresent] # flags: --python-version 3.13 -from dataclasses import dataclass +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) @@ -2523,3 +2543,142 @@ 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 --enable-error-code mutable-override +from dataclasses import dataclass +from typing import Optional +from typing_extensions import dataclass_transform + +@dataclass +class Base: + a: Optional[int] + +@dataclass +class Child(Base): + a: int # E: Covariant override of a mutable attribute (base class "Base" defined the type as "Optional[int]", expression has type "int") + +@dataclass +class Other(Base): + a: str # E: Incompatible types in assignment (expression has type "str", base class "Base" defined the type as "Optional[int]") + +@dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class X(metaclass=DCMeta): + a: Optional[int] + +class Y(X): + a: int # E: Covariant override of a mutable attribute (base class "X" defined the type as "Optional[int]", expression has type "int") +[builtins fixtures/tuple.pyi] + + +[case testFrozenWithFinal] +from dataclasses import dataclass +from typing import Final + +@dataclass(frozen=True) +class My: + a: Final = 1 + b: Final[int] = 2 + +reveal_type(My.a) # N: Revealed type is "Literal[1]?" +reveal_type(My.b) # N: Revealed type is "builtins.int" +My.a = 1 # E: Cannot assign to final attribute "a" +My.b = 2 # E: Cannot assign to final attribute "b" + +m = My() +reveal_type(m.a) # N: Revealed type is "Literal[1]?" +reveal_type(m.b) # N: Revealed type is "builtins.int" + +m.a = 1 # E: Cannot assign to final attribute "a" +m.b = 2 # E: Cannot assign to final attribute "b" +[builtins fixtures/tuple.pyi] + +[case testNoCrashForDataclassNamedTupleCombination] +# flags: --python-version 3.13 +from dataclasses import dataclass +from typing import NamedTuple + +@dataclass +class A(NamedTuple): # E: A NamedTuple cannot be a dataclass + i: int + +class B1(NamedTuple): + i: int +@dataclass +class B2(B1): # E: A NamedTuple cannot be a dataclass + pass + +[builtins fixtures/tuple.pyi] + +[case testDataclassesTypeGuard] +import dataclasses + +raw_target: object + +if isinstance(raw_target, type) and dataclasses.is_dataclass(raw_target): + reveal_type(raw_target) # N: Revealed type is "type[dataclasses.DataclassInstance]" +[builtins fixtures/tuple.pyi] + +[case testDataclassKwOnlyArgsLast] +from dataclasses import dataclass, field + +@dataclass +class User: + id: int = field(kw_only=True) + name: str + +User("Foo", id=0) +[builtins fixtures/tuple.pyi] + +[case testDataclassKwOnlyArgsDefaultAllowedNonLast] +from dataclasses import dataclass, field + +@dataclass +class User: + id: int = field(kw_only=True, default=0) + name: str + +User() # E: Missing positional argument "name" in call to "User" +User("") +User(0) # E: Argument 1 to "User" has incompatible type "int"; expected "str" +User("", 0) # E: Too many positional arguments for "User" +User("", id=0) +User("", name="") # E: "User" gets multiple values for keyword argument "name" +[builtins fixtures/tuple.pyi] + +[case testDataclassDefaultFactoryTypedDict] +from dataclasses import dataclass, field +from mypy_extensions import TypedDict + +class Person(TypedDict, total=False): + name: str + +@dataclass +class Job: + person: Person = field(default_factory=Person) + +class PersonBad(TypedDict): + name: str + +@dataclass +class JobBad: + person: PersonBad = field(default_factory=PersonBad) # E: Argument "default_factory" to "field" has incompatible type "type[PersonBad]"; expected "Callable[[], PersonBad]" +[builtins fixtures/dict.pyi] + +[case testDataclassInitVarRedefinitionNoCrash] +# https://github.com/python/mypy/issues/19443 +from dataclasses import InitVar, dataclass + +class ClassA: + def value(self) -> int: + return 0 + +@dataclass +class ClassB(ClassA): + value: InitVar[int] + + def value(self) -> int: # E: Name "value" already defined on line 10 + return 0 +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test new file mode 100644 index 0000000000000..607e9d7679561 --- /dev/null +++ b/test-data/unit/check-deprecated.test @@ -0,0 +1,922 @@ +-- 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 testDeprecatedOverloadedInstanceMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union, overload +from typing_extensions import deprecated + +class A: + @overload + @deprecated("pass `str` instead") + def f(self, v: int) -> None: ... + @overload + def f(self, v: str) -> None: ... + def f(self, v: Union[int, str]) -> None: ... + + @overload + def g(self, v: int) -> None: ... + @overload + @deprecated("pass `int` instead") + def g(self, v: str) -> None: ... + def g(self, v: Union[int, str]) -> None: ... + + @overload + def h(self, v: int) -> A: ... + @overload + def h(self, v: str) -> A: ... + @deprecated("use `h2` instead") + def h(self, v: Union[int, str]) -> A: ... + +class B(A): ... + +a = A() +a.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +a.f("x") +a.g(1) +a.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +b = B() +b.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +b.f("x") +b.g(1) +b.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedOverloadedClassMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union, overload +from typing_extensions import deprecated + +class A: + @overload + @classmethod + @deprecated("pass `str` instead") + def f(cls, v: int) -> None: ... + @overload + @classmethod + def f(cls, v: str) -> None: ... + @classmethod + def f(cls, v: Union[int, str]) -> None: ... + + @overload + @classmethod + def g(cls, v: int) -> None: ... + @overload + @classmethod + @deprecated("pass `int` instead") + def g(cls, v: str) -> None: ... + @classmethod + def g(cls, v: Union[int, str]) -> None: ... + + @overload + @classmethod + def h(cls, v: int) -> A: ... + @overload + @classmethod + def h(cls, v: str) -> A: ... + @deprecated("use `h2` instead") + @classmethod + def h(cls, v: Union[int, str]) -> A: ... + +class B(A): ... + +a = A() +a.f(1) # E: overload def (cls: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +a.f("x") +a.g(1) +a.g("x") # E: overload def (cls: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +b = B() +b.f(1) # E: overload def (cls: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +b.f("x") +b.g(1) +b.g("x") # E: overload def (cls: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedOverloadedStaticMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union, overload +from typing_extensions import deprecated + +class A: + @overload + @staticmethod + @deprecated("pass `str` instead") + def f(v: int) -> None: ... + @overload + @staticmethod + def f(v: str) -> None: ... + @staticmethod + def f(v: Union[int, str]) -> None: ... + + @overload + @staticmethod + def g(v: int) -> None: ... + @overload + @staticmethod + @deprecated("pass `int` instead") + def g(v: str) -> None: ... + @staticmethod + def g(v: Union[int, str]) -> None: ... + + @overload + @staticmethod + def h(v: int) -> A: ... + @overload + @staticmethod + def h(v: str) -> A: ... + @deprecated("use `h2` instead") + @staticmethod + def h(v: Union[int, str]) -> A: ... + +class B(A): ... + +a = A() +a.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +a.f("x") +a.g(1) +a.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +b = B() +b.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead +b.f("x") +b.g(1) +b.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead +b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead +b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead + +[builtins fixtures/classmethod.pyi] + + +[case testDeprecatedOverloadedSpecialMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union, overload +from typing_extensions import deprecated + +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, Generic, Optional, overload, TypeVar, Union +from typing_extensions import deprecated + +T = TypeVar("T") + +@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 D4(Generic[T]): + @overload + def __get__(self, obj: None, objtype: Any) -> T: ... + @overload + @deprecated("deprecated instance access") + def __get__(self, obj: C, objtype: Any) -> T: ... + def __get__(self, obj: Optional[C], objtype: Any) -> T: ... + +class C: + d1 = D1() # E: class __main__.D1 is deprecated: use E1 instead + d2 = D2() + d3 = D3() + d4 = D4[int]() + +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 + +C.d4 +c.d4 # E: overload def (self: __main__.D4[T`1], obj: __main__.C, objtype: Any) -> T`1 of function __main__.D4.__get__ is deprecated: deprecated instance access +[builtins fixtures/property.pyi] + + +[case testDeprecatedOverloadedFunction] +# flags: --enable-error-code=deprecated + +from typing import Any, overload, Union +from typing_extensions import deprecated + +int_or_str: Union[int, str] +any: Any + +@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(int_or_str) # E: function __main__.f is deprecated: use f2 instead +f(any) # 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(int_or_str) # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead +g(any) +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(int_or_str) # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead +h(any) +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 + +@overload +def i(x: int) -> int: ... +@overload +@deprecated("work with int instead") +def i(x: str) -> str: ... +@overload +def i(x: Any) -> Any: ... +def i(x: Union[int, str]) -> Union[int, str]: ... + +i +i(1) +i("x") # E: overload def (x: builtins.str) -> builtins.str of function __main__.i is deprecated: work with int instead +i(int_or_str) # E: overload def (x: builtins.str) -> builtins.str of function __main__.i is deprecated: work with int instead +i(any) +i(1.0) + +@overload +def j(x: int) -> int: ... +@overload +def j(x: str) -> str: ... +@overload +@deprecated("work with int or str instead") +def j(x: Any) -> Any: ... +def j(x: Union[int, str]) -> Union[int, str]: ... + +j +j(1) +j("x") +j(int_or_str) +j(any) +j(1.0) # E: overload def (x: Any) -> Any of function __main__.j is deprecated: work with int or str instead + +@overload +@deprecated("work with str instead") +def k(x: int) -> int: ... +@overload +def k(x: str) -> str: ... +@overload +@deprecated("work with str instead") +def k(x: object) -> Any: ... +def k(x: object) -> Union[int, str]: ... + +k +k(1) # E: overload def (x: builtins.int) -> builtins.int of function __main__.k is deprecated: work with str instead +k("x") +k(int_or_str) # E: overload def (x: builtins.int) -> builtins.int of function __main__.k is deprecated: work with str instead +k(any) +k(1.0) # E: overload def (x: builtins.object) -> Any of function __main__.k is deprecated: work with str instead +[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, overload +from typing_extensions import deprecated + +@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] + +[case testDeprecatedExclude] +# flags: --enable-error-code=deprecated --deprecated-calls-exclude=m.C --deprecated-calls-exclude=m.D --deprecated-calls-exclude=m.E.f --deprecated-calls-exclude=m.E.g --deprecated-calls-exclude=m.E.__add__ +from m import C, D, E + +[file m.py] +from typing import Union, overload +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: + def __init__(self) -> None: ... + +c: C +C() +C.__init__(c) + +class D: + @deprecated("use D.g instead") + def f(self) -> None: ... + + def g(self) -> None: ... + +D.f +D().f +D().f() + +class E: + @overload + def f(self, x: int) -> int: ... + @overload + def f(self, x: str) -> str: ... + @deprecated("use E.f2 instead") + def f(self, x: Union[int, str]) -> Union[int, str]: ... + + @deprecated("use E.h instead") + def g(self) -> None: ... + + @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: ... + +E().f(1) +E().f("x") + +e = E() +e.g() +e + 1 +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test index 21fd52169ff52..166073dd15534 100644 --- a/test-data/unit/check-dynamic-typing.test +++ b/test-data/unit/check-dynamic-typing.test @@ -252,7 +252,7 @@ if int(): if int(): a = d.foo(a, a) d.x = a -d.x.y.z # E: "A" has no attribute "y" +d.x.y.z class A: pass [out] @@ -279,7 +279,7 @@ t2: Tuple[A, A] d: Any if int(): - t2 = (d, d, d) # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[A, A]") + t2 = (d, d, d) # E: Incompatible types in assignment (expression has type "tuple[Any, Any, Any]", variable has type "tuple[A, A]") if int(): t2 = (d, d) @@ -320,8 +320,10 @@ d = None # All ok d = t d = g d = A -t = d -f = d + +d1: Any +t = d1 +f = d1 [builtins fixtures/tuple.pyi] @@ -569,7 +571,7 @@ a: A A(a) # E: Missing positional argument "b" in call to "A" if int(): - f1 = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "Callable[[A], A]") + f1 = A # E: Incompatible types in assignment (expression has type "type[A]", variable has type "Callable[[A], A]") A(a, a) if int(): @@ -597,8 +599,8 @@ t5: Tuple[Any, Any, Any] def f(): t1, t2, t3, t4, t5 # Prevent redefinition -t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[Any, Any]") -t5 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[Any, Any, Any]") +t3 = t5 # E: Incompatible types in assignment (expression has type "tuple[Any, Any, Any]", variable has type "tuple[Any, Any]") +t5 = t4 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[Any, Any, Any]") t1 = t1 t1 = t2 diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 78a114eda7642..3bcf9745a801c 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -11,12 +11,14 @@ 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 -- ----------------------- [case testEnumCreatedFromStringLiteral] from enum import Enum -from typing_extensions import Literal +from typing import Literal x: Literal['ANT BEE CAT DOG'] = 'ANT BEE CAT DOG' Animal = Enum('Animal', x) @@ -29,7 +31,7 @@ reveal_type(Animal.DOG) # N: Revealed type is "Literal[__main__.Animal.DOG]?" [case testEnumCreatedFromFinalValue] from enum import Enum -from typing_extensions import Final +from typing import Final x: Final['str'] = 'ANT BEE CAT DOG' Animal = Enum('Animal', x) @@ -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 @@ -151,7 +154,7 @@ def infer_truth(truth: Truth) -> None: reveal_type(truth.value) # N: Revealed type is "builtins.str" [builtins fixtures/primitives.pyi] -[case testEnumValueInhomogenous] +[case testEnumValueInhomogeneous] from enum import Enum class Truth(Enum): true = 'True' @@ -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 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 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 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 @@ -479,11 +601,12 @@ T = Enum('T', keyword='a b') # E: Unexpected keyword argument "keyword" U = Enum('U', *['a']) # E: Unexpected arguments to Enum() V = Enum('U', **{'a': 1}) # E: Unexpected arguments to Enum() W = Enum('W', 'a b') -W.c # E: "Type[W]" has no attribute "c" +W.c # E: "type[W]" has no attribute "c" X = Enum('Something', 'a b') # E: String argument 1 "Something" to enum.Enum(...) does not match variable name "X" reveal_type(X.a) # N: Revealed type is "Literal[__main__.Something@23.a]?" -X.asdf # E: "Type[Something@23]" has no attribute "asdf" +X.asdf # E: "type[Something@23]" has no attribute "asdf" +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [case testFunctionalEnumFlag] @@ -495,9 +618,9 @@ reveal_type(B.a) # N: Revealed type is "Literal[__main__.B.a]?" reveal_type(A.x.name) # N: Revealed type is "Literal['x']?" 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" +reveal_type(A.x.value) # N: Revealed type is "builtins.int" +reveal_type(B.a.value) # N: Revealed type is "builtins.int" +[builtins fixtures/enum.pyi] [case testAnonymousFunctionalEnum] from enum import Enum @@ -507,6 +630,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 +644,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 +662,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 +673,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 +691,7 @@ Gu.a Gb.a Hu.a Hb.a +[builtins fixtures/enum.pyi] [out] [case testEnumIncremental] @@ -576,6 +704,7 @@ class E(Enum): a = 1 b = 2 F = Enum('F', 'a b') +[builtins fixtures/enum.pyi] [rechecked] [stale] [out1] @@ -596,7 +725,7 @@ reveal_type(Test.a) # N: Revealed type is "Literal[__main__.Test.a]?" [case testEnumAttributeAccessMatrix] from enum import Enum, IntEnum, IntFlag, Flag, EnumMeta, auto -from typing_extensions import Literal +from typing import Literal def is_x(val: Literal['x']) -> None: pass @@ -625,12 +754,10 @@ class B2(IntEnum): class B3(IntEnum): x = 1 -# TODO: getting B1.x._value_ and B2.x._value_ to have type 'int' requires a typeshed change - is_x(reveal_type(B1.x.name)) # N: Revealed type is "Literal['x']" is_x(reveal_type(B1.x._name_)) # N: Revealed type is "Literal['x']" reveal_type(B1.x.value) # N: Revealed type is "builtins.int" -reveal_type(B1.x._value_) # N: Revealed type is "Any" +reveal_type(B1.x._value_) # N: Revealed type is "builtins.int" is_x(reveal_type(B2.x.name)) # N: Revealed type is "Literal['x']" is_x(reveal_type(B2.x._name_)) # N: Revealed type is "Literal['x']" reveal_type(B2.x.value) # N: Revealed type is "builtins.int" @@ -640,9 +767,6 @@ is_x(reveal_type(B3.x._name_)) # N: Revealed type is "Literal['x']" reveal_type(B3.x.value) # N: Revealed type is "Literal[1]?" reveal_type(B3.x._value_) # N: Revealed type is "Literal[1]?" -# TODO: C1.x.value and C2.x.value should also be of type 'int' -# This requires either a typeshed change or a plugin refinement - C1 = IntFlag('C1', 'x') class C2(IntFlag): x = auto() @@ -651,8 +775,8 @@ class C3(IntFlag): is_x(reveal_type(C1.x.name)) # N: Revealed type is "Literal['x']" is_x(reveal_type(C1.x._name_)) # N: Revealed type is "Literal['x']" -reveal_type(C1.x.value) # N: Revealed type is "Any" -reveal_type(C1.x._value_) # N: Revealed type is "Any" +reveal_type(C1.x.value) # N: Revealed type is "builtins.int" +reveal_type(C1.x._value_) # N: Revealed type is "builtins.int" is_x(reveal_type(C2.x.name)) # N: Revealed type is "Literal['x']" is_x(reveal_type(C2.x._name_)) # N: Revealed type is "Literal['x']" reveal_type(C2.x.value) # N: Revealed type is "builtins.int" @@ -670,8 +794,8 @@ class D3(Flag): is_x(reveal_type(D1.x.name)) # N: Revealed type is "Literal['x']" is_x(reveal_type(D1.x._name_)) # N: Revealed type is "Literal['x']" -reveal_type(D1.x.value) # N: Revealed type is "Any" -reveal_type(D1.x._value_) # N: Revealed type is "Any" +reveal_type(D1.x.value) # N: Revealed type is "builtins.int" +reveal_type(D1.x._value_) # N: Revealed type is "builtins.int" is_x(reveal_type(D2.x.name)) # N: Revealed type is "Literal['x']" is_x(reveal_type(D2.x._name_)) # N: Revealed type is "Literal['x']" reveal_type(D2.x.value) # N: Revealed type is "builtins.int" @@ -734,6 +858,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] @@ -741,7 +866,7 @@ main:2: note: Revealed type is "Literal['foo']?" [case testEnumReachabilityChecksBasic] from enum import Enum -from typing_extensions import Literal +from typing import Literal class Foo(Enum): A = 1 @@ -757,7 +882,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 +892,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: @@ -793,14 +918,14 @@ reveal_type(y) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityChecksWithOrdering] from enum import Enum -from typing_extensions import Literal +from typing import Literal class Foo(Enum): _order_ = "A B" A = 1 B = 2 -Foo._order_ # E: "Type[Foo]" has no attribute "_order_" +Foo._order_ # E: "type[Foo]" has no attribute "_order_" x: Literal[Foo.A, Foo.B] if x is Foo.A: @@ -815,7 +940,7 @@ class Bar(Enum): A = 1 B = 2 -Bar.__order__ # E: "Type[Bar]" has no attribute "__order__" +Bar.__order__ # E: "type[Bar]" has no attribute "__order__" y: Literal[Bar.A, Bar.B] if y is Bar.A: @@ -844,7 +969,7 @@ else: [case testEnumReachabilityChecksIndirect] from enum import Enum -from typing_extensions import Literal, Final +from typing import Final, Literal class Foo(Enum): A = 1 @@ -909,7 +1034,7 @@ else: [case testEnumReachabilityNoNarrowingForUnionMessiness] from enum import Enum -from typing_extensions import Literal +from typing import Literal class Foo(Enum): A = 1 @@ -949,7 +1074,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,12 +1086,11 @@ 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 -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Foo(Enum): A = 1 @@ -999,8 +1123,7 @@ reveal_type(x3) # N: Revealed type is "Union[__main__.Foo, __main__.Bar]" [builtins fixtures/bool.pyi] [case testEnumReachabilityPEP484ExampleWithFinal] -from typing import Union -from typing_extensions import Final +from typing import Final, Union from enum import Enum class Empty(Enum): @@ -1045,8 +1168,7 @@ def process(response: Union[str, Reason] = '') -> str: [case testEnumReachabilityPEP484ExampleSingleton] -from typing import Union -from typing_extensions import Final +from typing import Final, Union from enum import Enum class Empty(Enum): @@ -1069,17 +1191,20 @@ def func(x: Union[int, None, Empty] = _empty) -> int: [builtins fixtures/primitives.pyi] [case testEnumReachabilityPEP484ExampleSingletonWithMethod] -from typing import Union -from typing_extensions import Final -from enum import Enum +# flags: --python-version 3.11 +from typing import Final, Union +from enum import Enum, member class Empty(Enum): - token = lambda x: x + # note, that without `member` we cannot tell that `token` is a member: + token = member(lambda x: x) def f(self) -> int: return 1 _empty = Empty.token +reveal_type(_empty) # N: Revealed type is "__main__.Empty" +reveal_type(Empty.f) # N: Revealed type is "def (self: __main__.Empty) -> builtins.int" def func(x: Union[int, None, Empty] = _empty) -> int: boom = x + 42 # E: Unsupported left operand type for + ("None") \ @@ -1104,6 +1229,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 @@ -1199,7 +1325,7 @@ reveal_type(x) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityWithChainingDirectConflict] # flags: --warn-unreachable from enum import Enum -from typing_extensions import Literal, Final +from typing import Final, Literal class Foo(Enum): A = 1 @@ -1234,7 +1360,7 @@ reveal_type(x) # N: Revealed type is "__main__.Foo" [case testEnumReachabilityWithChainingBigDisjoints] # flags: --warn-unreachable from enum import Enum -from typing_extensions import Literal, Final +from typing import Final, Literal class Foo(Enum): A = 1 @@ -1307,6 +1433,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 +1452,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] @@ -1355,8 +1482,7 @@ reveal_type(a._value_) # N: Revealed type is "Any" # as the full type, regardless of the amount of elements # the enum contains. from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Foo(Enum): A = 1 @@ -1374,7 +1500,7 @@ def f(x: Foo): [case testEnumTypeCompatibleWithLiteralUnion] from enum import Enum -from typing_extensions import Literal +from typing import Literal class E(Enum): A = 1 @@ -1398,6 +1524,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 +1537,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 +1545,7 @@ from enum import Enum class Types(Enum): key = 0 value = 1 +[builtins fixtures/enum.pyi] [case testEnumReusedKeys] @@ -1484,6 +1613,65 @@ class ErrorIntFlagWithoutValue(NonEmptyIntFlag): # E: Cannot extend enum with e pass [builtins fixtures/bool.pyi] +[case testEnumImplicitlyFinalForSubclassingWithCallableMember] +# flags: --python-version 3.11 +from enum import Enum, IntEnum, Flag, IntFlag, member + +class NonEmptyEnum(Enum): + @member + def call(self) -> None: ... +class NonEmptyIntEnum(IntEnum): + @member + def call(self) -> None: ... +class NonEmptyFlag(Flag): + @member + def call(self) -> None: ... +class NonEmptyIntFlag(IntFlag): + @member + def call(self) -> None: ... + +class ErrorEnumWithoutValue(NonEmptyEnum): # E: Cannot extend enum with existing members: "NonEmptyEnum" + pass +class ErrorIntEnumWithoutValue(NonEmptyIntEnum): # E: Cannot extend enum with existing members: "NonEmptyIntEnum" + pass +class ErrorFlagWithoutValue(NonEmptyFlag): # E: Cannot extend enum with existing members: "NonEmptyFlag" + pass +class ErrorIntFlagWithoutValue(NonEmptyIntFlag): # E: Cannot extend enum with existing members: "NonEmptyIntFlag" + pass +[builtins fixtures/bool.pyi] + +[case testEnumCanExtendEnumsWithNonMembers] +# flags: --python-version 3.11 +from enum import Enum, IntEnum, Flag, IntFlag, nonmember + +class NonEmptyEnum(Enum): + x = nonmember(1) +class NonEmptyIntEnum(IntEnum): + x = nonmember(1) +class NonEmptyFlag(Flag): + x = nonmember(1) +class NonEmptyIntFlag(IntFlag): + x = nonmember(1) + +class ErrorEnumWithoutValue(NonEmptyEnum): + pass +class ErrorIntEnumWithoutValue(NonEmptyIntEnum): + pass +class ErrorFlagWithoutValue(NonEmptyFlag): + pass +class ErrorIntFlagWithoutValue(NonEmptyIntFlag): + pass +[builtins fixtures/bool.pyi] + +[case testLambdaIsNotEnumMember] +from enum import Enum + +class My(Enum): + x = lambda a: a + +class Other(My): ... +[builtins fixtures/bool.pyi] + [case testSubclassingNonFinalEnums] from enum import Enum, IntEnum, Flag, IntFlag, EnumMeta @@ -1708,6 +1896,10 @@ from enum import Enum class A(Enum): class Inner: pass class B(A): pass # E: Cannot extend enum with existing members: "A" + +class A1(Enum): + class __Inner: pass +class B1(A1): pass [builtins fixtures/bool.pyi] [case testEnumFinalSpecialProps] @@ -1761,10 +1953,12 @@ class A(Enum): x: int def method(self) -> int: pass class B(A): - x = 1 # E: Cannot override writable attribute "x" with a final one + x = 1 # E: Cannot override writable attribute "x" with a final one \ + # E: Incompatible types in assignment (expression has type "B", base class "A" defined the type as "int") 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 @@ -1778,7 +1972,8 @@ class B2(A2): # E: Cannot extend enum with existing members: "A2" 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") + x = 1 # E: Cannot override final attribute "x" (previously declared in base class "A3") \ + # E: Incompatible types in assignment (expression has type "B3", base class "A3" defined the type as "int") [builtins fixtures/bool.pyi] [case testEnumNotFinalWithMethodsAndUninitializedValuesStub] @@ -1786,15 +1981,32 @@ 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 B(A): + x = 1 # E: Cannot override writable attribute "x" with a final one \ + # E: Incompatible types in assignment (expression has type "B", base class "A" defined the type as "int") class C(Enum): x = 1 -class D(C): # E: Cannot extend enum with existing members: "C" - x: int # E: Cannot assign to final name "x" +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: Incompatible types in assignment (expression has type "int", base class "C" defined the type as "C") \ + # E: Cannot assign to final name "x" +[builtins fixtures/bool.pyi] + +[case testEnumNotFinalWithMethodsAndUninitializedValuesStubMember] +# flags: --python-version 3.11 +# This was added in 3.11 +import lib + +[file lib.pyi] +from enum import Enum, member +class A(Enum): + @member + def x(self) -> None: ... [builtins fixtures/bool.pyi] [case testEnumLiteralValues] @@ -1809,7 +2021,7 @@ class A(Enum): reveal_type(A.str.value) # N: Revealed type is "Literal['foo']?" reveal_type(A.int.value) # N: Revealed type is "Literal[1]?" reveal_type(A.bool.value) # N: Revealed type is "Literal[False]?" -reveal_type(A.tuple.value) # N: Revealed type is "Tuple[Literal[1]?]" +reveal_type(A.tuple.value) # N: Revealed type is "tuple[Literal[1]?]" [builtins fixtures/tuple.pyi] [case testFinalWithPrivateAssignment] @@ -2029,8 +2241,8 @@ from enum import Enum class C(Enum): _ignore_ = 'X' -C._ignore_ # E: "Type[C]" has no attribute "_ignore_" -[typing fixtures/typing-medium.pyi] +C._ignore_ # E: "type[C]" has no attribute "_ignore_" +[builtins fixtures/enum.pyi] [case testCanOverrideDunderAttributes] import typing @@ -2087,7 +2299,7 @@ class A(Some, Enum): from enum import Enum class Mixed(Enum): - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") b = None def check(self) -> None: @@ -2095,7 +2307,7 @@ class Mixed(Enum): reveal_type(Mixed.b.value) # N: Revealed type is "None" # Inferring Any here instead of a union seems to be a deliberate - # choice; see the testEnumValueInhomogenous case above. + # choice; see the testEnumValueInhomogeneous case above. reveal_type(self.value) # N: Revealed type is "Any" for field in Mixed: @@ -2104,11 +2316,12 @@ class Mixed(Enum): pass class AllPartialList(Enum): - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") - b = [] # E: Need type annotation for "b" (hint: "b: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") + b = [] # E: Need type annotation for "b" (hint: "b: list[] = ...") 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 @@ -2119,7 +2332,8 @@ class MyEnum(Enum): __my_dict = {A: "ham", B: "spam"} # 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") +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 +2399,285 @@ 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 testEnumMemberAndNonMemberSupport] +# flags: --python-version 3.11 --warn-unreachable +# This was added in 3.11 +from enum import Enum, member, nonmember + +class A(Enum): + x = 1 + y = member(2) + z = nonmember(3) + +def some_a(a: A): + if a is not A.x and a is not A.z: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.y]" + if a is not A.y and a is not A.z: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.x]" + if a is not A.x: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.y]" + if a is not A.y: + reveal_type(a) # N: Revealed type is "Literal[__main__.A.x]" +[builtins fixtures/dict.pyi] + +[case testEnumAccessFromInstance] +# flags: --python-version 3.11 --warn-unreachable +# This was added in 3.11 +from enum import Enum, member, nonmember + +class A(Enum): + x = 1 + y = member(2) + z = nonmember(3) + +reveal_type(A.x) # N: Revealed type is "Literal[__main__.A.x]?" +reveal_type(A.y) # N: Revealed type is "Literal[__main__.A.y]?" +reveal_type(A.z) # N: Revealed type is "builtins.int" + +reveal_type(A.x.x) # N: Revealed type is "Literal[__main__.A.x]?" +reveal_type(A.x.x.x) # N: Revealed type is "Literal[__main__.A.x]?" +reveal_type(A.x.y) # N: Revealed type is "Literal[__main__.A.y]?" +reveal_type(A.x.y.y) # N: Revealed type is "Literal[__main__.A.y]?" +reveal_type(A.x.z) # N: Revealed type is "builtins.int" + +reveal_type(A.y.x) # N: Revealed type is "Literal[__main__.A.x]?" +reveal_type(A.y.y) # N: Revealed type is "Literal[__main__.A.y]?" +reveal_type(A.y.z) # N: Revealed type is "builtins.int" + +A.z.x # E: "int" has no attribute "x" + +class B(Enum): + x = 1 + value = 2 + +reveal_type(B.x) # N: Revealed type is "Literal[__main__.B.x]?" +reveal_type(B.x.value) # N: Revealed type is "Literal[2]?" +reveal_type(B.x.x.value) # N: Revealed type is "Literal[2]?" +B.x.value.value # E: "int" has no attribute "value" +B.x.value.value.value # E: "int" has no attribute "value" +reveal_type(B.value) # N: Revealed type is "Literal[__main__.B.value]?" +reveal_type(B.value.x) # N: Revealed type is "Literal[__main__.B.x]?" +reveal_type(B.value.x.x) # N: Revealed type is "Literal[__main__.B.x]?" +reveal_type(B.value.x.value) # N: Revealed type is "Literal[2]?" +B.value.x.value.value # E: "int" has no attribute "value" +B.value.value.value # E: "int" has no attribute "value" +[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] + +[case testStrEnumClassCorrectIterable] +from enum import StrEnum +from typing import Type, TypeVar + +class Choices(StrEnum): + LOREM = "lorem" + IPSUM = "ipsum" + +var = list(Choices) +reveal_type(var) # N: Revealed type is "builtins.list[__main__.Choices]" + +e: type[StrEnum] +reveal_type(list(e)) # N: Revealed type is "builtins.list[enum.StrEnum]" + +T = TypeVar("T", bound=StrEnum) +def list_vals(e: Type[T]) -> list[T]: + reveal_type(list(e)) # N: Revealed type is "builtins.list[T`-1]" + return list(e) + +reveal_type(list_vals(Choices)) # N: Revealed type is "builtins.list[__main__.Choices]" +[builtins fixtures/enum.pyi] + +[case testEnumAsClassMemberNoCrash] +# https://github.com/python/mypy/issues/18736 +from enum import Enum + +class Base: + def __init__(self, namespace: tuple[str, ...]) -> None: + # Not a bug: trigger defer + names = [name for name in namespace if fail] # E: Name "fail" is not defined + self.o = Enum("o", names) # E: Enum type as attribute is not supported \ + # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members +[builtins fixtures/tuple.pyi] + +[case testSingleUnderscoreNameEnumMember] +# flags: --warn-unreachable + +# https://github.com/python/mypy/issues/19271 +from enum import Enum + +class Things(Enum): + _ = "under score" + +def check(thing: Things) -> None: + if thing is Things._: + return None + return None # E: Statement is unreachable +[builtins fixtures/enum.pyi] + +[case testSunderValueTypeEllipsis] +from foo.bar import ( + Basic, FromStub, InheritedInt, InheritedStr, InheritedFlag, + InheritedIntFlag, Wrapper +) + +reveal_type(Basic.FOO) # N: Revealed type is "Literal[foo.bar.Basic.FOO]?" +reveal_type(Basic.FOO.value) # N: Revealed type is "Literal[1]?" +reveal_type(Basic.FOO._value_) # N: Revealed type is "builtins.int" + +reveal_type(FromStub.FOO) # N: Revealed type is "Literal[foo.bar.FromStub.FOO]?" +reveal_type(FromStub.FOO.value) # N: Revealed type is "builtins.int" +reveal_type(FromStub.FOO._value_) # N: Revealed type is "builtins.int" + +reveal_type(Wrapper.Nested.FOO) # N: Revealed type is "Literal[foo.bar.Wrapper.Nested.FOO]?" +reveal_type(Wrapper.Nested.FOO.value) # N: Revealed type is "builtins.int" +reveal_type(Wrapper.Nested.FOO._value_) # N: Revealed type is "builtins.int" + +reveal_type(InheritedInt.FOO) # N: Revealed type is "Literal[foo.bar.InheritedInt.FOO]?" +reveal_type(InheritedInt.FOO.value) # N: Revealed type is "builtins.int" +reveal_type(InheritedInt.FOO._value_) # N: Revealed type is "builtins.int" + +reveal_type(InheritedStr.FOO) # N: Revealed type is "Literal[foo.bar.InheritedStr.FOO]?" +reveal_type(InheritedStr.FOO.value) # N: Revealed type is "builtins.str" +reveal_type(InheritedStr.FOO._value_) # N: Revealed type is "builtins.str" + +reveal_type(InheritedFlag.FOO) # N: Revealed type is "Literal[foo.bar.InheritedFlag.FOO]?" +reveal_type(InheritedFlag.FOO.value) # N: Revealed type is "builtins.int" +reveal_type(InheritedFlag.FOO._value_) # N: Revealed type is "builtins.int" + +reveal_type(InheritedIntFlag.FOO) # N: Revealed type is "Literal[foo.bar.InheritedIntFlag.FOO]?" +reveal_type(InheritedIntFlag.FOO.value) # N: Revealed type is "builtins.int" +reveal_type(InheritedIntFlag.FOO._value_) # N: Revealed type is "builtins.int" + +[file foo/__init__.pyi] +[file foo/bar/__init__.pyi] +from enum import Enum, IntEnum, StrEnum, Flag, IntFlag + +class Basic(Enum): + _value_: int + FOO = 1 + +class FromStub(Enum): + _value_: int + FOO = ... + +class Wrapper: + class Nested(Enum): + _value_: int + FOO = ... + +class InheritedInt(IntEnum): + FOO = ... + +class InheritedStr(StrEnum): + FOO = ... + +class InheritedFlag(Flag): + FOO = ... + +class InheritedIntFlag(IntFlag): + FOO = ... +[builtins fixtures/enum.pyi] + +[case testSunderValueTypeEllipsisNonStub] +from enum import Enum, StrEnum + +class Basic(Enum): + _value_: int + FOO = 1 + +reveal_type(Basic.FOO) # N: Revealed type is "Literal[__main__.Basic.FOO]?" +reveal_type(Basic.FOO.value) # N: Revealed type is "Literal[1]?" +reveal_type(Basic.FOO._value_) # N: Revealed type is "builtins.int" + +# TODO: this and below should produce diagnostics, Ellipsis is not assignable to int +# Now we do not check members against _value_ at all. + +class FromStub(Enum): + _value_: int + FOO = ... + +reveal_type(FromStub.FOO) # N: Revealed type is "Literal[__main__.FromStub.FOO]?" +reveal_type(FromStub.FOO.value) # N: Revealed type is "builtins.ellipsis" +reveal_type(FromStub.FOO._value_) # N: Revealed type is "builtins.int" + +class InheritedStr(StrEnum): + FOO = ... + +reveal_type(InheritedStr.FOO) # N: Revealed type is "Literal[__main__.InheritedStr.FOO]?" +reveal_type(InheritedStr.FOO.value) # N: Revealed type is "builtins.ellipsis" +reveal_type(InheritedStr.FOO._value_) # N: Revealed type is "builtins.ellipsis" + +class Wrapper: + class Nested(StrEnum): + FOO = ... + +reveal_type(Wrapper.Nested.FOO) # N: Revealed type is "Literal[__main__.Wrapper.Nested.FOO]?" +reveal_type(Wrapper.Nested.FOO.value) # N: Revealed type is "builtins.ellipsis" +reveal_type(Wrapper.Nested.FOO._value_) # N: Revealed type is "builtins.ellipsis" +[builtins fixtures/enum.pyi] diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 10cc145d0c70b..06c5753db5a74 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -33,9 +33,9 @@ reveal_type(1) # N: Revealed type is "Literal[1]?" [case testErrorCodeSyntaxError] 1 '' [out] -main:1: error: invalid syntax [syntax] +main:1: error: Invalid syntax [syntax] [out version==3.10.0] -main:1: error: invalid syntax. Perhaps you forgot a comma? [syntax] +main:1: error: Invalid syntax. Perhaps you forgot a comma? [syntax] [case testErrorCodeSyntaxError2] def f(): # E: Type signature has too many arguments [syntax] @@ -105,6 +105,19 @@ x # type: ignore[name-defined, attr-defined] # E: Unused "type: ignore[attr-defi # flags: --warn-unused-ignores "x" # type: ignore[name-defined] # E: Unused "type: ignore" comment [unused-ignore] +[case testErrorCodeWarnUnusedIgnores7_WarnWhenErrorCodeDisabled] +# flags: --warn-unused-ignores --disable-error-code name-defined +x # type: ignore # E: Unused "type: ignore" comment [unused-ignore] +x # type: ignore[name-defined] # E: Unused "type: ignore" comment [unused-ignore] +"x".foobar(y) # type: ignore[name-defined, attr-defined] # E: Unused "type: ignore[name-defined]" comment [unused-ignore] + +[case testErrorCodeWarnUnusedIgnores8_IgnoreUnusedIgnore] +# flags: --warn-unused-ignores --disable-error-code name-defined +"x" # type: ignore[unused-ignore] +"x" # type: ignore[name-defined, unused-ignore] +"x" # type: ignore[xyz, unused-ignore] +x # type: ignore[name-defined, unused-ignore] + [case testErrorCodeMissingWhenRequired] # flags: --enable-error-code ignore-without-code "x" # type: ignore # E: "type: ignore" comment without error code [ignore-without-code] @@ -259,7 +272,7 @@ from typing import TypeVar T = TypeVar('T') def f() -> T: pass # E: A function returning TypeVar should receive at least one argument containing the same TypeVar [type-var] x = f() # E: Need type annotation for "x" [var-annotated] -y = [] # E: Need type annotation for "y" (hint: "y: List[] = ...") [var-annotated] +y = [] # E: Need type annotation for "y" (hint: "y: list[] = ...") [var-annotated] [builtins fixtures/list.pyi] [case testErrorCodeBadOverride] @@ -331,7 +344,7 @@ a.x = '' # E: Incompatible types in assignment (expression has type "str", vari # flags: --disallow-any-generics from typing import List, TypeVar x: List # E: Missing type parameters for generic type "List" [type-arg] -y: list # E: Implicit generic "Any". Use "typing.List" and specify generic parameters [type-arg] +y: list # E: Missing type parameters for generic type "list" [type-arg] T = TypeVar('T') L = List[List[T]] z: L # E: Missing type parameters for generic type "L" [type-arg] @@ -381,10 +394,25 @@ def f() -> None: def g(): pass +[case testErrorCodeUntypedDecorator] +# flags: --disallow-untyped-decorators --warn-unused-ignores +def d(f): return f + +@d # E: Untyped decorator makes function "x" untyped [untyped-decorator] +def x() -> int: return 1 +@d # type: ignore +def y() -> int: return 2 +@d # type: ignore[untyped-decorator] +def best() -> int: return 3 +@d # type: ignore[misc] # E: Unused "type: ignore" comment [unused-ignore] \ + # E: Untyped decorator makes function "z" untyped [untyped-decorator] \ + # N: Error code "untyped-decorator" not covered by "type: ignore" comment +def z() -> int: return 4 + [case testErrorCodeIndexing] from typing import Dict x: Dict[int, int] -x[''] # E: Invalid index type "str" for "Dict[int, int]"; expected type "int" [index] +x[''] # E: Invalid index type "str" for "dict[int, int]"; expected type "int" [index] 1[''] # E: Value of type "int" is not indexable [index] 1[''] = 1 # E: Unsupported target for indexed assignment ("int") [index] [builtins fixtures/dict.pyi] @@ -449,7 +477,7 @@ y: Dict[int, int] = {1: ''} # E: Dict entry 0 has incompatible type "int": "str [builtins fixtures/dict.pyi] [case testErrorCodeTypedDict] -from typing_extensions import TypedDict +from typing import TypedDict class D(TypedDict): x: int class E(TypedDict): @@ -472,7 +500,7 @@ a['y'] # E: TypedDict "D" has no key "y" [typeddict-item] [typing fixtures/typing-typeddict.pyi] [case testErrorCodeTypedDictNoteIgnore] -from typing_extensions import TypedDict +from typing import TypedDict class A(TypedDict): one_commonpart: int two_commonparts: int @@ -484,7 +512,7 @@ not_exist = a['not_exist'] # type: ignore[typeddict-item] [typing fixtures/typing-typeddict.pyi] [case testErrorCodeTypedDictSubCodeIgnore] -from typing_extensions import TypedDict +from typing import TypedDict class D(TypedDict): x: int d: D = {'x': 1, 'y': 2} # type: ignore[typeddict-item] @@ -522,13 +550,16 @@ if int() is str(): # E: Non-overlapping identity check (left operand type: "int [builtins fixtures/primitives.pyi] [case testErrorCodeMissingModule] -from defusedxml import xyz # E: Cannot find implementation or library stub for module named "defusedxml" [import-not-found] +from defusedxml import xyz # E: Library stubs not installed for "defusedxml" [import-untyped] \ + # N: Hint: "python3 -m pip install types-defusedxml" \ + # N: (or run "mypy --install-types" to install all missing stub packages) from nonexistent import foobar # E: Cannot find implementation or library stub for module named "nonexistent" [import-not-found] import nonexistent2 # E: Cannot find implementation or library stub for module named "nonexistent2" [import-not-found] from nonexistent3 import * # E: Cannot find implementation or library stub for module named "nonexistent3" [import-not-found] from pkg import bad # E: Module "pkg" has no attribute "bad" [attr-defined] from pkg.bad2 import bad3 # E: Cannot find implementation or library stub for module named "pkg.bad2" [import-not-found] \ # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports + [file pkg/__init__.py] [case testErrorCodeAlreadyDefined] @@ -649,7 +680,7 @@ def g() -> int: x: List[int] # type: ignore[name-defined] [case testErrorCodeProtocolProblemsIgnore] -from typing_extensions import Protocol +from typing import Protocol class P(Protocol): def f(self, x: str) -> None: ... @@ -726,7 +757,7 @@ main:2: error: Syntax error in type comment "int" [syntax] [case testErrorCode__exit__Return] class InvalidReturn: def __exit__(self, x, y, z) -> bool: # E: "bool" is invalid as return type for "__exit__" that always returns False [exit-return] \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions return False [builtins fixtures/bool.pyi] @@ -828,13 +859,14 @@ Foo = NamedTuple("Bar", []) # E: First argument to namedtuple() should be "Foo" [builtins fixtures/tuple.pyi] [case testTypedDictNameMismatch] -from typing_extensions import TypedDict +from typing import TypedDict Foo = TypedDict("Bar", {}) # E: First argument "Bar" to TypedDict() does not match variable name "Foo" [name-match] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTruthyBool] -# flags: --enable-error-code truthy-bool +# flags: --enable-error-code truthy-bool --no-local-partial-types from typing import List, Union, Any class Foo: @@ -953,22 +985,21 @@ def f(arg: int) -> int: def f(arg: str) -> str: ... -[case testSliceInDict39] -# flags: --python-version 3.9 --show-column-numbers -from typing import Dict -b: Dict[int, x:y] -c: Dict[x:y] +[case testSliceInDictBuiltin] +# flags: --show-column-numbers +b: dict[int, x:y] +c: dict[x:y] [builtins fixtures/dict.pyi] [out] -main:3:14: error: Invalid type comment or annotation [valid-type] -main:3:14: note: did you mean to use ',' instead of ':' ? -main:4:4: error: "dict" expects 2 type arguments, but 1 given [type-arg] -main:4:9: error: Invalid type comment or annotation [valid-type] -main:4:9: note: did you mean to use ',' instead of ':' ? - -[case testSliceInDict38] -# flags: --python-version 3.8 --show-column-numbers +main:2:14: error: Invalid type comment or annotation [valid-type] +main:2:14: note: did you mean to use ',' instead of ':' ? +main:3:4: error: "dict" expects 2 type arguments, but 1 given [type-arg] +main:3:9: error: Invalid type comment or annotation [valid-type] +main:3:9: note: did you mean to use ',' instead of ':' ? + +[case testSliceInDictTyping] +# flags: --show-column-numbers from typing import Dict b: Dict[int, x:y] c: Dict[x:y] @@ -990,7 +1021,7 @@ reveal_type(t) # N: Revealed type is "__main__.TensorType" [builtins fixtures/tuple.pyi] [case testNoteAboutChangedTypedDictErrorCode] -from typing_extensions import TypedDict +from typing import TypedDict class D(TypedDict): x: int @@ -1055,12 +1086,12 @@ class C(abc.ABC): T = TypeVar("T") def test(tp: Type[T]) -> T: ... -test(C) # E: Only concrete class can be given where "Type[C]" is expected [type-abstract] +test(C) # E: Only concrete class can be given where "type[C]" is expected [type-abstract] class D(C): @abc.abstractmethod def bar(self) -> None: ... -cls: Type[C] = D # E: Can only assign concrete classes to a variable of type "Type[C]" [type-abstract] +cls: Type[C] = D # E: Can only assign concrete classes to a variable of type "type[C]" [type-abstract] [case testUncheckedAnnotationCodeShown] def f(): @@ -1223,6 +1254,47 @@ def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of inpu [builtins fixtures/tuple.pyi] +[case testDynamicMetaclass] +class A(metaclass=type(tuple)): pass # E: Dynamic metaclass not supported for "A" [metaclass] +[builtins fixtures/tuple.pyi] + +[case testMetaclassOfTypeAny] +# mypy: disallow-subclassing-any=True +from typing import Any +foo: Any = ... +class A(metaclass=foo): pass # E: Class cannot use "foo" as a metaclass (has type "Any") [metaclass] + +[case testMetaclassOfWrongType] +class Foo: + bar = 1 +class A2(metaclass=Foo.bar): pass # E: Invalid metaclass "Foo.bar" [metaclass] + +[case testMetaclassNotTypeSubclass] +class M: pass +class A(metaclass=M): pass # E: Metaclasses not inheriting from "type" are not supported [metaclass] + +[case testMultipleMetaclasses] +import six +class M1(type): pass + +@six.add_metaclass(M1) +class A1(metaclass=M1): pass # E: Multiple metaclass definitions [metaclass] + +class A2(six.with_metaclass(M1), metaclass=M1): pass # E: Multiple metaclass definitions [metaclass] + +@six.add_metaclass(M1) +class A3(six.with_metaclass(M1)): pass # E: Multiple metaclass definitions [metaclass] +[builtins fixtures/tuple.pyi] + +[case testInvalidMetaclassStructure] +class X(type): pass +class Y(type): pass +class A(metaclass=X): pass +class B(A, metaclass=Y): pass # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [metaclass] \ + # N: "__main__.Y" (metaclass of "__main__.B") conflicts with "__main__.X" (metaclass of "__main__.A") + + + [case testOverloadedFunctionSignature] from typing import overload, Union diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index d5ddc910bcd63..ea6eac9a39b3a 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -750,17 +750,17 @@ i = 8 f = 8.0 d = Decimal(8) -reveal_type(divmod(i, i)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" -reveal_type(divmod(f, i)) # N: Revealed type is "Tuple[builtins.float, builtins.float]" -reveal_type(divmod(d, i)) # N: Revealed type is "Tuple[__main__.Decimal, __main__.Decimal]" +reveal_type(divmod(i, i)) # N: Revealed type is "tuple[builtins.int, builtins.int]" +reveal_type(divmod(f, i)) # N: Revealed type is "tuple[builtins.float, builtins.float]" +reveal_type(divmod(d, i)) # N: Revealed type is "tuple[__main__.Decimal, __main__.Decimal]" -reveal_type(divmod(i, f)) # N: Revealed type is "Tuple[builtins.float, builtins.float]" -reveal_type(divmod(f, f)) # N: Revealed type is "Tuple[builtins.float, builtins.float]" +reveal_type(divmod(i, f)) # N: Revealed type is "tuple[builtins.float, builtins.float]" +reveal_type(divmod(f, f)) # N: Revealed type is "tuple[builtins.float, builtins.float]" divmod(d, f) # E: Unsupported operand types for divmod ("Decimal" and "float") -reveal_type(divmod(i, d)) # N: Revealed type is "Tuple[__main__.Decimal, __main__.Decimal]" +reveal_type(divmod(i, d)) # N: Revealed type is "tuple[__main__.Decimal, __main__.Decimal]" divmod(f, d) # E: Unsupported operand types for divmod ("float" and "Decimal") -reveal_type(divmod(d, d)) # N: Revealed type is "Tuple[__main__.Decimal, __main__.Decimal]" +reveal_type(divmod(d, d)) # N: Revealed type is "tuple[__main__.Decimal, __main__.Decimal]" # Now some bad calls divmod() # E: "divmod" expects 2 arguments \ @@ -985,8 +985,7 @@ main:4: error: "A" not callable -- assert_type() [case testAssertType] -from typing import assert_type, Any -from typing_extensions import Literal +from typing import assert_type, Any, Literal a: int = 1 returned = assert_type(a, int) reveal_type(returned) # N: Revealed type is "builtins.int" @@ -998,8 +997,7 @@ assert_type(42, int) # E: Expression is of type "Literal[42]", not "int" [builtins fixtures/tuple.pyi] [case testAssertTypeGeneric] -from typing import assert_type, TypeVar, Generic -from typing_extensions import Literal +from typing import assert_type, Literal, TypeVar, Generic T = TypeVar("T") def f(x: T) -> T: return x assert_type(f(1), int) @@ -1012,25 +1010,23 @@ y: Gen[Literal[1]] = assert_type(Gen(1), Gen[Literal[1]]) [builtins fixtures/tuple.pyi] [case testAssertTypeUncheckedFunction] -from typing import assert_type -from typing_extensions import Literal +from typing import Literal, assert_type def f(): x = 42 assert_type(x, Literal[42]) [out] -main:5: error: Expression is of type "Any", not "Literal[42]" -main:5: note: "assert_type" expects everything to be "Any" in unchecked functions +main:4: error: Expression is of type "Any", not "Literal[42]" +main:4: note: "assert_type" expects everything to be "Any" in unchecked functions [builtins fixtures/tuple.pyi] [case testAssertTypeUncheckedFunctionWithUntypedCheck] # flags: --check-untyped-defs -from typing import assert_type -from typing_extensions import Literal +from typing import Literal, assert_type def f(): x = 42 assert_type(x, Literal[42]) [out] -main:6: error: Expression is of type "int", not "Literal[42]" +main:5: error: Expression is of type "int", not "Literal[42]" [builtins fixtures/tuple.pyi] [case testAssertTypeNoPromoteUnion] @@ -1178,8 +1174,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] @@ -1382,7 +1378,7 @@ class B: pass [out] main:5: error: Key expression in dictionary comprehension has incompatible type "A"; expected type "B" main:5: error: Value expression in dictionary comprehension has incompatible type "B"; expected type "A" -main:6: error: Incompatible types in assignment (expression has type "Dict[A, B]", variable has type "A") +main:6: error: Incompatible types in assignment (expression has type "dict[A, B]", variable has type "A") [case testDictionaryComprehensionWithNonDirectMapping] @@ -1506,6 +1502,13 @@ x.append(y) if bool() else x.append(y) z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value (it only ever returns None) [builtins fixtures/list.pyi] +[case testConditionalExpressionWithUnreachableBranches] +from typing import TypeVar +T = TypeVar("T", int, str) +def foo(x: T) -> T: + return x + 1 if isinstance(x, int) else x + "a" +[builtins fixtures/isinstancelist.pyi] + -- Special cases -- ------------- @@ -1658,13 +1661,13 @@ d1 = dict(a=1, b=2) # type: Dict[str, int] d2 = dict(a=1, b='') # type: Dict[str, int] # E: Dict entry 1 has incompatible type "str": "str"; expected "str": "int" d3 = dict(a=1) # type: Dict[int, int] # E: Dict entry 0 has incompatible type "str": "int"; expected "int": "int" d4 = dict(a=1, b=1) -d4.xyz # E: "Dict[str, int]" has no attribute "xyz" +d4.xyz # E: "dict[str, int]" has no attribute "xyz" d5 = dict(a=1, b='') # type: Dict[str, Any] [builtins fixtures/dict.pyi] [case testDictWithoutKeywordArgs] from typing import Dict -d = dict() # E: Need type annotation for "d" (hint: "d: Dict[, ] = ...") +d = dict() # E: Need type annotation for "d" (hint: "d: dict[, ] = ...") d2 = dict() # type: Dict[int, str] dict(undefined) # E: Name "undefined" is not defined [builtins fixtures/dict.pyi] @@ -1672,8 +1675,8 @@ dict(undefined) # E: Name "undefined" is not defined [case testDictFromList] from typing import Dict d = dict([(1, 'x'), (2, 'y')]) -d() # E: "Dict[int, str]" not callable -d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "Tuple[int, str]"; expected "Tuple[str, str]" +d() # E: "dict[int, str]" not callable +d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "tuple[int, str]"; expected "tuple[str, str]" [builtins fixtures/dict.pyi] [case testDictFromIterableAndKeywordArg] @@ -1681,10 +1684,10 @@ from typing import Dict it = [('x', 1)] d = dict(it, x=1) -d() # E: "Dict[str, int]" not callable +d() # E: "dict[str, int]" not callable d2 = dict(it, x='') -d2() # E: "Dict[str, object]" not callable +d2() # E: "dict[str, object]" not callable d3 = dict(it, x='') # type: Dict[str, int] # E: Argument "x" to "dict" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] @@ -1696,7 +1699,7 @@ dict(it, x='y') # E: Keyword argument only valid with "str" key type in call to [case testDictFromIterableAndKeywordArg3] d = dict([], x=1) -d() # E: "Dict[str, int]" not callable +d() # E: "dict[str, int]" not callable [builtins fixtures/dict.pyi] [case testDictFromIterableAndStarStarArgs] @@ -1705,20 +1708,20 @@ it = [('x', 1)] kw = {'x': 1} d = dict(it, **kw) -d() # E: "Dict[str, int]" not callable +d() # E: "dict[str, int]" not callable kw2 = {'x': ''} d2 = dict(it, **kw2) -d2() # E: "Dict[str, object]" not callable +d2() # E: "dict[str, object]" not callable -d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "**Dict[str, str]"; expected "int" +d3 = dict(it, **kw2) # type: Dict[str, int] # E: Argument 2 to "dict" has incompatible type "**dict[str, str]"; expected "int" [builtins fixtures/dict.pyi] [case testDictFromIterableAndStarStarArgs2] it = [(1, 'x')] kw = {'x': 'y'} d = dict(it, **kw) # E: Keyword argument only valid with "str" key type in call to "dict" -d() # E: "Dict[int, str]" not callable +d() # E: "dict[int, str]" not callable [builtins fixtures/dict.pyi] [case testUserDefinedClassNamedDict] @@ -1858,7 +1861,7 @@ None < None # E: Unsupported left operand type for < ("None") [case testDictWithStarExpr] -b = {'z': 26, *a} # E: invalid syntax +b = {'z': 26, *a} # E: Invalid syntax [builtins fixtures/dict.pyi] [case testDictWithStarStarExpr] @@ -1875,9 +1878,9 @@ a = {'a': 1} b = {'z': 26, **a} c = {**b} d = {**a, **b, 'c': 3} -e = {1: 'a', **a} # E: Cannot infer type argument 1 of \ +e = {1: 'a', **a} # E: Cannot infer value of type parameter "KT" of \ # N: Try assigning the literal to a variable annotated as dict[, ] -f = {**b} # type: Dict[int, int] # E: Unpacked dict entry 0 has incompatible type "Dict[str, int]"; expected "SupportsKeysAndGetItem[int, int]" +f = {**b} # type: Dict[int, int] # E: Unpacked dict entry 0 has incompatible type "dict[str, int]"; expected "SupportsKeysAndGetItem[int, int]" g = {**Thing()} h = {**a, **Thing()} i = {**Thing()} # type: Dict[int, int] # E: Unpacked dict entry 0 has incompatible type "Thing"; expected "SupportsKeysAndGetItem[int, int]" \ @@ -1890,7 +1893,7 @@ i = {**Thing()} # type: Dict[int, int] # E: Unpacked dict entry 0 has incompat # N: def keys(self) -> Iterable[int] \ # N: Got: \ # N: def keys(self) -> Iterable[str] -j = {1: 'a', **Thing()} # E: Cannot infer type argument 1 of \ +j = {1: 'a', **Thing()} # E: Cannot infer value of type parameter "KT" of \ # N: Try assigning the literal to a variable annotated as dict[, ] [builtins fixtures/dict.pyi] [typing fixtures/typing-medium.pyi] @@ -1935,8 +1938,8 @@ class B: ... [builtins fixtures/dict.pyi] [case testTypeAnnotationNeededMultipleAssignment] -x, y = [], [] # E: Need type annotation for "x" (hint: "x: List[] = ...") \ - # E: Need type annotation for "y" (hint: "y: List[] = ...") +x, y = [], [] # E: Need type annotation for "x" (hint: "x: list[] = ...") \ + # E: Need type annotation for "y" (hint: "y: list[] = ...") [builtins fixtures/list.pyi] [case testStrictEqualityEq] @@ -2166,9 +2169,17 @@ class CustomMeta(type): class Normal: ... class Custom(metaclass=CustomMeta): ... -Normal == int() # E: Non-overlapping equality check (left operand type: "Type[Normal]", right operand type: "int") +Normal == int() # E: Non-overlapping equality check (left operand type: "type[Normal]", right operand type: "int") Normal == Normal Custom == int() + +n: type[Normal] = Normal +c: type[Custom] = Custom + +n == int() # E: Non-overlapping equality check (left operand type: "type[Normal]", right operand type: "int") +n == n +c == int() + [builtins fixtures/bool.pyi] [case testCustomContainsCheckStrictEquality] @@ -2191,7 +2202,7 @@ class Bad: ... subclasses: List[Type[C]] object in subclasses D in subclasses -Bad in subclasses # E: Non-overlapping container check (element type: "Type[Bad]", container item type: "Type[C]") +Bad in subclasses # E: Non-overlapping container check (element type: "type[Bad]", container item type: "type[C]") [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] @@ -2213,7 +2224,7 @@ exp: List[Meta] A in exp B in exp -C in exp # E: Non-overlapping container check (element type: "Type[C]", container item type: "Meta") +C in exp # E: Non-overlapping container check (element type: "type[C]", container item type: "Meta") o in exp a in exp @@ -2276,7 +2287,7 @@ def f(x: T) -> T: [case testStrictEqualityWithALiteral] # flags: --strict-equality -from typing_extensions import Literal, Final +from typing import Final, Literal def returns_a_or_b() -> Literal['a', 'b']: ... @@ -2388,7 +2399,7 @@ assert a == b R2 = Dict[int, R2] c: R2 -assert a == c # E: Non-overlapping equality check (left operand type: "Dict[str, R]", right operand type: "Dict[int, R2]") +assert a == c # E: Non-overlapping equality check (left operand type: "dict[str, R]", right operand type: "dict[int, R2]") [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] @@ -2408,6 +2419,35 @@ assert a == c [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] +[case testStrictEqualityForNone] +# flags: --strict-equality --strict-equality-for-none + +class A: ... + +def a1(x: A) -> None: + assert x is None # E: Non-overlapping identity check (left operand type: "A", right operand type: "None") +def a2(x: A) -> None: + x is not None # E: Non-overlapping identity check (left operand type: "A", right operand type: "None") +def a3(x: A) -> None: + None == x # E: Non-overlapping equality check (left operand type: "None", right operand type: "A") +def a4(x: list[A]) -> None: + None in x # E: Non-overlapping container check (element type: "None", container item type: "A") + +class B: + def __eq__(self, x: object) -> bool: ... + +def b1(x: B) -> None: + assert x is None # E: Non-overlapping identity check (left operand type: "B", right operand type: "None") +def b2(x: B) -> None: + x is not None # E: Non-overlapping identity check (left operand type: "B", right operand type: "None") +def b3(x: B) -> None: + x == None +def b4(x: list[B]) -> None: + None in x + +[builtins fixtures/list.pyi] +[typing fixtures/typing-full.pyi] + [case testUnimportedHintAny] def f(x: Any) -> None: # E: Name "Any" is not defined \ # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any") diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test index 534967b1edbf1..e7417d049442e 100644 --- a/test-data/unit/check-fastparse.test +++ b/test-data/unit/check-fastparse.test @@ -1,6 +1,6 @@ [case testFastParseSyntaxError] -1 + # E: invalid syntax +1 + # E: Invalid syntax [case testFastParseTypeCommentSyntaxError] @@ -158,7 +158,7 @@ def f(a, # type: A [case testFastParsePerArgumentAnnotationsWithAnnotatedBareStar] -def f(*, # type: int # E: bare * has associated type comment +def f(*, # type: int # E: Bare * has associated type comment x # type: str ): # type: (...) -> int @@ -241,37 +241,37 @@ assert 1, f() # E: Name "f" is not defined [case testFastParserConsistentFunctionTypes] -def f(x, y, z): +def f1(x, y, z): # type: (int, int, int) -> int pass -def f(x, # type: int # E: Function has duplicate type signatures +def f2(x, # type: int # E: Function has duplicate type signatures y, # type: int z # type: int ): # type: (int, int, int) -> int pass -def f(x, # type: int +def f3(x, # type: int y, # type: int z # type: int ): # type: (...) -> int pass -def f(x, y, z): +def f4(x, y, z): # type: (int, int, int) -> int pass -def f(x) -> int: # E: Function has duplicate type signatures +def f5(x) -> int: # E: Function has duplicate type signatures # type: (int) -> int pass -def f(x: int, y: int, z: int): +def f6(x: int, y: int, z: int): # type: (...) -> int pass -def f(x: int): # E: Function has duplicate type signatures +def f7(x: int): # E: Function has duplicate type signatures # type: (int) -> int pass @@ -324,3 +324,18 @@ class Bla: def call() -> str: pass [builtins fixtures/module.pyi] + +[case testInvalidEscapeSequenceWarningsSuppressed] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# Test that SyntaxWarnings for invalid escape sequences are suppressed +# when parsing potential type expressions containing regex patterns or +# similar strings. Callable arguments are always potential type expressions. +from typing import TypeForm + +def identity(typx: TypeForm) -> TypeForm: + return typx + +# This should not generate SyntaxWarning despite invalid escape sequence +identity(r"re\.match") # E: Argument 1 to "identity" has incompatible type "str"; expected "TypeForm[Any]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 763183159e94f..e3fc4614fc069 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -41,7 +41,7 @@ class C: def __init__(self, x: Tuple[int, Any]) -> None: self.x: Final = x self.y: Final[float] = 1 -reveal_type(C((1, 2)).x) # N: Revealed type is "Tuple[builtins.int, Any]" +reveal_type(C((1, 2)).x) # N: Revealed type is "tuple[builtins.int, Any]" reveal_type(C((1, 2)).y) # N: Revealed type is "builtins.float" [builtins fixtures/tuple.pyi] [out] @@ -194,6 +194,7 @@ def g(x: int) -> Final[int]: ... # E: Final can be only used as an outermost qu [out] [case testFinalDefiningNotInMethodExtensions] +# flags: --python-version 3.14 from typing_extensions import Final def f(x: Final[int]) -> int: ... # E: Final can be only used as an outermost qualifier in a variable annotation @@ -250,7 +251,7 @@ class C(Generic[T]): self.x: Final = x self.y: Final = 1 -reveal_type(C((1, 2)).x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(C((1, 2)).x) # N: Revealed type is "tuple[builtins.int, builtins.int]" C.x # E: Cannot access final instance attribute "x" on class object \ # E: Access to generic instance variables via class is ambiguous C.y # E: Cannot access final instance attribute "y" on class object @@ -1119,7 +1120,7 @@ class B: [out] [case testFinalInDeferredMethod] -from typing_extensions import Final +from typing import Final class A: def __init__(self) -> None: @@ -1128,6 +1129,7 @@ class A: [builtins fixtures/tuple.pyi] [case testFinalUsedWithClassVar] +# flags: --python-version 3.12 from typing import Final, ClassVar class A: @@ -1136,6 +1138,15 @@ class A: c: ClassVar[Final] = 1 # E: Final can be only used as an outermost qualifier in a variable annotation [out] +[case testFinalUsedWithClassVarAfterPy313] +# flags: --python-version 3.13 +from typing import Final, ClassVar + +class A: + a: Final[ClassVar[int]] = 1 + b: ClassVar[Final[int]] = 1 + c: ClassVar[Final] = 1 + [case testFinalClassWithAbstractMethod] from typing import final from abc import ABC, abstractmethod @@ -1176,7 +1187,7 @@ class Child(Parent): def __bar(self) -> None: ... [case testFinalWithoutBool] -from typing_extensions import final, Literal +from typing import Literal, final class A: pass @@ -1196,7 +1207,7 @@ reveal_type(C() and 42) # N: Revealed type is "Literal[42]?" [builtins fixtures/bool.pyi] [case testFinalWithoutBoolButWithLen] -from typing_extensions import final, Literal +from typing import Literal, final # Per Python data model, __len__ is called if __bool__ does not exist. # In a @final class, __bool__ would not exist. @@ -1218,3 +1229,110 @@ reveal_type(B() and 42) # N: Revealed type is "Literal[42]?" reveal_type(C() and 42) # N: Revealed type is "__main__.C" [builtins fixtures/bool.pyi] + +[case testCanAccessFinalClassInit] +from typing import final + +@final +class FinalClass: + pass + +def check_final_class() -> None: + new_instance = FinalClass() + new_instance.__init__() + +class FinalInit: + @final + def __init__(self) -> None: + pass + +def check_final_init() -> None: + new_instance = FinalInit() + new_instance.__init__() +[builtins fixtures/tuple.pyi] + +[case testNarrowingOfFinalPersistsInFunctions] +from typing import Final, Union + +def _init() -> Union[int, None]: + return 0 + +FOO: Final = _init() + +class Example: + + if FOO is not None: + reveal_type(FOO) # N: Revealed type is "builtins.int" + + def fn(self) -> int: + return FOO + +if FOO is not None: + reveal_type(FOO) # N: Revealed type is "builtins.int" + + def func() -> int: + return FOO + +[case testDisjointBase] +from typing_extensions import disjoint_base + +@disjoint_base +class Disjoint1: pass + +@disjoint_base +class Disjoint2: pass + +@disjoint_base +class DisjointChild(Disjoint1): pass + +class C1: pass +class C2(Disjoint1, C1): pass +class C3(DisjointChild, Disjoint1): pass + +class C4(Disjoint1, Disjoint2): # E: Class "C4" has incompatible disjoint bases + pass + +class C5(Disjoint2, Disjoint1): # E: Class "C5" has incompatible disjoint bases + pass + +class C6(Disjoint2, DisjointChild): # E: Class "C6" has incompatible disjoint bases + pass + +class C7(DisjointChild, Disjoint2): # E: Class "C7" has incompatible disjoint bases + pass + +class C8(DisjointChild, Disjoint1, Disjoint2): # E: Class "C8" has incompatible disjoint bases + pass + +class C9(C2, Disjoint2): # E: Class "C9" has incompatible disjoint bases + pass + +class C10(C3, Disjoint2): # E: Class "C10" has incompatible disjoint bases + pass + +[builtins fixtures/tuple.pyi] +[case testDisjointBaseSlots] +class S1: + __slots__ = ("a",) + +class S2: + __slots__ = ("b",) + +class S3: + __slots__ = () + +class S4(S1): + __slots__ = ("c",) + +class S5(S1, S2): # E: Class "S5" has incompatible disjoint bases + pass + +class S6(S1, S3): pass # OK +class S7(S3, S1): pass # OK + +class S8(S4, S1): pass # OK + +class S9(S2, S4): # E: Class "S9" has incompatible disjoint bases + pass + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 4f327a2f0edc0..8eec979029d00 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): @@ -340,7 +347,7 @@ def f() -> int: [case testNoReturnDisallowsReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def f() -> NoReturn: if bool(): @@ -351,7 +358,7 @@ def f() -> NoReturn: [case testNoReturnWithoutImplicitReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def no_return() -> NoReturn: pass def f() -> NoReturn: @@ -360,7 +367,7 @@ def f() -> NoReturn: [case testNoReturnDisallowsImplicitReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def f() -> NoReturn: # E: Implicit return in function which does not return non_trivial_function = 1 @@ -384,7 +391,7 @@ x = force_forward_reference() [case testNoReturnNoWarnNoReturn] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def no_return() -> NoReturn: pass def f() -> int: @@ -396,7 +403,7 @@ def f() -> int: [case testNoReturnInExpr] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn def no_return() -> NoReturn: pass def f() -> int: @@ -406,14 +413,14 @@ reveal_type(f() or no_return()) # N: Revealed type is "builtins.int" [case testNoReturnVariable] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn x = 0 # type: NoReturn # E: Incompatible types in assignment (expression has type "int", variable has type "Never") [builtins fixtures/dict.pyi] [case testNoReturnAsync] # flags: --warn-no-return -from mypy_extensions import NoReturn +from typing import NoReturn async def f() -> NoReturn: ... @@ -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 @@ -903,9 +979,9 @@ def foo(l: List[Unchecked]) -> List[Unchecked]: return l [builtins fixtures/list.pyi] [out] -main:5: error: Return type becomes "List[Any]" due to an unfollowed import -main:5: error: Argument 1 to "foo" becomes "List[Any]" due to an unfollowed import -main:6: error: Type of variable becomes "List[Any]" due to an unfollowed import +main:5: error: Return type becomes "list[Any]" due to an unfollowed import +main:5: error: Argument 1 to "foo" becomes "list[Any]" due to an unfollowed import +main:6: error: Type of variable becomes "list[Any]" due to an unfollowed import [case testDisallowImplicitAnyInherit] # flags: --ignore-missing-imports --disallow-any-unimported @@ -915,7 +991,7 @@ from typing import List class C(Unchecked): # E: Base type Unchecked becomes "Any" due to an unfollowed import pass -class A(List[Unchecked]): # E: Base type becomes "List[Any]" due to an unfollowed import +class A(List[Unchecked]): # E: Base type becomes "list[Any]" due to an unfollowed import pass [builtins fixtures/list.pyi] @@ -924,7 +1000,7 @@ 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] # E: Type alias target becomes "List[Any]" due to an unfollowed import +X = List[Unchecked] # E: Type alias target becomes "list[Any]" due to an unfollowed import def f(x: X) -> None: pass @@ -937,7 +1013,7 @@ from typing import List, cast foo = [1, 2, 3] -cast(List[Unchecked], foo) # E: Target type of cast becomes "List[Any]" due to an unfollowed import +cast(List[Unchecked], foo) # E: Target type of cast becomes "list[Any]" due to an unfollowed import cast(Unchecked, foo) # E: Target type of cast becomes "Any" due to an unfollowed import [builtins fixtures/list.pyi] @@ -950,7 +1026,7 @@ Point = NamedTuple('Point', [('x', List[Unchecked]), ('y', Unchecked)]) [builtins fixtures/list.pyi] [out] -main:5: error: NamedTuple type becomes "Tuple[List[Any], Any]" due to an unfollowed import +main:5: error: NamedTuple type becomes "tuple[list[Any], Any]" due to an unfollowed import [case testDisallowImplicitAnyTypeVarConstraints] # flags: --ignore-missing-imports --disallow-any-unimported @@ -961,7 +1037,7 @@ T = TypeVar('T', Unchecked, List[Unchecked], str) [builtins fixtures/list.pyi] [out] main:5: error: Constraint 1 becomes "Any" due to an unfollowed import -main:5: error: Constraint 2 becomes "List[Any]" due to an unfollowed import +main:5: error: Constraint 2 becomes "list[Any]" due to an unfollowed import [case testDisallowImplicitAnyNewType] # flags: --ignore-missing-imports --disallow-any-unimported @@ -969,7 +1045,7 @@ from typing import NewType, List from missing import Unchecked Baz = NewType('Baz', Unchecked) # E: Argument 2 to NewType(...) must be subclassable (got "Any") -Bar = NewType('Bar', List[Unchecked]) # E: Argument 2 to NewType(...) becomes "List[Any]" due to an unfollowed import +Bar = NewType('Bar', List[Unchecked]) # E: Argument 2 to NewType(...) becomes "list[Any]" due to an unfollowed import [builtins fixtures/list.pyi] @@ -982,7 +1058,7 @@ def foo(f: Callable[[], Unchecked]) -> Tuple[Unchecked]: return f() [builtins fixtures/list.pyi] [out] -main:5: error: Return type becomes "Tuple[Any]" due to an unfollowed import +main:5: error: Return type becomes "tuple[Any]" due to an unfollowed import main:5: error: Argument 1 to "foo" becomes "Callable[[], Any]" due to an unfollowed import [case testDisallowImplicitAnySubclassingExplicitAny] @@ -1006,25 +1082,25 @@ main:6: error: A type on this line becomes "Any" due to an unfollowed import [case testDisallowUnimportedAnyTypedDictSimple] # flags: --ignore-missing-imports --disallow-any-unimported -from mypy_extensions import TypedDict +from typing import TypedDict from x import Unchecked M = TypedDict('M', {'x': str, 'y': Unchecked}) # E: Type of a TypedDict key becomes "Any" due to an unfollowed import def f(m: M) -> M: pass # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowUnimportedAnyTypedDictGeneric] # flags: --ignore-missing-imports --disallow-any-unimported - -from mypy_extensions import TypedDict -from typing import List +from typing import List, TypedDict from x import Unchecked -M = TypedDict('M', {'x': str, 'y': List[Unchecked]}) # E: Type of a TypedDict key becomes "List[Any]" due to an unfollowed import +M = TypedDict('M', {'x': str, 'y': List[Unchecked]}) # E: Type of a TypedDict key becomes "list[Any]" due to an unfollowed import def f(m: M) -> M: pass # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowAnyDecoratedUnannotatedDecorator] # flags: --disallow-any-decorated @@ -1040,6 +1116,39 @@ def f(x: Any) -> Any: # E: Function is untyped after decorator transformation def h(x): # E: Function is untyped after decorator transformation pass [builtins fixtures/list.pyi] + +[case testDisallowAnyDecoratedUnannotatedDecoratorDeferred1] +# flags: --disallow-any-decorated +from typing import Callable + +def d(f: Callable[[int], None]) -> Callable[[int], None]: + return f + +def wrapper() -> None: + if c: + @d + def h(x): + pass + +c = [1] +[builtins fixtures/list.pyi] + +[case testDisallowAnyDecoratedUnannotatedDecoratorDeferred2] +# flags: --disallow-any-decorated +from typing import Callable + +def d(f: Callable[[int], None]) -> Callable[[int], None]: + return f + +c = 1 # no deferral - check that the previous testcase is valid + +def wrapper() -> None: + if c: + @d + def h(x): + pass +[builtins fixtures/list.pyi] + [case testDisallowAnyDecoratedErrorIsReportedOnlyOnce] # flags: --disallow-any-decorated @@ -1094,10 +1203,10 @@ def d3(f) -> Callable[[Any], List[str]]: pass def f(i: int, s: str) -> None: # E: Type of decorated function contains type "Any" ("Callable[[int, Any], Any]") pass @d2 -def g(i: int) -> None: # E: Type of decorated function contains type "Any" ("Callable[[int], List[Any]]") +def g(i: int) -> None: # E: Type of decorated function contains type "Any" ("Callable[[int], list[Any]]") pass @d3 -def h(i: int) -> None: # E: Type of decorated function contains type "Any" ("Callable[[Any], List[str]]") +def h(i: int) -> None: # E: Type of decorated function contains type "Any" ("Callable[[Any], list[str]]") pass [builtins fixtures/list.pyi] @@ -1184,9 +1293,9 @@ def g(s: List[Any]) -> None: f(0) -# type of list below is inferred with expected type of "List[Any]", so that becomes it's type -# instead of List[str] -g(['']) # E: Expression type contains "Any" (has type "List[Any]") +# type of list below is inferred with expected type of "list[Any]", so that becomes it's type +# instead of list[str] +g(['']) # E: Expression type contains "Any" (has type "list[Any]") [builtins fixtures/list.pyi] [case testDisallowAnyExprAllowsAnyInCast] @@ -1217,8 +1326,8 @@ n = Foo().g # type: Any # E: Expression has type "Any" from typing import List l: List = [] -l.append(1) # E: Expression type contains "Any" (has type "List[Any]") -k = l[0] # E: Expression type contains "Any" (has type "List[Any]") # E: Expression has type "Any" +l.append(1) # E: Expression type contains "Any" (has type "list[Any]") +k = l[0] # E: Expression type contains "Any" (has type "list[Any]") # E: Expression has type "Any" [builtins fixtures/list.pyi] [case testDisallowAnyExprTypeVar] @@ -1261,13 +1370,14 @@ def k(s: E) -> None: pass [case testDisallowAnyExprTypedDict] # flags: --disallow-any-expr -from mypy_extensions import TypedDict +from typing import TypedDict Movie = TypedDict('Movie', {'name': str, 'year': int}) def g(m: Movie) -> Movie: return m [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowIncompleteDefs] # flags: --disallow-incomplete-defs @@ -1407,8 +1517,7 @@ n: N [case testCheckDisallowAnyGenericsTypedDict] # flags: --disallow-any-generics -from typing import Dict, Any, Optional -from mypy_extensions import TypedDict +from typing import Dict, Any, Optional, TypedDict VarsDict = Dict[str, Any] HostsDict = Dict[str, Optional[VarsDict]] @@ -1421,19 +1530,18 @@ GroupDataDict = TypedDict( GroupsDict = Dict[str, GroupDataDict] # type: ignore [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCheckDisallowAnyGenericsStubOnly] -# flags: --disallow-any-generics --python-version 3.8 +# flags: --disallow-any-generics from asyncio import Future from queue import Queue x: Future[str] y: Queue[int] -p: Future # E: Missing type parameters for generic type "Future" \ - # N: Subscripting classes that are not generic at runtime may require escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html#not-generic-runtime -q: Queue # E: Missing type parameters for generic type "Queue" \ - # N: Subscripting classes that are not generic at runtime may require escaping, see https://mypy.readthedocs.io/en/stable/runtime_troubles.html#not-generic-runtime +p: Future # E: Missing type parameters for generic type "Future" +q: Queue # E: Missing type parameters for generic type "Queue" [file asyncio/__init__.pyi] from asyncio.futures import Future as Future [file asyncio/futures.pyi] @@ -1447,6 +1555,29 @@ class Queue(Generic[_T]): ... [builtins fixtures/async_await.pyi] [typing fixtures/typing-full.pyi] +[case testDisallowAnyGenericsBuiltinTuple] +# flags: --disallow-any-generics +s = tuple([1, 2, 3]) +def f(t: tuple) -> None: pass # E: Missing type parameters for generic type "tuple" +[builtins fixtures/tuple.pyi] + +[case testDisallowAnyGenericsBuiltinList] +# flags: --disallow-any-generics +l = list([1, 2, 3]) +def f(t: list) -> None: pass # E: Missing type parameters for generic type "list" +[builtins fixtures/list.pyi] + +[case testDisallowAnyGenericsBuiltinSet] +# flags: --disallow-any-generics +l = set({1, 2, 3}) +def f(s: set) -> None: pass # E: Missing type parameters for generic type "set" +[builtins fixtures/set.pyi] + +[case testDisallowAnyGenericsBuiltinDict] +# flags: --disallow-any-generics +l = dict([('a', 1)]) +def f(d: dict) -> None: pass # E: Missing type parameters for generic type "dict" +[builtins fixtures/dict.pyi] [case testCheckDefaultAllowAnyGeneric] from typing import TypeVar, Callable @@ -1730,51 +1861,51 @@ x: A # E:4: Missing type parameters for generic type "A" [builtins fixtures/list.pyi] [case testDisallowAnyExplicitDefSignature] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -def f(x: Any) -> None: # E: Explicit "Any" is not allowed +def f(x: Any) -> None: # E: Explicit "Any" is not allowed [explicit-any] pass -def g() -> Any: # E: Explicit "Any" is not allowed +def g() -> Any: # E: Explicit "Any" is not allowed [explicit-any] pass -def h() -> List[Any]: # E: Explicit "Any" is not allowed +def h() -> List[Any]: # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/list.pyi] [case testDisallowAnyExplicitVarDeclaration] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any -v: Any = '' # E: Explicit "Any" is not allowed -w = '' # type: Any # E: Explicit "Any" is not allowed +v: Any = '' # E: Explicit "Any" is not allowed [explicit-any] +w = '' # type: Any # E: Explicit "Any" is not allowed [explicit-any] class X: - y = '' # type: Any # E: Explicit "Any" is not allowed + y = '' # type: Any # E: Explicit "Any" is not allowed [explicit-any] [case testDisallowAnyExplicitGenericVarDeclaration] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -v: List[Any] = [] # E: Explicit "Any" is not allowed +v: List[Any] = [] # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitInheritance] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -class C(Any): # E: Explicit "Any" is not allowed +class C(Any): # E: Explicit "Any" is not allowed [explicit-any] pass -class D(List[Any]): # E: Explicit "Any" is not allowed +class D(List[Any]): # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/list.pyi] [case testDisallowAnyExplicitAlias] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -X = Any # E: Explicit "Any" is not allowed -Y = List[Any] # E: Explicit "Any" is not allowed +X = Any # E: Explicit "Any" is not allowed [explicit-any] +Y = List[Any] # E: Explicit "Any" is not allowed [explicit-any] def foo(x: X) -> Y: # no error x.nonexistent() # no error @@ -1782,70 +1913,70 @@ def foo(x: X) -> Y: # no error [builtins fixtures/list.pyi] [case testDisallowAnyExplicitGenericAlias] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, TypeVar, Tuple T = TypeVar('T') -TupleAny = Tuple[Any, T] # E: Explicit "Any" is not allowed +TupleAny = Tuple[Any, T] # E: Explicit "Any" is not allowed [explicit-any] def foo(x: TupleAny[str]) -> None: # no error pass -def goo(x: TupleAny[Any]) -> None: # E: Explicit "Any" is not allowed +def goo(x: TupleAny[Any]) -> None: # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/tuple.pyi] [case testDisallowAnyExplicitCast] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, cast x = 1 -y = cast(Any, x) # E: Explicit "Any" is not allowed -z = cast(List[Any], x) # E: Explicit "Any" is not allowed +y = cast(Any, x) # E: Explicit "Any" is not allowed [explicit-any] +z = cast(List[Any], x) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitNamedTuple] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, NamedTuple -Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed +Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitTypeVarConstraint] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, TypeVar -T = TypeVar('T', Any, List[Any]) # E: Explicit "Any" is not allowed +T = TypeVar('T', Any, List[Any]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitNewType] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, NewType # this error does not come from `--disallow-any-explicit` flag -Baz = NewType('Baz', Any) # E: Argument 2 to NewType(...) must be subclassable (got "Any") -Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed +Baz = NewType('Baz', Any) # E: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype] +Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitTypedDictSimple] -# flags: --disallow-any-explicit -from mypy_extensions import TypedDict -from typing import Any +# flags: --disallow-any-explicit --show-error-codes +from typing import Any, TypedDict -M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed +M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed [explicit-any] M(x='x', y=2) # no error def f(m: M) -> None: pass # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowAnyExplicitTypedDictGeneric] -# flags: --disallow-any-explicit -from mypy_extensions import TypedDict -from typing import Any, List +# flags: --disallow-any-explicit --show-error-codes +from typing import Any, List, TypedDict -M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed +M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed [explicit-any] N = TypedDict('N', {'x': str, 'y': List}) # no error [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testDisallowAnyGenericsTupleNoTypeParams] # flags: --disallow-any-generics @@ -1863,8 +1994,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 +2040,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[] = ...") +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 @@ -2149,7 +2303,7 @@ untyped_calls_exclude = foo, bar.A import tests.foo import bar [file bar.py] -x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") +x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") [file tests/__init__.py] [file tests/foo.py] x = [] # OK @@ -2162,6 +2316,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 @@ -2191,10 +2377,19 @@ x: int = "" # E: Incompatible types in assignment (expression has type "str", v x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testDisableBytearrayPromotion] -# flags: --disable-bytearray-promotion +# flags: --disable-bytearray-promotion --strict-equality def f(x: bytes) -> None: ... f(bytearray(b"asdf")) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" f(memoryview(b"asdf")) +ba = bytearray(b"") +if ba == b"": + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +if b"" == ba: + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +if ba == bytes(): + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +if bytes() == ba: + f(ba) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" [builtins fixtures/primitives.pyi] [case testDisableMemoryviewPromotion] @@ -2230,6 +2425,37 @@ def f(x: bytes, y: bytearray, z: memoryview) -> None: x in z [builtins fixtures/primitives.pyi] +[case testStrictBytes] +# flags: --strict-bytes +def f(x: bytes) -> None: ... +f(bytearray(b"asdf")) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +f(memoryview(b"asdf")) # E: Argument 1 to "f" has incompatible type "memoryview"; expected "bytes" +[builtins fixtures/primitives.pyi] + +[case testNoStrictBytes] +# flags: --no-strict-bytes +def f(x: bytes) -> None: ... +f(bytearray(b"asdf")) +f(memoryview(b"asdf")) +[builtins fixtures/primitives.pyi] + +[case testStrictBytesDisabledByDefault] +# TODO: probably change this default in Mypy v2.0, with https://github.com/python/mypy/pull/18371 +# (this would also obsolete the testStrictBytesEnabledByStrict test, below) +def f(x: bytes) -> None: ... +f(bytearray(b"asdf")) +f(memoryview(b"asdf")) +[builtins fixtures/primitives.pyi] + +[case testStrictBytesEnabledByStrict] +# flags: --strict --disable-error-code type-arg +# The type-arg thing is just work around the primitives.pyi isinstance Tuple not having type parameters, +# which isn't important for this. +def f(x: bytes) -> None: ... +f(bytearray(b"asdf")) # E: Argument 1 to "f" has incompatible type "bytearray"; expected "bytes" +f(memoryview(b"asdf")) # E: Argument 1 to "f" has incompatible type "memoryview"; expected "bytes" +[builtins fixtures/primitives.pyi] + [case testNoCrashFollowImportsForStubs] # flags: --config-file tmp/mypy.ini {**{"x": "y"}} @@ -2257,13 +2483,13 @@ cb(fn) x: int = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] list(1) # E: No overload variant of "list" matches argument type "int" [call-overload] \ # N: Possible overload variants: \ - # N: def [T] __init__(self) -> List[T] \ - # N: def [T] __init__(self, x: Iterable[T]) -> List[T] \ + # N: def [T] __init__(self) -> list[T] \ + # N: def [T] __init__(self, x: Iterable[T]) -> list[T] \ # N: See https://mypy.rtfd.io/en/stable/_refs.html#code-call-overload for more info list(2) # E: No overload variant of "list" matches argument type "int" [call-overload] \ # N: Possible overload variants: \ - # N: def [T] __init__(self) -> List[T] \ - # N: def [T] __init__(self, x: Iterable[T]) -> List[T] + # N: def [T] __init__(self) -> list[T] \ + # N: def [T] __init__(self, x: Iterable[T]) -> list[T] [builtins fixtures/list.pyi] [case testNestedGenericInAliasDisallow] @@ -2284,3 +2510,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-formatting.test b/test-data/unit/check-formatting.test index 83ae9b526f22e..b5b37f8d29768 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -150,8 +150,8 @@ di: Dict[int, int] '%(a)' % 1 # E: Format requires a mapping (expression has type "int", expected type for mapping is "SupportsKeysAndGetItem[str, Any]") '%()d' % a '%()d' % ds -'%()d' % do # E: Format requires a mapping (expression has type "Dict[object, int]", expected type for mapping is "SupportsKeysAndGetItem[str, Any]") -b'%()d' % ds # E: Format requires a mapping (expression has type "Dict[str, int]", expected type for mapping is "SupportsKeysAndGetItem[bytes, Any]") +'%()d' % do # E: Format requires a mapping (expression has type "dict[object, int]", expected type for mapping is "SupportsKeysAndGetItem[str, Any]") +b'%()d' % ds # E: Format requires a mapping (expression has type "dict[str, int]", expected type for mapping is "SupportsKeysAndGetItem[bytes, Any]") '%()s' % StringThing() b'%()s' % BytesThing() [builtins fixtures/primitives.pyi] @@ -502,7 +502,7 @@ def better_snakecase(text: str) -> str: [builtins fixtures/primitives.pyi] [case testFormatCallFinal] -from typing_extensions import Final +from typing import Final FMT: Final = '{.x}, {:{:d}}' @@ -511,7 +511,7 @@ FMT.format(1, 2, 'no') # E: "int" has no attribute "x" \ [builtins fixtures/primitives.pyi] [case testFormatCallFinalChar] -from typing_extensions import Final +from typing import Final GOOD: Final = 'c' BAD: Final = 'no' @@ -542,7 +542,7 @@ x: Any [builtins fixtures/primitives.pyi] [case testFormatCallAccessorsIndices] -from typing_extensions import TypedDict +from typing import TypedDict class User(TypedDict): id: int @@ -554,6 +554,7 @@ u: User def f() -> str: ... '{[f()]}'.format(u) # E: Invalid index expression in format field accessor "[f()]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFormatCallFlags] from typing import Union diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 96f9815019e63..1882f235f7e30 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -105,16 +105,38 @@ if int(): h = h [case testSubtypingFunctionsDoubleCorrespondence] +def l(x) -> None: ... +def r(__x, *, x) -> None: ... +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "def r(Any, /, *, x: Any) -> None") +[case testSubtypingFunctionsDoubleCorrespondenceNamedOptional] def l(x) -> None: ... -def r(__, *, x) -> None: ... -r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "Callable[[Any, NamedArg(Any, 'x')], None]") +def r(__x, *, x = 1) -> None: ... +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "def r(Any, /, *, x: Any = ...) -> None") -[case testSubtypingFunctionsRequiredLeftArgNotPresent] +[case testSubtypingFunctionsDoubleCorrespondenceBothNamedOptional] +def l(x = 1) -> None: ... +def r(__x, *, x = 1) -> None: ... +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "def r(Any, /, *, x: Any = ...) -> None") + +[case testSubtypingFunctionsTrivialSuffixRequired] +def l(__x) -> None: ... +def r(x, *args, **kwargs) -> None: ... + +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any], None]", variable has type "def r(x: Any, *args: Any, **kwargs: Any) -> None") +[builtins fixtures/dict.pyi] +[case testSubtypingFunctionsTrivialSuffixOptional] +def l(__x = 1) -> None: ... +def r(x = 1, *args, **kwargs) -> None: ... + +r = l # E: Incompatible types in assignment (expression has type "def l(Any = ..., /) -> None", variable has type "def r(x: Any = ..., *args: Any, **kwargs: Any) -> None") +[builtins fixtures/dict.pyi] + +[case testSubtypingFunctionsRequiredLeftArgNotPresent] def l(x, y) -> None: ... def r(x) -> None: ... -r = l # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") +r = l # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") [case testSubtypingFunctionsImplicitNames] from typing import Any @@ -148,13 +170,13 @@ if int(): if int(): ff_nonames = f_nonames # reset if int(): - ff = ff_nonames # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") + ff = ff_nonames # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "def f(a: int, b: str) -> None") if int(): ff = f # reset if int(): - gg = ff # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]", variable has type "Callable[[Arg(int, 'a'), DefaultArg(str, 'b')], None]") + gg = ff # E: Incompatible types in assignment (expression has type "def f(a: int, b: str) -> None", variable has type "def g(a: int, b: str = ...) -> None") if int(): - gg = hh # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'aa'), DefaultArg(str, 'b')], None]", variable has type "Callable[[Arg(int, 'a'), DefaultArg(str, 'b')], None]") + gg = hh # E: Incompatible types in assignment (expression has type "def h(aa: int, b: str = ...) -> None", variable has type "def g(a: int, b: str = ...) -> None") [case testSubtypingFunctionsArgsKwargs] from typing import Any, Callable @@ -223,7 +245,7 @@ gg = g if int(): ff = g if int(): - gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") + gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "def g(a: int, b: str) -> None") [case testLackOfNamesFastparse] def f(__a: int, __b: str) -> None: pass @@ -235,7 +257,7 @@ gg = g if int(): ff = g if int(): - gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") + gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "def g(a: int, b: str) -> None") [case testFunctionTypeCompatibilityWithOtherTypes] # flags: --no-strict-optional @@ -347,7 +369,7 @@ t: type a: A if int(): - a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") + a = A # E: Incompatible types in assignment (expression has type "type[A]", variable has type "A") if int(): t = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "type") if int(): @@ -442,10 +464,10 @@ def f(x: C) -> C: pass from typing import Any, Callable, List def f(fields: List[Callable[[Any], Any]]): pass class C: pass -f([C]) # E: List item 0 has incompatible type "Type[C]"; expected "Callable[[Any], Any]" +f([C]) # E: List item 0 has incompatible type "type[C]"; expected "Callable[[Any], Any]" class D: def __init__(self, a, b): pass -f([D]) # E: List item 0 has incompatible type "Type[D]"; expected "Callable[[Any], Any]" +f([D]) # E: List item 0 has incompatible type "type[D]"; expected "Callable[[Any], Any]" [builtins fixtures/list.pyi] [case testSubtypingTypeTypeAsCallable] @@ -461,7 +483,7 @@ class A: pass x: Callable[..., A] y: Type[A] if int(): - y = x # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "Type[A]") + y = x # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "type[A]") -- Default argument values -- ----------------------- @@ -916,10 +938,19 @@ f(None) # E: Too many arguments for "f" from typing import Any, Callable def dec1(f: Callable[[Any], None]) -> Callable[[], None]: pass def dec2(f: Callable[[Any, Any], None]) -> Callable[[Any], None]: pass -@dec1 # E: Argument 1 to "dec2" has incompatible type "Callable[[Any], Any]"; expected "Callable[[Any, Any], None]" -@dec2 +@dec1 +@dec2 # E: Argument 1 to "dec2" has incompatible type "Callable[[Any], Any]"; expected "Callable[[Any, Any], None]" def f(x): pass +def faulty(c: Callable[[int], None]) -> Callable[[tuple[int, int]], None]: + return lambda x: None + +@faulty # E: Argument 1 to "faulty" has incompatible type "Callable[[tuple[int, int]], None]"; expected "Callable[[int], None]" +@faulty # E: Argument 1 to "faulty" has incompatible type "Callable[[str], None]"; expected "Callable[[int], None]" +def g(x: str) -> None: + return None +[builtins fixtures/tuple.pyi] + [case testInvalidDecorator2] from typing import Any, Callable def dec1(f: Callable[[Any, Any], None]) -> Callable[[], None]: pass @@ -1474,7 +1505,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 @@ -1489,9 +1520,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: @@ -1580,11 +1614,11 @@ if g(C()): def f(x: B) -> B: pass [case testRedefineFunctionDefinedAsVariableInitializedToEmptyList] -f = [] # E: Need type annotation for "f" (hint: "f: List[] = ...") +f = [] # E: Need type annotation for "f" (hint: "f: list[] = ...") if object(): def f(): pass # E: Incompatible redefinition -f() # E: "List[Any]" not callable -f(1) # E: "List[Any]" not callable +f() # E: "list[Any]" not callable +f(1) # E: "list[Any]" not callable [builtins fixtures/list.pyi] [case testDefineConditionallyAsImportedAndDecorated] @@ -1599,7 +1633,7 @@ else: def f(): yield [file m.py] -def f(): pass +def f() -> None: pass [case testDefineConditionallyAsImportedAndDecoratedWithInference] if int(): @@ -1778,7 +1812,7 @@ def a(f: F): f("foo") # E: Argument 1 has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] -[case testCallableParsingInInheritence] +[case testCallableParsingInInheritance] from collections import namedtuple class C(namedtuple('t', 'x')): @@ -1793,7 +1827,6 @@ 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 @@ -1824,13 +1857,6 @@ Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is 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] @@ -1990,12 +2016,12 @@ def isf_unnamed(__i: int, __s: str) -> str: int_str_fun = isf int_str_fun = isf_unnamed -int_named_str_fun = isf_unnamed # E: Incompatible types in assignment (expression has type "Callable[[int, str], str]", variable has type "Callable[[int, Arg(str, 's')], str]") +int_named_str_fun = isf_unnamed # E: Incompatible types in assignment (expression has type "Callable[[int, str], str]", variable has type "def (int, /, s: str) -> str") int_opt_str_fun = iosf int_str_fun = iosf -int_opt_str_fun = isf # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'ii'), Arg(str, 'ss')], str]", variable has type "Callable[[int, DefaultArg(str)], str]") +int_opt_str_fun = isf # E: Incompatible types in assignment (expression has type "def isf(ii: int, ss: str) -> str", variable has type "def (int, str = ..., /) -> str") -int_named_str_fun = isf # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'ii'), Arg(str, 'ss')], str]", variable has type "Callable[[int, Arg(str, 's')], str]") +int_named_str_fun = isf # E: Incompatible types in assignment (expression has type "def isf(ii: int, ss: str) -> str", variable has type "def (int, /, s: str) -> str") int_named_str_fun = iosf [builtins fixtures/dict.pyi] @@ -2050,7 +2076,7 @@ def g4(*, y: int) -> str: pass f(g1) f(g2) f(g3) -f(g4) # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(int, 'y')], str]"; expected "Callable[..., int]" +f(g4) # E: Argument 1 to "f" has incompatible type "def g4(*, y: int) -> str"; expected "Callable[..., int]" [case testCallableWithArbitraryArgsSubtypingWithGenericFunc] from typing import Callable, TypeVar @@ -2085,7 +2111,7 @@ f(x=1, y="hello", z=[]) from typing import Dict def f(x, **kwargs): # type: (...) -> None success_dict_type = kwargs # type: Dict[str, str] - failure_dict_type = kwargs # type: Dict[int, str] # E: Incompatible types in assignment (expression has type "Dict[str, Any]", variable has type "Dict[int, str]") + failure_dict_type = kwargs # type: Dict[int, str] # E: Incompatible types in assignment (expression has type "dict[str, Any]", variable has type "dict[int, str]") f(1, thing_in_kwargs=["hey"]) [builtins fixtures/dict.pyi] [out] @@ -2094,7 +2120,7 @@ f(1, thing_in_kwargs=["hey"]) from typing import Tuple, Any def f(x, *args): # type: (...) -> None success_tuple_type = args # type: Tuple[Any, ...] - fail_tuple_type = args # type: None # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "None") + fail_tuple_type = args # type: None # E: Incompatible types in assignment (expression has type "tuple[Any, ...]", variable has type "None") f(1, "hello") [builtins fixtures/tuple.pyi] [out] @@ -2212,7 +2238,7 @@ def g(x, y): pass def h(x): pass def j(y) -> Any: pass f = h -f = j # E: Incompatible types in assignment (expression has type "Callable[[Arg(Any, 'y')], Any]", variable has type "Callable[[Arg(Any, 'x')], Any]") +f = j # E: Incompatible types in assignment (expression has type "def j(y: Any) -> Any", variable has type "def f(x: Any) -> Any") f = g # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], Any]", variable has type "Callable[[Any], Any]") [case testRedefineFunction2] @@ -2346,7 +2372,7 @@ a.__eq__(other=a) # E: Unexpected keyword argument "other" for "__eq__" of "A" [builtins fixtures/bool.pyi] --- Type variable shenanagins +-- Type variable shenanigans -- ------------------------- [case testGenericFunctionTypeDecl] @@ -2421,7 +2447,7 @@ def make_list() -> List[T]: pass l: List[int] = make_list() -bad = make_list() # E: Need type annotation for "bad" (hint: "bad: List[] = ...") +bad = make_list() # E: Need type annotation for "bad" (hint: "bad: list[] = ...") [builtins fixtures/list.pyi] [case testAnonymousArgumentError] @@ -2468,27 +2494,27 @@ def fn( from typing import Union, Dict, List def f() -> List[Union[str, int]]: x = ['a'] - return x # E: Incompatible return value type (got "List[str]", expected "List[Union[str, int]]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + return x # E: Incompatible return value type (got "list[str]", expected "list[Union[str, int]]") \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant \ - # N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]" + # N: Perhaps you need a type annotation for "x"? Suggestion: "list[Union[str, int]]" def g() -> Dict[str, Union[str, int]]: x = {'a': 'a'} - return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[str, Union[str, int]]") \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + return x # E: Incompatible return value type (got "dict[str, str]", expected "dict[str, Union[str, int]]") \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type \ - # N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]" + # N: Perhaps you need a type annotation for "x"? Suggestion: "dict[str, Union[str, int]]" def h() -> Dict[Union[str, int], str]: x = {'a': 'a'} - return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[Union[str, int], str]") \ -# N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[Union[str, int], str]" + return x # E: Incompatible return value type (got "dict[str, str]", expected "dict[Union[str, int], str]") \ +# N: Perhaps you need a type annotation for "x"? Suggestion: "dict[Union[str, int], str]" def i() -> List[Union[int, float]]: x: List[int] = [1] - return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + return x # E: Incompatible return value type (got "list[int]", expected "list[Union[int, float]]") \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/dict.pyi] @@ -2497,11 +2523,11 @@ def i() -> List[Union[int, float]]: from typing import Union, List def f() -> List[Union[int, float]]: x = ['a'] - return x # E: Incompatible return value type (got "List[str]", expected "List[Union[int, float]]") + return x # E: Incompatible return value type (got "list[str]", expected "list[Union[int, float]]") def g() -> List[Union[str, int]]: x = ('a', 2) - return x # E: Incompatible return value type (got "Tuple[str, int]", expected "List[Union[str, int]]") + return x # E: Incompatible return value type (got "tuple[str, int]", expected "list[Union[str, int]]") [builtins fixtures/list.pyi] @@ -2509,7 +2535,7 @@ def g() -> List[Union[str, int]]: from typing import Union, Dict, List def f() -> Dict[str, Union[str, int]]: x = {'a': 'a', 'b': 2} - return x # E: Incompatible return value type (got "Dict[str, object]", expected "Dict[str, Union[str, int]]") + return x # E: Incompatible return value type (got "dict[str, object]", expected "dict[str, Union[str, int]]") def g() -> Dict[str, Union[str, int]]: x: Dict[str, Union[str, int]] = {'a': 'a', 'b': 2} @@ -2517,7 +2543,7 @@ def g() -> Dict[str, Union[str, int]]: def h() -> List[Union[str, int]]: x = ['a', 2] - return x # E: Incompatible return value type (got "List[object]", expected "List[Union[str, int]]") + return x # E: Incompatible return value type (got "list[object]", expected "list[Union[str, int]]") def i() -> List[Union[str, int]]: x: List[Union[str, int]] = ['a', 2] @@ -2726,7 +2752,7 @@ class B: @property @dec def f(self) -> int: pass - @dec # E: Only supported top decorator is @f.setter + @dec # E: Only supported top decorators are "@f.setter" and "@f.deleter" @f.setter def f(self, v: int) -> None: pass @@ -2738,7 +2764,6 @@ class C: @dec def f(self, v: int) -> None: pass [builtins fixtures/property.pyi] -[out] [case testInvalidArgCountForProperty] from typing import Callable, TypeVar @@ -2757,7 +2782,6 @@ class A: @property def h(self, *args, **kwargs) -> int: pass # OK [builtins fixtures/property.pyi] -[out] [case testSubtypingUnionGenericBounds] from typing import Callable, TypeVar, Union, Sequence @@ -2785,6 +2809,8 @@ class Child(Base): @decorator def foo(self) -> int: return 42 +reveal_type(Child().foo) # N: Revealed type is "builtins.int" +Child().foo = 1 # E: Property "foo" defined in "Child" is read-only reveal_type(Child().foo) # N: Revealed type is "builtins.int" @@ -2801,15 +2827,13 @@ class not_a_decorator: def __init__(self, fn): ... class BadChild2(Base): + # Override error not shown as accessing 'foo' on BadChild2 returns Any. @property @not_a_decorator - def foo(self) -> int: # E: "not_a_decorator" not callable \ - # E: Signature of "foo" incompatible with supertype "Base" \ - # N: Superclass: \ - # N: int \ - # N: Subclass: \ - # N: not_a_decorator + def foo(self) -> int: return 42 +reveal_type(BadChild2().foo) # E: "not_a_decorator" not callable \ + # N: Revealed type is "Any" [builtins fixtures/property.pyi] [case explicitOverride] @@ -3251,6 +3275,39 @@ class C(B): def __f(self, y: int) -> str: pass # OK [typing fixtures/typing-override.pyi] +[case testOverrideUntypedDef] +# flags: --python-version 3.12 +from typing import override + +class Parent: pass + +class Child(Parent): + @override + def foo(self, y): pass # E: Method "foo" is marked as an override, but no base method was found with this name + +[typing fixtures/typing-override.pyi] + +[case testOverrideOnUnknownBaseClass] +# flags: --python-version 3.12 +from typing import overload, override + +from unknown import UnknownParent # type: ignore[import-not-found] + +class UnknownChild(UnknownParent): + @override + def foo(self, y): pass # OK + @override + def bar(self, y: str) -> None: pass # OK + + @override + @overload + def baz(self, y: str) -> None: ... + @override + @overload + def baz(self, y: int) -> None: ... + def baz(self, y: str | int) -> None: ... +[typing fixtures/typing-override.pyi] + [case testCallableProperty] from typing import Callable @@ -3365,8 +3422,7 @@ class Bar(Foo): [builtins fixtures/property.pyi] [case testNoCrashOnUnpackOverride] -from typing import Unpack -from typing_extensions import TypedDict +from typing import TypedDict, Unpack class Params(TypedDict): x: int @@ -3385,9 +3441,9 @@ class C(B): # N: def meth(*, x: int, y: str) -> None \ # N: Subclass: \ # N: def meth(*, x: int, y: int) -> None - ... -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testOverrideErrorLocationNamed] class B: @@ -3460,3 +3516,195 @@ class Qux(Bar): def baz(self, x) -> None: pass [builtins fixtures/tuple.pyi] + +[case testDistinctFormatting] +from typing import Awaitable, Callable, ParamSpec + +P = ParamSpec("P") + +class A: pass +class B(A): pass + +def decorator(f: Callable[P, None]) -> Callable[[Callable[P, A]], None]: + return lambda _: None + +def key(x: int) -> None: ... +def fn_b(b: int) -> B: ... + +decorator(key)(fn_b) # E: Argument 1 has incompatible type "def fn_b(b: int) -> B"; expected "def (x: int) -> A" + +def decorator2(f: Callable[P, None]) -> Callable[ + [Callable[P, Awaitable[None]]], + Callable[P, Awaitable[None]], +]: + return lambda f: f + +def key2(x: int) -> None: + ... + +@decorator2(key2) # E: Argument 1 has incompatible type "def foo2(y: int) -> Coroutine[Any, Any, None]"; expected "def (x: int) -> Awaitable[None]" +async def foo2(y: int) -> None: + ... + +class Parent: + def method_without(self) -> "Parent": ... + def method_with(self, param: str) -> "Parent": ... + +class Child(Parent): + method_without: Callable[[], "Child"] + method_with: Callable[[str], "Child"] # E: Incompatible types in assignment (expression has type "Callable[[str], Child]", base class "Parent" defined the type as "def method_with(self, param: str) -> Parent") +[builtins fixtures/tuple.pyi] + +[case testDistinctFormattingUnion] +from typing import Callable, Union +from mypy_extensions import Arg + +def f(x: Callable[[Arg(int, 'x')], None]) -> None: pass + +y: Callable[[Union[int, str]], None] +f(y) # E: Argument 1 to "f" has incompatible type "Callable[[Union[int, str]], None]"; expected "def (x: int) -> None" +[builtins fixtures/tuple.pyi] + +[case testAbstractOverloadsWithoutImplementationAllowed] +from abc import abstractmethod +from typing import overload, Union + +class Foo: + @overload + @abstractmethod + def foo(self, value: int) -> int: + ... + @overload + @abstractmethod + def foo(self, value: str) -> str: + ... + +class Bar(Foo): + @overload + def foo(self, value: int) -> int: + ... + @overload + def foo(self, value: str) -> str: + ... + + def foo(self, value: Union[int, str]) -> Union[int, str]: + return super().foo(value) # E: Call to abstract method "foo" of "Foo" with trivial body via super() is unsafe + +[case fullNamesOfImportedBaseClassesDisplayed] +from a import A + +class B(A): + def f(self, x: str) -> None: # E: Argument 1 of "f" is incompatible with supertype "a.A"; supertype defines the argument type as "int" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides + ... + def g(self, x: str) -> None: # E: Signature of "g" incompatible with supertype "a.A" \ + # N: Superclass: \ + # N: def g(self) -> None \ + # N: Subclass: \ + # N: def g(self, x: str) -> None + ... + +[file a.py] +class A: + def f(self, x: int) -> None: + ... + def g(self) -> None: + ... + +[case testBoundMethodsAssignedInClassBody] +from typing import Callable + +class A: + def f(self, x: int) -> str: + pass + @classmethod + def g(cls, x: int) -> str: + pass + @staticmethod + def h(x: int) -> str: + pass + attr: Callable[[int], str] + +class C: + x1 = A.f + x2 = A.g + x3 = A().f + x4 = A().g + x5 = A.h + x6 = A().h + x7 = A().attr + +reveal_type(C.x1) # N: Revealed type is "def (self: __main__.A, x: builtins.int) -> builtins.str" +reveal_type(C.x2) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C.x3) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C.x4) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C.x5) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C.x6) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C.x7) # N: Revealed type is "def (builtins.int) -> builtins.str" + +reveal_type(C().x1) # E: Invalid self argument "C" to attribute function "x1" with type "Callable[[A, int], str]" \ + # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C().x2) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C().x3) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C().x4) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C().x5) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C().x6) # N: Revealed type is "def (x: builtins.int) -> builtins.str" +reveal_type(C().x7) # E: Invalid self argument "C" to attribute function "x7" with type "Callable[[int], str]" \ + # N: Revealed type is "def () -> builtins.str" +[builtins fixtures/classmethod.pyi] + +[case testFunctionRedefinitionDeferred] +from typing import Callable, TypeVar + +def outer() -> None: + if bool(): + def inner() -> str: ... + else: + def inner() -> int: ... # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def inner() -> str \ + # N: Redefinition: \ + # N: def inner() -> int + x = defer() + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ... + +@deco +def defer() -> int: ... +[builtins fixtures/list.pyi] + +[case testCheckFunctionErrorContextDuplicateDeferred] +# flags: --show-error-context +from typing import Callable, TypeVar + +def a() -> None: + def b() -> None: + 1 + "" + x = defer() + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ... + +@deco +def defer() -> int: ... +[out] +main: note: In function "a": +main:6: error: Unsupported operand types for + ("int" and "str") + +[case testNoExtraNoteForUnpacking] +from typing import Protocol + +class P(Protocol): + arg: int + # Something that list and dict also have + def __contains__(self, item: object) -> bool: ... + +def foo(x: P, y: P) -> None: ... + +args: list[object] +foo(*args) # E: Argument 1 to "foo" has incompatible type "*list[object]"; expected "P" +kwargs: dict[str, object] +foo(**kwargs) # E: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "P" +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 9f8fbd42440be..650928b1a5edb 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -162,7 +162,7 @@ def takes_callable_int(f: Callable[..., int]) -> None: ... def takes_callable_str(f: Callable[..., str]) -> None: ... takes_callable_int(p1) takes_callable_str(p1) # E: Argument 1 to "takes_callable_str" has incompatible type "partial[int]"; expected "Callable[..., str]" \ - # N: "partial[int].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], int]" + # N: "partial[int].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> int" p2 = functools.partial(foo, 1) p2("a") # OK @@ -213,9 +213,9 @@ functools.partial(foo, 1, "a", "b", "c", d="a") # E: Argument 3 to "foo" has in def bar(*a: bytes, **k: int): p1("a", 2, 3, 4, d="a", **k) 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", **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: Expected iterable as variadic argument def baz(a: int, b: int) -> int: ... @@ -223,6 +223,8 @@ 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] @@ -290,7 +292,7 @@ p1("a", "b") # TODO: false negative import functools from typing_extensions import TypeGuard -def is_str_list(val: list[object]) -> TypeGuard[list[str]]: ... # E: "list" is not subscriptable, use "typing.List" instead +def is_str_list(val: list[object]) -> TypeGuard[list[str]]: ... reveal_type(functools.partial(is_str_list, [1, 2, 3])) # N: Revealed type is "functools.partial[builtins.bool]" reveal_type(functools.partial(is_str_list, [1, 2, 3])()) # N: Revealed type is "builtins.bool" @@ -346,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 @@ -363,11 +382,11 @@ T = TypeVar("T") def generic(string: str, integer: int, resulting_type: Type[T]) -> T: ... p: partial[str] = partial(generic, resulting_type=str) -q: partial[bool] = partial(generic, resulting_type=str) # E: Argument "resulting_type" to "generic" has incompatible type "Type[str]"; expected "Type[bool]" +q: partial[bool] = partial(generic, resulting_type=str) # E: Argument "resulting_type" to "generic" has incompatible type "type[str]"; expected "type[bool]" pc: Callable[..., str] = partial(generic, resulting_type=str) qc: Callable[..., bool] = partial(generic, resulting_type=str) # E: Incompatible types in assignment (expression has type "partial[str]", variable has type "Callable[..., bool]") \ - # N: "partial[str].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], str]" + # N: "partial[str].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> str" [builtins fixtures/tuple.pyi] [case testFunctoolsPartialNestedPartial] @@ -413,7 +432,8 @@ def foo(cls3: Type[B[T]]): [builtins fixtures/tuple.pyi] [case testFunctoolsPartialTypedDictUnpack] -from typing_extensions import TypedDict, Unpack +from typing import TypedDict +from typing_extensions import Unpack from functools import partial class D1(TypedDict, total=False): @@ -489,8 +509,8 @@ def main6(a2good: A2Good, a2bad: A2Bad, **d1: Unpack[D1]) -> None: partial(fn4, **d1)(a2="asdf") partial(fn4, **d1)(**a2good) partial(fn4, **d1)(**a2bad) # E: Argument "a2" to "fn4" has incompatible type "int"; expected "str" - [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFunctoolsPartialNestedGeneric] @@ -511,7 +531,7 @@ reveal_type(first_kw(args=[1])) # N: Revealed type is "builtins.int" # TODO: this is indeed invalid, but the error is incomprehensible. first_kw([1]) # E: Too many positional arguments for "get" \ # E: Too few arguments for "get" \ - # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int" + # E: Argument 1 to "get" has incompatible type "list[int]"; expected "int" [builtins fixtures/list.pyi] [case testFunctoolsPartialHigherOrder] @@ -557,3 +577,152 @@ def bar(f: S) -> S: g = functools.partial(f, "foo") return f [builtins fixtures/primitives.pyi] + +[case testFunctoolsPartialAbstractType] +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] + +[case testFunctoolsPartialOverloadedCallableProtocol] +from functools import partial +from typing import Callable, Protocol, overload + +class P(Protocol): + @overload + def __call__(self, x: int) -> int: ... + @overload + def __call__(self, x: str) -> str: ... + +def f(x: P): + reveal_type(partial(x, 1)()) # N: Revealed type is "builtins.int" + + # TODO: but this is incorrect, predating the functools.partial plugin + reveal_type(partial(x, "a")()) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeVarErasure] +from typing import Callable, TypeVar, Union +from typing_extensions import ParamSpec, TypeVarTuple, Unpack +from functools import partial + +def use_int_callable(x: Callable[[int], int]) -> None: + pass +def use_func_callable( + x: Callable[ + [Callable[[int], None]], + Callable[[int], None], + ], +) -> None: + pass + +Tc = TypeVar("Tc", int, str) +Tb = TypeVar("Tb", bound=Union[int, str]) +P = ParamSpec("P") +Ts = TypeVarTuple("Ts") + +def func_b(a: Tb, b: str) -> Tb: + return a +def func_c(a: Tc, b: str) -> Tc: + return a + +def func_fn(fn: Callable[P, Tc], b: str) -> Callable[P, Tc]: + return fn +def func_fn_unpack(fn: Callable[[Unpack[Ts]], Tc], b: str) -> Callable[[Unpack[Ts]], Tc]: + return fn + +# We should not leak stray typevars that aren't in scope: +reveal_type(partial(func_b, b="")) # N: Revealed type is "functools.partial[Any]" +reveal_type(partial(func_c, b="")) # N: Revealed type is "functools.partial[Any]" +reveal_type(partial(func_fn, b="")) # N: Revealed type is "functools.partial[def (*Any, **Any) -> Any]" +reveal_type(partial(func_fn_unpack, b="")) # N: Revealed type is "functools.partial[def (*Any) -> Any]" + +use_int_callable(partial(func_b, b="")) +use_func_callable(partial(func_b, b="")) +use_int_callable(partial(func_c, b="")) +use_func_callable(partial(func_c, b="")) +use_int_callable(partial(func_fn, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[def (*Any, **Any) -> Any]"; expected "Callable[[int], int]" \ + # N: "partial[def (*Any, **Any) -> Any].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> def (*Any, **Any) -> Any" +use_func_callable(partial(func_fn, b="")) +use_int_callable(partial(func_fn_unpack, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[def (*Any) -> Any]"; expected "Callable[[int], int]" \ + # N: "partial[def (*Any) -> Any].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> def (*Any) -> Any" +use_func_callable(partial(func_fn_unpack, b="")) + +# But we should not erase typevars that aren't bound by function +# passed to `partial`: + +def outer_b(arg: Tb) -> None: + + def inner(a: Tb, b: str) -> Tb: + return a + + reveal_type(partial(inner, b="")) # N: Revealed type is "functools.partial[Tb`-1]" + use_int_callable(partial(inner, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[Tb]"; expected "Callable[[int], int]" \ + # N: "partial[Tb].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> Tb" + +def outer_c(arg: Tc) -> None: + + def inner(a: Tc, b: str) -> Tc: + return a + + reveal_type(partial(inner, b="")) # N: Revealed type is "functools.partial[builtins.int]" \ + # N: Revealed type is "functools.partial[builtins.str]" + use_int_callable(partial(inner, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[str]"; expected "Callable[[int], int]" \ + # N: "partial[str].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> str" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 3ae815a5cd48a..3f088308da64e 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -1,48 +1,5 @@ -- Test cases for generic aliases -[case testGenericBuiltinWarning] -# flags: --python-version 3.8 -t1: list -t2: list[int] # E: "list" is not subscriptable, use "typing.List" instead -t3: list[str] # E: "list" is not subscriptable, use "typing.List" instead - -t4: tuple -t5: tuple[int] # E: "tuple" is not subscriptable, use "typing.Tuple" instead -t6: tuple[int, str] # E: "tuple" is not subscriptable, use "typing.Tuple" instead -t7: tuple[int, ...] # E: Unexpected "..." \ - # E: "tuple" is not subscriptable, use "typing.Tuple" instead - -t8: dict = {} -t9: dict[int, str] # E: "dict" is not subscriptable, use "typing.Dict" instead - -t10: type -t11: type[int] # E: "type" expects no type arguments, but 1 given -[builtins fixtures/dict.pyi] - - -[case testGenericBuiltinSetWarning] -# flags: --python-version 3.8 -t1: set -t2: set[int] # E: "set" is not subscriptable, use "typing.Set" instead -[builtins fixtures/set.pyi] - - -[case testGenericCollectionsWarning] -# flags: --python-version 3.8 -import collections - -t01: collections.deque -t02: collections.deque[int] # E: "deque" is not subscriptable, use "typing.Deque" instead -t03: collections.defaultdict -t04: collections.defaultdict[int, str] # E: "defaultdict" is not subscriptable, use "typing.DefaultDict" instead -t05: collections.OrderedDict -t06: collections.OrderedDict[int, str] -t07: collections.Counter -t08: collections.Counter[int] # E: "Counter" is not subscriptable, use "typing.Counter" instead -t09: collections.ChainMap -t10: collections.ChainMap[int, str] # E: "ChainMap" is not subscriptable, use "typing.ChainMap" instead - - [case testGenericBuiltinFutureAnnotations] from __future__ import annotations t1: list @@ -80,7 +37,6 @@ t10: collections.ChainMap[int, str] [case testGenericAliasBuiltinsReveal] -# flags: --python-version 3.9 t1: list t2: list[int] t3: list[str] @@ -101,19 +57,18 @@ reveal_type(t2) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(t3) # N: Revealed type is "builtins.list[builtins.str]" reveal_type(t4) # N: Revealed type is "builtins.tuple[Any, ...]" # TODO: ideally these would reveal builtins.tuple -reveal_type(t5) # N: Revealed type is "Tuple[builtins.int]" -reveal_type(t6) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(t5) # N: Revealed type is "tuple[builtins.int]" +reveal_type(t6) # N: Revealed type is "tuple[builtins.int, builtins.str]" # TODO: this is incorrect, see #9522 reveal_type(t7) # N: Revealed type is "builtins.tuple[builtins.int, ...]" reveal_type(t8) # N: Revealed type is "builtins.dict[Any, Any]" reveal_type(t9) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" reveal_type(t10) # N: Revealed type is "builtins.type" -reveal_type(t11) # N: Revealed type is "Type[builtins.int]" +reveal_type(t11) # N: Revealed type is "type[builtins.int]" [builtins fixtures/dict.pyi] [case testGenericAliasBuiltinsSetReveal] -# flags: --python-version 3.9 t1: set t2: set[int] t3: set[str] @@ -125,7 +80,6 @@ reveal_type(t3) # N: Revealed type is "builtins.set[builtins.str]" [case testGenericAliasCollectionsReveal] -# flags: --python-version 3.9 import collections t1: collections.deque[int] @@ -143,7 +97,6 @@ reveal_type(t5) # N: Revealed type is "collections.ChainMap[builtins.int, built [case testGenericAliasCollectionsABCReveal] -# flags: --python-version 3.9 import collections.abc t01: collections.abc.Awaitable[int] @@ -196,6 +149,38 @@ t23: collections.abc.ValuesView[str] # reveal_type(t23) # Nx Revealed type is "collections.abc.ValuesView[builtins.str]" [builtins fixtures/tuple.pyi] +[case testGenericAliasIsinstanceUnreachable] +# flags: --warn-unreachable --python-version 3.10 +from collections.abc import Iterable + +class A: ... + +def test(dependencies: list[A] | None) -> None: + if dependencies is None: + dependencies = [] + elif not isinstance(dependencies, Iterable): + dependencies = [dependencies] # E: Statement is unreachable + +[builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-full.pyi] + +[case testGenericAliasRedundantExprCompoundIfExpr] +# flags: --warn-unreachable --enable-error-code=redundant-expr --python-version 3.10 + +from typing import Any, reveal_type +from collections.abc import Iterable + +def test_example(x: Iterable[Any]) -> None: + if isinstance(x, Iterable) and not isinstance(x, str): # E: Left operand of "and" is always true + reveal_type(x) # N: Revealed type is "typing.Iterable[Any]" + +def test_counterexample(x: Any) -> None: + if isinstance(x, Iterable) and not isinstance(x, str): + reveal_type(x) # N: Revealed type is "typing.Iterable[Any]" + +[builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-full.pyi] + [case testGenericBuiltinTupleTyping] from typing import Tuple @@ -213,8 +198,6 @@ t09: Tuple[int, ...] = (1, 2, 3) [case testGenericBuiltinTuple] -# flags: --python-version 3.9 - t01: tuple = () t02: tuple[int] = (1, ) t03: tuple[int, str] = (1, 'a') @@ -230,16 +213,14 @@ t10: Tuple[int, ...] = t09 [builtins fixtures/tuple.pyi] [case testTypeAliasWithBuiltinTuple] -# flags: --python-version 3.9 - A = tuple[int, ...] a: A = () b: A = (1, 2, 3) -c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[int, ...]") +c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[int, ...]") B = tuple[int, str] x: B = (1, 'x') -y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "Tuple[str, int]", variable has type "Tuple[int, str]") +y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "tuple[str, int]", variable has type "tuple[int, str]") reveal_type(tuple[int, ...]()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] @@ -247,7 +228,7 @@ reveal_type(tuple[int, ...]()) # N: Revealed type is "builtins.tuple[builtins.i [case testTypeAliasWithBuiltinTupleInStub] import m reveal_type(m.a) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(m.b) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(m.b) # N: Revealed type is "tuple[builtins.int, builtins.str]" [file m.pyi] A = tuple[int, ...] @@ -261,7 +242,7 @@ import m reveal_type(m.a) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(m.b) # N: Revealed type is "builtins.list[builtins.list[builtins.int]]" m.C # has complex representation, ignored -reveal_type(m.d) # N: Revealed type is "Type[builtins.str]" +reveal_type(m.d) # N: Revealed type is "type[builtins.str]" [file m.pyi] A = list[int] diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 90180e0f83f6a..ee5bcccf8aceb 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -279,9 +279,9 @@ class C(A): [out] main:11: error: Signature of "f" incompatible with supertype "A" main:11: note: Superclass: -main:11: note: def [T, S] f(self, x: List[T], y: List[S]) -> None +main:11: note: def [T, S] f(self, x: list[T], y: list[S]) -> None main:11: note: Subclass: -main:11: note: def [T] f(self, x: List[T], y: List[T]) -> None +main:11: note: def [T] f(self, x: list[T], y: list[T]) -> None [case testOverrideGenericMethodInNonGenericClassGeneralize] from typing import TypeVar @@ -753,6 +753,20 @@ s, s = Nums() # E: Incompatible types in assignment (expression has type "int", [builtins fixtures/for.pyi] [out] +[case testUninhabitedCacheChecksAmbiguous] +# https://github.com/python/mypy/issues/19641 +from typing import Mapping, Never, TypeVar + +M = TypeVar("M", bound=Mapping[str,object]) + +def get(arg: M, /) -> M: + return arg + +get({}) + +def upcast(d: dict[Never, Never]) -> Mapping[str, object]: + return d # E: Incompatible return value type (got "dict[Never, Never]", expected "Mapping[str, object]") +[builtins fixtures/dict.pyi] -- Variance -- -------- @@ -1065,3 +1079,158 @@ class F(E[T_co], Generic[T_co]): ... # E: Variance of TypeVar "T_co" incompatib class G(Generic[T]): ... class H(G[T_contra], Generic[T_contra]): ... # E: Variance of TypeVar "T_contra" incompatible with variance in parent type + +[case testParameterizedGenericOverrideWithProperty] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class A(Generic[T]): + def __init__(self, val: T): + self.member: T = val + +class B(A[str]): + member: str + +class GoodPropertyOverride(A[str]): + @property + def member(self) -> str: ... + @member.setter + def member(self, val: str): ... + +class BadPropertyOverride(A[str]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: int + def member(self) -> int: ... + @member.setter + def member(self, val: int): ... + +class BadGenericPropertyOverride(A[str], Generic[T]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: T + def member(self) -> T: ... + @member.setter + def member(self, val: T): ... +[builtins fixtures/property.pyi] + +[case testParameterizedGenericPropertyOverrideWithProperty] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class A(Generic[T]): + @property + def member(self) -> T: ... + @member.setter + def member(self, val: T): ... + +class B(A[str]): + member: str + +class GoodPropertyOverride(A[str]): + @property + def member(self) -> str: ... + @member.setter + def member(self, val: str): ... + +class BadPropertyOverride(A[str]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: int + def member(self) -> int: ... + @member.setter + def member(self, val: int): ... + +class BadGenericPropertyOverride(A[str], Generic[T]): + @property # E: Signature of "member" incompatible with supertype "A" \ + # N: Superclass: \ + # N: str \ + # N: Subclass: \ + # N: T + def member(self) -> T: ... + @member.setter + def member(self, val: T): ... +[builtins fixtures/property.pyi] + +[case testParameterizedGenericOverrideSelfWithProperty] +from typing_extensions import Self + +class A: + def __init__(self, val: Self): + self.member: Self = val + +class GoodPropertyOverride(A): + @property + def member(self) -> "GoodPropertyOverride": ... + @member.setter + def member(self, val: "GoodPropertyOverride"): ... + +class GoodPropertyOverrideSelf(A): + @property + def member(self) -> Self: ... + @member.setter + def member(self, val: Self): ... +[builtins fixtures/property.pyi] + +[case testParameterizedGenericOverrideWithSelfProperty] +from typing import TypeVar, Generic +from typing_extensions import Self + +T = TypeVar("T") + +class A(Generic[T]): + def __init__(self, val: T): + self.member: T = val + +class B(A["B"]): + member: Self + +class GoodPropertyOverride(A["GoodPropertyOverride"]): + @property + def member(self) -> Self: ... + @member.setter + def member(self, val: Self): ... +[builtins fixtures/property.pyi] + +[case testMultipleInheritanceCompatibleTypeVar] +from typing import Generic, TypeVar + +T = TypeVar("T") +U = TypeVar("U") + +class A(Generic[T]): + x: T + def fn(self, t: T) -> None: ... + +class A2(A[T]): + y: str + z: str + +class B(Generic[T]): + x: T + def fn(self, t: T) -> None: ... + +class C1(A2[str], B[str]): pass +class C2(A2[str], B[int]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" +class C3(A2[T], B[T]): pass +class C4(A2[U], B[U]): pass +class C5(A2[U], B[T]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" + +class D1(A[str], B[str]): pass +class D2(A[str], B[int]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" +class D3(A[T], B[T]): pass +class D4(A[U], B[U]): pass +class D5(A[U], B[T]): pass # E: Definition of "fn" in base class "A" is incompatible with definition in base class "B" \ + # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b8cc0422b749f..3b535ab4a1c04 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -458,7 +458,7 @@ import types a: A class A: pass a[A]() # E: Value of type "A" is not indexable -A[A]() # E: The type "Type[A]" is not generic and not indexable +A[A]() # E: The type "type[A]" is not generic and not indexable [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] @@ -516,7 +516,7 @@ Alias[int]("a") # E: Argument 1 to "Node" has incompatible type "str"; expected [case testTypeApplicationCrash] import types -type[int] # this was crashing, see #2302 (comment) # E: The type "Type[type]" is not generic and not indexable +type[int] [builtins fixtures/tuple.pyi] @@ -584,7 +584,7 @@ def func2(x: SameNode[T]) -> SameNode[T]: return x reveal_type(func2) # N: Revealed type is "def [T] (x: __main__.Node[T`-1, T`-1]) -> __main__.Node[T`-1, T`-1]" -func2(Node(1, 'x')) # E: Cannot infer type argument 1 of "func2" +func2(Node(1, 'x')) # E: Cannot infer value of type parameter "T" of "func2" y = func2(Node('x', 'x')) reveal_type(y) # N: Revealed type is "__main__.Node[builtins.str, builtins.str]" @@ -671,7 +671,7 @@ reveal_type(a) # N: Revealed type is "other.array[Any, other.dtype[builtins.floa [out] main:3: error: Type argument "float" of "Array" must be a subtype of "generic" [type-var] a: other.Array[float] - ^ + ^ [file other.py] from typing import Any, Generic, TypeVar @@ -713,7 +713,7 @@ reveal_type(z) # N: Revealed type is "__main__.Node[Any, Any]" [out] -[case testGenericTypeAliasesAcessingMethods] +[case testGenericTypeAliasesAccessingMethods] from typing import TypeVar, Generic, List T = TypeVar('T') class Node(Generic[T]): @@ -729,7 +729,7 @@ l.meth().append(1) reveal_type(l.meth()) # N: Revealed type is "builtins.list[builtins.int]" l.meth().append('x') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" -ListedNode[str]([]).x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "List[str]") +ListedNode[str]([]).x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "list[str]") [builtins fixtures/list.pyi] @@ -751,10 +751,10 @@ def f_bad(x: T) -> D[T]: return D(1) # Error, see out L[int]().append(Node((1, 1))) -L[int]().append(5) # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "Node[Tuple[int, int]]" +L[int]().append(5) # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "Node[tuple[int, int]]" x = D((1, 1)) # type: D[int] -y = D(5) # type: D[int] # E: Argument 1 to "D" has incompatible type "int"; expected "Tuple[int, int]" +y = D(5) # type: D[int] # E: Argument 1 to "D" has incompatible type "int"; expected "tuple[int, int]" def f(x: T) -> D[T]: return D((x, x)) @@ -762,7 +762,7 @@ reveal_type(f('a')) # N: Revealed type is "__main__.D[builtins.str]" [builtins fixtures/list.pyi] [out] -main:15: error: Argument 1 to "D" has incompatible type "int"; expected "Tuple[T, T]" +main:15: error: Argument 1 to "D" has incompatible type "int"; expected "tuple[T, T]" [case testGenericTypeAliasesSubclassingBad] @@ -838,8 +838,8 @@ reveal_type(x) # N: Revealed type is "builtins.int" def f2(x: IntTP[T]) -> IntTP[T]: return x -f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "Tuple[int, Never]" -reveal_type(f2((1, 'x'))) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "tuple[int, int, int]"; expected "tuple[int, Never]" +reveal_type(f2((1, 'x'))) # N: Revealed type is "tuple[builtins.int, builtins.str]" [builtins fixtures/for.pyi] @@ -878,8 +878,8 @@ T = TypeVar('T', int, bool) Vec = List[Tuple[T, T]] vec = [] # type: Vec[bool] -vec.append('x') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "Tuple[bool, bool]" -reveal_type(vec[0]) # N: Revealed type is "Tuple[builtins.bool, builtins.bool]" +vec.append('x') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "tuple[bool, bool]" +reveal_type(vec[0]) # N: Revealed type is "tuple[builtins.bool, builtins.bool]" def fun1(v: Vec[T]) -> T: return v[0][0] @@ -887,10 +887,10 @@ def fun2(v: Vec[T], scale: T) -> Vec[T]: return v reveal_type(fun1([(1, 1)])) # N: Revealed type is "builtins.int" -fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "List[Tuple[bool, bool]]" -fun1([(1, 'x')]) # E: Cannot infer type argument 1 of "fun1" +fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "list[tuple[bool, bool]]" +fun1([(1, 'x')]) # E: Cannot infer value of type parameter "T" of "fun1" -reveal_type(fun2([(1, 1)], 1)) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int]]" +reveal_type(fun2([(1, 1)], 1)) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int]]" fun2([('x', 'x')], 'x') # E: Value of type variable "T" of "fun2" cannot be "str" [builtins fixtures/list.pyi] @@ -903,13 +903,13 @@ T = TypeVar('T') n: TupledNode[int] n.x = 1 n.y = (1, 1) -n.y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int, int]") +n.y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, int]") def f(x: Node[T, T]) -> TupledNode[T]: return Node(x.x, (x.x, x.x)) f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Node[Never, Never]" -f(Node(1, 'x')) # E: Cannot infer type argument 1 of "f" +f(Node(1, 'x')) # E: Cannot infer value of type parameter "T" of "f" reveal_type(Node('x', 'x')) # N: Revealed type is "a.Node[builtins.str, builtins.str]" [file a.py] @@ -935,7 +935,7 @@ def int_tf(m: int) -> Transform[int, str]: return transform var: Transform[int, str] -reveal_type(var) # N: Revealed type is "def (builtins.int, builtins.int) -> Tuple[builtins.int, builtins.str]" +reveal_type(var) # N: Revealed type is "def (builtins.int, builtins.int) -> tuple[builtins.int, builtins.str]" [file lib.py] from typing import Callable, TypeVar, Tuple @@ -966,9 +966,9 @@ NewAlias = Alias[int, int, S, S] class C: pass x: NewAlias[str] -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, builtins.str, builtins.str]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int, builtins.str, builtins.str]]" y: Alias[int, str, C, C] -reveal_type(y) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, __main__.C, __main__.C]]" +reveal_type(y) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str, __main__.C, __main__.C]]" [file mod.py] from typing import TypeVar, List, Tuple @@ -1019,7 +1019,7 @@ class C: if int(): a = B if int(): - b = int # E: Cannot assign multiple types to name "b" without an explicit "Type[...]" annotation + b = int # E: Cannot assign multiple types to name "b" without an explicit "type[...]" annotation if int(): c = int def f(self, x: a) -> None: pass # E: Variable "__main__.C.a" is not valid as a type \ @@ -1129,10 +1129,10 @@ Bad = A[int] # type: ignore reveal_type(Bad) # N: Revealed type is "Any" [out] -[case testNoSubscriptionOfBuiltinAliases] +[case testSubscriptionOfBuiltinAliases] from typing import List, TypeVar -list[int]() # E: "list" is not subscriptable +list[int]() ListAlias = List def fun() -> ListAlias[int]: @@ -1141,11 +1141,10 @@ def fun() -> ListAlias[int]: reveal_type(fun()) # N: Revealed type is "builtins.list[builtins.int]" BuiltinAlias = list -BuiltinAlias[int]() # E: "list" is not subscriptable +BuiltinAlias[int]() -#check that error is reported only once, and type is still stored T = TypeVar('T') -BadGenList = list[T] # E: "list" is not subscriptable +BadGenList = list[T] reveal_type(BadGenList[int]()) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(BadGenList()) # N: Revealed type is "builtins.list[Any]" @@ -1224,7 +1223,7 @@ class C(A[S, B[T, int]], B[U, A[int, T]]): pass c = C[object, int, str]() -reveal_type(c.m()) # N: Revealed type is "Tuple[builtins.str, __main__.A[builtins.int, builtins.int]]" +reveal_type(c.m()) # N: Revealed type is "tuple[builtins.str, __main__.A[builtins.int, builtins.int]]" [builtins fixtures/tuple.pyi] [out] @@ -1771,8 +1770,7 @@ T = TypeVar('T') class C(Generic[T]): def __init__(self) -> None: pass x = C # type: Callable[[], C[int]] -y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type "Type[C[Any]]", variable has type "Callable[[], int]") - +y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type "type[C[T]]", variable has type "Callable[[], int]") -- Special cases -- ------------- @@ -1966,8 +1964,8 @@ class C(Generic[T]): class D(C[Tuple[T, T]]): ... class E(D[str]): ... -reveal_type(E.get()) # N: Revealed type is "Tuple[builtins.str, builtins.str]" -reveal_type(E().get()) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(E.get()) # N: Revealed type is "tuple[builtins.str, builtins.str]" +reveal_type(E().get()) # N: Revealed type is "tuple[builtins.str, builtins.str]" [builtins fixtures/classmethod.pyi] [case testGenericClassMethodExpansionReplacingTypeVar] @@ -2015,10 +2013,10 @@ class C(Generic[T]): class D(C[Tuple[T, S]]): ... class E(D[S, str]): ... -reveal_type(D.make_one) # N: Revealed type is "def [T, S] (x: Tuple[T`1, S`2]) -> __main__.C[Tuple[T`1, S`2]]" -reveal_type(D[int, str].make_one) # N: Revealed type is "def (x: Tuple[builtins.int, builtins.str]) -> __main__.C[Tuple[builtins.int, builtins.str]]" -reveal_type(E.make_one) # N: Revealed type is "def [S] (x: Tuple[S`1, builtins.str]) -> __main__.C[Tuple[S`1, builtins.str]]" -reveal_type(E[int].make_one) # N: Revealed type is "def (x: Tuple[builtins.int, builtins.str]) -> __main__.C[Tuple[builtins.int, builtins.str]]" +reveal_type(D.make_one) # N: Revealed type is "def [T, S] (x: tuple[T`1, S`2]) -> __main__.C[tuple[T`1, S`2]]" +reveal_type(D[int, str].make_one) # N: Revealed type is "def (x: tuple[builtins.int, builtins.str]) -> __main__.C[tuple[builtins.int, builtins.str]]" +reveal_type(E.make_one) # N: Revealed type is "def [S] (x: tuple[S`1, builtins.str]) -> __main__.C[tuple[S`1, builtins.str]]" +reveal_type(E[int].make_one) # N: Revealed type is "def (x: tuple[builtins.int, builtins.str]) -> __main__.C[tuple[builtins.int, builtins.str]]" [builtins fixtures/classmethod.pyi] [case testGenericClassClsNonGeneric] @@ -2163,7 +2161,7 @@ class Sub(Base[str]): ... Sub.make_some(1) # E: No overload variant of "make_some" of "Base" matches argument type "int" \ # N: Possible overload variants: \ # N: def make_some(cls, item: str) -> Sub \ - # N: def make_some(cls, item: str, n: int) -> Tuple[Sub, ...] + # N: def make_some(cls, item: str, n: int) -> tuple[Sub, ...] [builtins fixtures/classmethod.pyi] [case testNoGenericAccessOnImplicitAttributes] @@ -2193,11 +2191,11 @@ class A(Generic[T]): class B(A[T], Generic[T, S]): def meth(self) -> None: - reveal_type(A[T].foo) # N: Revealed type is "def () -> Tuple[T`1, __main__.A[T`1]]" + reveal_type(A[T].foo) # N: Revealed type is "def () -> tuple[T`1, __main__.A[T`1]]" @classmethod def other(cls) -> None: - reveal_type(cls.foo) # N: Revealed type is "def () -> Tuple[T`1, __main__.B[T`1, S`2]]" -reveal_type(B.foo) # N: Revealed type is "def [T, S] () -> Tuple[T`1, __main__.B[T`1, S`2]]" + reveal_type(cls.foo) # N: Revealed type is "def () -> tuple[T`1, __main__.B[T`1, S`2]]" +reveal_type(B.foo) # N: Revealed type is "def [T, S] () -> tuple[T`1, __main__.B[T`1, S`2]]" [builtins fixtures/classmethod.pyi] [case testGenericClassAlternativeConstructorPrecise2] @@ -2213,7 +2211,7 @@ class Base(Generic[T]): class Sub(Base[T]): ... -reveal_type(Sub.make_pair('yes')) # N: Revealed type is "Tuple[__main__.Sub[builtins.str], __main__.Sub[builtins.str]]" +reveal_type(Sub.make_pair('yes')) # N: Revealed type is "tuple[__main__.Sub[builtins.str], __main__.Sub[builtins.str]]" Sub[int].make_pair('no') # E: Argument 1 to "make_pair" of "Base" has incompatible type "str"; expected "int" [builtins fixtures/classmethod.pyi] @@ -2752,7 +2750,6 @@ reveal_type(func(1)) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] [case testGenericLambdaGenericMethodNoCrash] -# flags: --new-type-inference from typing import TypeVar, Union, Callable, Generic S = TypeVar("S") @@ -2791,7 +2788,6 @@ reveal_type(dict2) # N: Revealed type is "builtins.dict[Any, __main__.B]" -- ------------------------------------------------------------------ [case testInferenceAgainstGenericCallable] -# flags: --new-type-inference from typing import TypeVar, Callable, List X = TypeVar('X') @@ -2809,7 +2805,6 @@ reveal_type(bar(id)) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableNoLeak] -# flags: --new-type-inference from typing import TypeVar, Callable T = TypeVar('T') @@ -2825,7 +2820,6 @@ reveal_type(f(tpl)) # N: Revealed type is "Any" [out] [case testInferenceAgainstGenericCallableChain] -# flags: --new-type-inference from typing import TypeVar, Callable, List X = TypeVar('X') @@ -2838,7 +2832,6 @@ reveal_type(chain(id, id)) # N: Revealed type is "def (builtins.int) -> builtin [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGeneric] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2859,7 +2852,6 @@ reveal_type(same(42)) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericReverse] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2880,7 +2872,6 @@ reveal_type(same([42])) # N: Revealed type is "builtins.int" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericArg] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2901,7 +2892,6 @@ reveal_type(single(42)) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericChain] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2915,7 +2905,6 @@ reveal_type(comb(id, id)) # N: Revealed type is "def [T] (T`1) -> T`1" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericNonLinear] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2931,12 +2920,11 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]: def id(__x: U) -> U: ... fs = [id, id, id] -reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`11) -> builtins.list[S`11]" -reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`13) -> builtins.list[S`13]" +reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`2) -> builtins.list[S`2]" +reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCurry] -# flags: --new-type-inference from typing import Callable, List, TypeVar S = TypeVar("S") @@ -2955,7 +2943,6 @@ reveal_type(dec2(test2)) # N: Revealed type is "def [T] (T`3) -> def (T`3) -> T [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableNewVariable] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2970,7 +2957,6 @@ reveal_type(dec(test)) # N: Revealed type is "def [U] (builtins.list[U`-1]) -> [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericAlias] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -2988,7 +2974,6 @@ reveal_type(dec(id)) # N: Revealed type is "def [S] (S`1) -> builtins.list[S`1] [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCallableGenericProtocol] -# flags: --new-type-inference from typing import TypeVar, Protocol, Generic, Optional T = TypeVar('T') @@ -3004,7 +2989,6 @@ reveal_type(lift(g)) # N: Revealed type is "def [T] (Union[T`1, None]) -> Union [builtins fixtures/list.pyi] [case testInferenceAgainstGenericSplitOrder] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -3019,7 +3003,6 @@ reveal_type(dec(id, id)) # N: Revealed type is "def (builtins.int) -> builtins. [builtins fixtures/list.pyi] [case testInferenceAgainstGenericSplitOrderGeneric] -# flags: --new-type-inference from typing import TypeVar, Callable, Tuple S = TypeVar('S') @@ -3031,11 +3014,25 @@ def dec(f: Callable[[T], S], g: Callable[[T], U]) -> Callable[[T], Tuple[S, U]]: def id(x: V) -> V: ... -reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`1) -> Tuple[T`1, T`1]" +reveal_type(dec(id, id)) # N: Revealed type is "def [T] (T`1) -> tuple[T`1, T`1]" +[builtins fixtures/tuple.pyi] + +[case testInferenceAgainstGenericSecondary] +from typing import TypeVar, Callable, List + +S = TypeVar('S') +T = TypeVar('T') +U = TypeVar('U') + +def dec(f: Callable[[List[T]], List[int]]) -> Callable[[T], T]: ... + +@dec +def id(x: U) -> U: + ... +reveal_type(id) # N: Revealed type is "def (builtins.int) -> builtins.int" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericEllipsisSelfSpecialCase] -# flags: --new-type-inference from typing import Self, Callable, TypeVar T = TypeVar("T") @@ -3049,7 +3046,6 @@ c: C reveal_type(c.test()) # N: Revealed type is "__main__.C" [case testInferenceAgainstGenericBoundsAndValues] -# flags: --new-type-inference from typing import TypeVar, Callable, List class B: ... @@ -3077,7 +3073,6 @@ reveal_type(dec2(id2)) # N: Revealed type is "def (Never) -> builtins.list[Neve # E: Argument 1 to "dec2" has incompatible type "Callable[[V], V]"; expected "Callable[[Never], Never]" [case testInferenceAgainstGenericLambdas] -# flags: --new-type-inference from typing import TypeVar, Callable, List S = TypeVar('S') @@ -3105,16 +3100,15 @@ def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]: reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]" reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`8) -> S`8" -reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`12) -> S`12" +reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`11) -> S`11" reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]" -reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]" -reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24" -dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "List[T]" +reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`19) -> builtins.list[S`19]" +reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`23]) -> T`23" +dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "list[T]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecBasicInList] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple from typing_extensions import ParamSpec @@ -3129,11 +3123,10 @@ def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... reveal_type(dec(id)) # N: Revealed type is "def [T] (x: T`3) -> builtins.list[T`3]" reveal_type(dec(either)) # N: Revealed type is "def [T] (x: T`5, y: T`5) -> builtins.list[T`5]" -reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" +reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (x: U`-1, y: V`-2) -> builtins.list[tuple[U`-1, V`-2]]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecBasicDeList] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple from typing_extensions import ParamSpec @@ -3150,7 +3143,6 @@ reveal_type(dec(either)) # N: Revealed type is "def [T] (x: builtins.list[T`5], [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecPopOff] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple from typing_extensions import ParamSpec, Concatenate @@ -3166,12 +3158,11 @@ def either(x: U, y: U) -> U: ... def pair(x: U, y: V) -> Tuple[U, V]: ... reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`2) -> T`2" reveal_type(dec(either)) # N: Revealed type is "def [T] (y: T`5) -> def (T`5) -> T`5" -reveal_type(dec(pair)) # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`8) -> Tuple[T`8, V`-2]" +reveal_type(dec(pair)) # N: Revealed type is "def [V] (y: V`-2) -> def [T] (T`8) -> tuple[T`8, V`-2]" reveal_type(dec(dec)) # N: Revealed type is "def () -> def [T, P, S] (def (T`-1, *P.args, **P.kwargs) -> S`-3) -> def (*P.args, **P.kwargs) -> def (T`-1) -> S`-3" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecPopOn] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple from typing_extensions import ParamSpec, Concatenate @@ -3187,13 +3178,12 @@ def either(x: U) -> Callable[[U], U]: ... def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> T`3" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`6, x: T`6) -> T`6" -reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, x: U`-1) -> Tuple[T`9, U`-1]" +reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, x: U`-1) -> tuple[T`9, U`-1]" # This is counter-intuitive but looks correct, dec matches itself only if P can be empty reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`13, f: def () -> def (T`13) -> S`14) -> S`14" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecVsParamSpec] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple, Generic from typing_extensions import ParamSpec, Concatenate @@ -3214,7 +3204,6 @@ reveal_type(dec(h)) # N: Revealed type is "def [T, Q] (T`-1, *Q.args, **Q.kwarg [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecVsParamSpecConcatenate] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple, Generic from typing_extensions import ParamSpec, Concatenate @@ -3232,7 +3221,6 @@ reveal_type(dec(h)) # N: Revealed type is "def [T, Q] (T`-1, *Q.args, **Q.kwarg [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecSecondary] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple, Generic from typing_extensions import ParamSpec, Concatenate @@ -3250,7 +3238,6 @@ reveal_type(dec(g)) # N: Revealed type is "def (builtins.int) -> __main__.Foo[[ [builtins fixtures/list.pyi] [case testInferenceAgainstGenericParamSpecSecondOrder] -# flags: --new-type-inference from typing import TypeVar, Callable from typing_extensions import ParamSpec, Concatenate @@ -3272,7 +3259,6 @@ reveal_type(transform(dec2)) # N: Revealed type is "def [W, T] (def (builtins.i [builtins fixtures/tuple.pyi] [case testNoAccidentalVariableClashInNestedGeneric] -# flags: --new-type-inference from typing import TypeVar, Callable, Generic, Tuple T = TypeVar('T') @@ -3289,7 +3275,6 @@ def apply(a: S, b: T) -> None: [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericParamSpecSpuriousBoundsNotUsed] -# flags: --new-type-inference from typing import TypeVar, Callable, Generic from typing_extensions import ParamSpec, Concatenate @@ -3308,7 +3293,6 @@ reveal_type(test) # N: Revealed type is "def () -> def [Q] (__main__.Foo[Q`-1]) [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicBasicInList] -# flags: --new-type-inference from typing import Tuple, TypeVar, List, Callable from typing_extensions import Unpack, TypeVarTuple @@ -3324,11 +3308,10 @@ def pair(x: U, y: V) -> Tuple[U, V]: ... reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5, T`5) -> builtins.list[T`5]" -reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[Tuple[U`-1, V`-2]]" +reveal_type(dec(pair)) # N: Revealed type is "def [U, V] (U`-1, V`-2) -> builtins.list[tuple[U`-1, V`-2]]" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicBasicDeList] -# flags: --new-type-inference from typing import Tuple, TypeVar, List, Callable from typing_extensions import Unpack, TypeVarTuple @@ -3346,7 +3329,6 @@ reveal_type(dec(either)) # N: Revealed type is "def [T] (builtins.list[T`5], bu [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicPopOff] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple from typing_extensions import Unpack, TypeVarTuple @@ -3363,12 +3345,11 @@ def pair(x: U, y: V) -> Tuple[U, V]: ... reveal_type(dec(id)) # N: Revealed type is "def () -> def [T] (T`2) -> T`2" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`5) -> def (T`5) -> T`5" -reveal_type(dec(pair)) # N: Revealed type is "def [V] (V`-2) -> def [T] (T`8) -> Tuple[T`8, V`-2]" +reveal_type(dec(pair)) # N: Revealed type is "def [V] (V`-2) -> def [T] (T`8) -> tuple[T`8, V`-2]" reveal_type(dec(dec)) # N: Revealed type is "def () -> def [T, Ts, S] (def (T`-1, *Unpack[Ts`-2]) -> S`-3) -> def (*Unpack[Ts`-2]) -> def (T`-1) -> S`-3" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicPopOn] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Tuple from typing_extensions import Unpack, TypeVarTuple @@ -3385,13 +3366,12 @@ def pair(x: U) -> Callable[[V], Tuple[V, U]]: ... reveal_type(dec(id)) # N: Revealed type is "def [T] (T`3) -> T`3" reveal_type(dec(either)) # N: Revealed type is "def [T] (T`6, T`6) -> T`6" -reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, U`-1) -> Tuple[T`9, U`-1]" +reveal_type(dec(pair)) # N: Revealed type is "def [T, U] (T`9, U`-1) -> tuple[T`9, U`-1]" # This is counter-intuitive but looks correct, dec matches itself only if Ts is empty reveal_type(dec(dec)) # N: Revealed type is "def [T, S] (T`13, def () -> def (T`13) -> S`14) -> S`14" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicVsVariadic] -# flags: --new-type-inference from typing import TypeVar, Callable, List, Generic from typing_extensions import Unpack, TypeVarTuple @@ -3411,7 +3391,6 @@ reveal_type(dec(g)) # N: Revealed type is "def [Ts] (*Unpack[Ts`4]) -> builtins [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicVsVariadicConcatenate] -# flags: --new-type-inference from typing import TypeVar, Callable, Generic from typing_extensions import Unpack, TypeVarTuple @@ -3430,7 +3409,6 @@ reveal_type(dec(h)) # N: Revealed type is "def [T, Us] (T`-1, *Unpack[Us`-2]) - [builtins fixtures/list.pyi] [case testInferenceAgainstGenericVariadicSecondary] -# flags: --new-type-inference from typing import TypeVar, Callable, Generic from typing_extensions import Unpack, TypeVarTuple @@ -3547,3 +3525,100 @@ def foo(x: T): reveal_type(C) # N: Revealed type is "Overload(def [T, S] (x: builtins.int, y: S`-1) -> __main__.C[__main__.Int[S`-1]], def [T, S] (x: builtins.str, y: S`-1) -> __main__.C[__main__.Str[S`-1]])" reveal_type(C(0, x)) # N: Revealed type is "__main__.C[__main__.Int[T`-1]]" reveal_type(C("yes", x)) # N: Revealed type is "__main__.C[__main__.Str[T`-1]]" + +[case testInstanceMethodBoundOnClass] +from typing import TypeVar, Generic + +T = TypeVar("T") +class B(Generic[T]): + def foo(self) -> T: ... +class C(B[T]): ... +class D(C[int]): ... + +reveal_type(B.foo) # N: Revealed type is "def [T] (self: __main__.B[T`1]) -> T`1" +reveal_type(B[int].foo) # N: Revealed type is "def (self: __main__.B[builtins.int]) -> builtins.int" +reveal_type(C.foo) # N: Revealed type is "def [T] (self: __main__.B[T`1]) -> T`1" +reveal_type(C[int].foo) # N: Revealed type is "def (self: __main__.B[builtins.int]) -> builtins.int" +reveal_type(D.foo) # N: Revealed type is "def (self: __main__.B[builtins.int]) -> builtins.int" + +[case testDeterminismFromJoinOrderingInSolver] +# Used to fail non-deterministically +# https://github.com/python/mypy/issues/19121 +from __future__ import annotations +from typing import Generic, Iterable, Iterator, Self, TypeVar + +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_T_co = TypeVar("_T_co", covariant=True) + +class Base(Iterable[_T1]): + def __iter__(self) -> Iterator[_T1]: ... +class A(Base[_T1]): ... +class B(Base[_T1]): ... +class C(Base[_T1]): ... +class D(Base[_T1]): ... +class E(Base[_T1]): ... + +class zip2(Generic[_T_co]): + def __new__( + cls, + iter1: Iterable[_T1], + iter2: Iterable[_T2], + iter3: Iterable[_T3], + ) -> zip2[tuple[_T1, _T2, _T3]]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +def draw( + colors1: A[str] | B[str] | C[int] | D[int | str], + colors2: A[str] | B[str] | C[int] | D[int | str], + colors3: A[str] | B[str] | C[int] | D[int | str], +) -> None: + for c1, c2, c3 in zip2(colors1, colors2, colors3): + reveal_type(c1) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(c2) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(c3) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def takes_int_str_none(x: int | str | None) -> None: ... + +def draw_none( + colors1: A[str] | B[str] | C[int] | D[None], + colors2: A[str] | B[str] | C[int] | D[None], + colors3: A[str] | B[str] | C[int] | D[None], +) -> None: + for c1, c2, c3 in zip2(colors1, colors2, colors3): + # TODO: can't do reveal type because the union order is not deterministic + takes_int_str_none(c1) + takes_int_str_none(c2) + takes_int_str_none(c3) +[builtins fixtures/tuple.pyi] + +[case testPropertyWithGenericSetter] +from typing import TypeVar + +class B: ... +class C(B): ... +T = TypeVar("T", bound=B) + +class Test: + @property + def foo(self) -> list[C]: ... + @foo.setter + def foo(self, val: list[T]) -> None: ... + +t1: Test +t2: Test + +lb: list[B] +lc: list[C] +li: list[int] + +t1.foo = lb +t1.foo = lc +t1.foo = li # E: Value of type variable "T" of "foo" of "Test" cannot be "int" + +t2.foo = [B()] +t2.foo = [C()] +t2.foo = [1] # E: Value of type variable "T" of "foo" of "Test" cannot be "int" +[builtins fixtures/property.pyi] diff --git a/test-data/unit/check-ignore.test b/test-data/unit/check-ignore.test index fa451f373e704..d0f6bb6aeb603 100644 --- a/test-data/unit/check-ignore.test +++ b/test-data/unit/check-ignore.test @@ -38,7 +38,7 @@ from m import a # type: ignore [file m.py] + [out] -tmp/m.py:1: error: invalid syntax +tmp/m.py:1: error: Invalid syntax [case testIgnoreAppliesOnlyToMissing] import a # type: ignore @@ -59,7 +59,7 @@ from m import * # type: ignore [file m.py] + [out] -tmp/m.py:1: error: invalid syntax +tmp/m.py:1: error: Invalid syntax [case testIgnoreAssignmentTypeError] x = 1 @@ -275,6 +275,16 @@ class CD(six.with_metaclass(M)): # E: Multiple metaclass definitions [builtins fixtures/tuple.pyi] +[case testUnusedIgnoreCodeOrder] +# flags: --warn-unused-ignores +5 # type: ignore[import, steven] # E: Unused "type: ignore[import, steven]" comment +-- User ordering of codes is preserved +5 # type: ignore[steven, import] # E: Unused "type: ignore[steven, import]" comment +-- Spacing is not preserved +5 # type: ignore[ steven, import ] # E: Unused "type: ignore[steven, import]" comment +-- Make sure it works as intended in more complex situations +1 + "ok" + "ok".foo # type: ignore[ operator,steven,attr-defined, import] # E: Unused "type: ignore[steven, import]" comment + [case testUnusedIgnoreTryExcept] # flags: --warn-unused-ignores try: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 173265e48e6f7..fdda5f64284de 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -124,7 +124,7 @@ class A: pass def func1() -> A: pass [rechecked mod1] -[stale] +[stale mod1] [out2] tmp/mod1.py:1: error: Name "A" is not defined @@ -205,7 +205,7 @@ def foo() -> int: return "foo" return inner2() -[rechecked mod1, mod2] +[rechecked mod2] [stale] [out2] tmp/mod2.py:4: error: Incompatible return value type (got "str", expected "int") @@ -428,7 +428,7 @@ class CustomType: def foo(self) -> str: return "a" [rechecked mod1, mod2, mod2.mod3] -[stale mod2, mod2.mod3] +[stale mod1, mod2, mod2.mod3] [builtins fixtures/module.pyi] [out1] [out2] @@ -466,7 +466,7 @@ class CustomType: def foo(self) -> str: return "a" [rechecked mod1, mod2, mod2.mod3] -[stale mod2.mod3] +[stale mod1, mod2.mod3] [builtins fixtures/module.pyi] [out1] [out2] @@ -541,7 +541,7 @@ def func2() -> str: return "foo" [rechecked mod0, mod1, mod2] -[stale mod2] +[stale mod0, mod2] [out2] tmp/mod1.py:4: error: Incompatible return value type (got "str", expected "int") @@ -952,7 +952,7 @@ reveal_type(b.x) [file parent/b.py.2] x = 10 -[stale parent.b] +[stale parent.a, parent.b] [rechecked parent.a, parent.b] [out2] tmp/parent/a.py:2: note: Revealed type is "builtins.int" @@ -1080,7 +1080,7 @@ class Class: pass [builtins fixtures/args.pyi] [rechecked collections, main, package.subpackage.mod1] -[stale collections, package.subpackage.mod1] +[stale collections, main, package.subpackage.mod1] [out2] tmp/main.py:4: error: "Class" has no attribute "some_attribute" @@ -1120,7 +1120,7 @@ if int(): [builtins fixtures/module_all.pyi] [rechecked main, c, c.submodule] -[stale c] +[stale main, c, c.submodule] [out2] tmp/c/submodule.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/main.py:7: error: "C" has no attribute "foo" @@ -1174,7 +1174,7 @@ reveal_type(foo) foo = 3.14 reveal_type(foo) [rechecked m, n] -[stale] +[stale n] [out1] tmp/n.py:2: note: Revealed type is "builtins.str" tmp/m.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" @@ -1204,7 +1204,7 @@ from bad import foo foo(3) [rechecked client] -[stale] +[stale client] [out2] tmp/client.py:4: error: Argument 1 to "foo" has incompatible type "int"; expected "str" @@ -1316,7 +1316,7 @@ reveal_type(bar) bar = "str" [rechecked main] -[stale] +[stale main] [out1] tmp/main.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" tmp/main.py:4: note: Revealed type is "builtins.str" @@ -1354,8 +1354,8 @@ class B: class C: def foo(self) -> int: return 1 -[rechecked mod3, mod2, mod1] -[stale mod3, mod2] +[rechecked mod3] +[stale] [out1] tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: note: Revealed type is "builtins.int" @@ -1393,8 +1393,8 @@ class C: class C: def foo(self) -> str: return 'a' -[rechecked mod4, mod3, mod2, mod1] -[stale mod4] +[rechecked mod4, mod3, mod1] +[stale mod1, mod4] [out1] tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: note: Revealed type is "builtins.int" @@ -1438,8 +1438,8 @@ class C: class C: def foo(self) -> str: return 'a' -[rechecked mod4, mod3, mod2, mod1] -[stale mod4, mod3, mod2] +[rechecked mod4, mod3, mod1] +[stale mod1, mod4] [out1] tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: note: Revealed type is "builtins.int" @@ -1893,11 +1893,12 @@ main:1: error: Module "ntcrash" has no attribute "nope" [case testIncrementalTypedDictInMethod] from tdcrash import nope [file tdcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: def f(self) -> None: A = TypedDict('A', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "tdcrash" has no attribute "nope" [out2] @@ -1906,12 +1907,13 @@ main:1: error: Module "tdcrash" has no attribute "nope" [case testIncrementalTypedDictInMethod2] from tdcrash import nope [file tdcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: class D: def f(self) -> None: A = TypedDict('A', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "tdcrash" has no attribute "nope" [out2] @@ -1920,13 +1922,14 @@ main:1: error: Module "tdcrash" has no attribute "nope" [case testIncrementalTypedDictInMethod3] from tdcrash import nope [file tdcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: def a(self): class D: def f(self) -> None: A = TypedDict('A', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "tdcrash" has no attribute "nope" [out2] @@ -1935,8 +1938,7 @@ main:1: error: Module "tdcrash" has no attribute "nope" [case testIncrementalNewTypeInMethod] from ntcrash import nope [file ntcrash.py] -from mypy_extensions import TypedDict -from typing import NewType, NamedTuple +from typing import NewType, NamedTuple, TypedDict class C: def f(self) -> None: X = NewType('X', int) @@ -1949,6 +1951,7 @@ def f() -> None: B = NamedTuple('B', [('x', X)]) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:1: error: Module "ntcrash" has no attribute "nope" [out2] @@ -2048,7 +2051,7 @@ warn_no_return = True [case testIncrementalClassVar] from typing import ClassVar class A: - x = None # type: ClassVar + x: ClassVar A().x = 0 [out1] main:4: error: Cannot assign to class variable "x" via instance @@ -2088,10 +2091,11 @@ reveal_type(b.x) y: b.A reveal_type(y) [file b.py] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) x: A [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:2: note: Revealed type is "TypedDict('b.A', {'x': builtins.int, 'y': builtins.str})" main:4: note: Revealed type is "TypedDict('b.A', {'x': builtins.int, 'y': builtins.str})" @@ -2169,7 +2173,7 @@ import m x = 1 [delete m.py.2] [rechecked n] -[stale] +[stale n] [out2] tmp/n.py:1: error: Cannot find implementation or library stub for module named "m" tmp/n.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports @@ -2220,7 +2224,7 @@ def foo() -> int: [rechecked m] [stale m] [rechecked2 m] -[stale2] +[stale2 m] [out3] tmp/m.py:2: error: Incompatible return value type (got "str", expected "int") @@ -2239,7 +2243,7 @@ def foo() -> str: def foo(x) -> int: pass [rechecked m, n] -[stale m] +[stale m, n] [rechecked2 m, n] [stale2 m, n] [out2] @@ -2532,14 +2536,14 @@ x = NT(N(1)) [out] [case testNewTypeFromForwardTypedDictIncremental] -from typing import NewType, Tuple, Dict -from mypy_extensions import TypedDict +from typing import NewType, Tuple, TypedDict, Dict NT = NewType('NT', N) # type: ignore class N(TypedDict): x: A A = Dict[str, int] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -- Some crazy self-referential named tuples, types dicts, and aliases @@ -2573,6 +2577,13 @@ C(1)[0] [builtins fixtures/list.pyi] [out] +[case testSerializeRecursiveAlias] +from typing import Callable, Union + +Node = Union[str, int, Callable[[], "Node"]] +n: Node +[out] + [case testSerializeRecursiveAliases1] from typing import Type, Callable, Union @@ -2890,7 +2901,7 @@ extra = 1 import m.a # Depends on module with error [file m/c.py] import m # No error here -[rechecked m.a, m.b] +[rechecked] [out1] tmp/m/a.py:1: error: Unsupported operand types for + ("int" and "str") [out2] @@ -3087,10 +3098,10 @@ class A: [stale] [out2] main:2: note: Revealed type is "def (a: builtins.int) -> a.A" -main:3: note: Revealed type is "def [_AT] (self: _AT`1, other: _AT`1) -> builtins.bool" -main:4: note: Revealed type is "def [_AT] (self: _AT`2, other: _AT`2) -> builtins.bool" -main:5: note: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" -main:6: note: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +main:3: note: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" +main:4: note: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" +main:5: note: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" +main:6: note: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" main:15: error: Unsupported operand types for < ("A" and "int") main:16: error: Unsupported operand types for <= ("A" and "int") main:17: error: Unsupported operand types for > ("A" and "int") @@ -3166,13 +3177,13 @@ C(5, 'foo', True) [file a.py] import attrs -@attrs.define +@attrs.define(slots=False) class A: a: int [file b.py] import attrs -@attrs.define +@attrs.define(slots=False) class B: b: str @@ -3180,7 +3191,7 @@ class B: from a import A from b import B import attrs -@attrs.define +@attrs.define(slots=False) class C(A, B): c: bool @@ -3513,7 +3524,7 @@ class M(type): y: int [out] [out2] -tmp/a.py:2: error: "Type[B]" has no attribute "x" +tmp/a.py:2: error: "type[B]" has no attribute "x" [case testIncrementalLotsOfInheritance] import a @@ -3753,7 +3764,7 @@ import b [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by clobbering -- the proto deps file with something with mtime mismatches. -[file ../.mypy_cache/3.8/@deps.meta.json.2] +[file ../.mypy_cache/3.9/@deps.meta.json.2] {"snapshot": {"__main__": "a7c958b001a45bd6a2a320f4e53c4c16", "a": "d41d8cd98f00b204e9800998ecf8427e", "b": "d41d8cd98f00b204e9800998ecf8427e", "builtins": "c532c89da517a4b779bcf7a964478d67"}, "deps_meta": {"@root": {"path": "@root.deps.json", "mtime": 0}, "__main__": {"path": "__main__.deps.json", "mtime": 0}, "a": {"path": "a.deps.json", "mtime": 0}, "b": {"path": "b.deps.json", "mtime": 0}, "builtins": {"path": "builtins.deps.json", "mtime": 0}}} [file ../.mypy_cache/.gitignore] # Another hack to not trigger a .gitignore creation failure "false positive" @@ -3788,7 +3799,7 @@ import b [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by deleting -- the proto deps file. -[delete ../.mypy_cache/3.8/@deps.meta.json.2] +[delete ../.mypy_cache/3.9/@deps.meta.json.2] [file b.py.2] # uh -- Every file should get reloaded, since the cache was invalidated @@ -4146,7 +4157,7 @@ from d import k [case testCachedBadProtocolNote] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) [file b.py] from typing import Iterable @@ -4158,8 +4169,8 @@ from typing import Iterable from a import Point p: Point it: Iterable[int] = p # change -[typing fixtures/typing-medium.pyi] [builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [out] tmp/b.py:4: error: Incompatible types in assignment (expression has type "Point", variable has type "Iterable[int]") tmp/b.py:4: note: Following member(s) of "Point" have conflicts: @@ -4643,13 +4654,14 @@ from typing import NamedTuple from other import B A = NamedTuple('A', [('x', B)]) [file other.pyi] -from mypy_extensions import TypedDict +from typing import TypedDict from lib import A B = TypedDict('B', {'x': A}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Tuple[..., fallback=lib.A]}), fallback=lib.A]" +tmp/a.py:3: note: Revealed type is "tuple[TypedDict('other.B', {'x': tuple[..., fallback=lib.A]}), fallback=lib.A]" [case testFollowImportSkipNotInvalidatedOnPresent] # flags: --follow-imports=skip @@ -5075,10 +5087,10 @@ plugins=/test-data/unit/plugins/config_data.py import mod reveal_type(mod.a) [file mod.py] -from typing_extensions import Literal +from typing import Literal a = 1 [file mod.py.2] -from typing_extensions import Literal +from typing import Literal a: Literal[2] = 2 [builtins fixtures/tuple.pyi] [out] @@ -5118,7 +5130,7 @@ NT = NamedTuple('BadName', [('x', int)]) tmp/b.py:2: error: First argument to namedtuple() should be "NT", not "BadName" [out2] tmp/b.py:2: error: First argument to namedtuple() should be "NT", not "BadName" -tmp/a.py:3: note: Revealed type is "Tuple[builtins.int, fallback=b.NT]" +tmp/a.py:3: note: Revealed type is "tuple[builtins.int, fallback=b.NT]" [case testNewAnalyzerIncrementalBrokenNamedTupleNested] @@ -5159,7 +5171,7 @@ class C: [builtins fixtures/tuple.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is "Tuple[builtins.int, fallback=b.C.Hidden@5]" +tmp/a.py:3: note: Revealed type is "tuple[builtins.int, fallback=b.C.Hidden@5]" [case testIncrementalNodeCreatedFromGetattr] import a @@ -5230,11 +5242,7 @@ class Sub(Base): [builtins fixtures/property.pyi] [out] -tmp/a.py:3: error: Cannot determine type of "foo" -tmp/a.py:4: error: Cannot determine type of "foo" [out2] -tmp/a.py:3: error: Cannot determine type of "foo" -tmp/a.py:4: error: Cannot determine type of "foo" [case testRedefinitionClass] import b @@ -5268,7 +5276,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] [out2] -tmp/b.py:2: note: Revealed type is "a." +tmp/b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalNoChangeSameName] import b @@ -5291,7 +5299,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] [out2] -tmp/b.py:2: note: Revealed type is "a." +tmp/b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalNoChangeTuple] @@ -5313,7 +5321,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] [out2] -tmp/b.py:2: note: Revealed type is "a." +tmp/b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalIsInstanceChange] import c @@ -5347,9 +5355,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." [out2] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionIncrementalUnderlyingObjChang] import c @@ -5375,9 +5383,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "b." +tmp/c.py:2: note: Revealed type is "b." [out2] -tmp/c.py:2: note: Revealed type is "b." +tmp/c.py:2: note: Revealed type is "b." [case testIsInstanceAdHocIntersectionIncrementalIntersectionToUnreachable] import c @@ -5408,7 +5416,7 @@ from b import z reveal_type(z) [builtins fixtures/isinstance.pyi] [out] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." [out2] tmp/b.py:2: error: Cannot determine type of "y" tmp/c.py:2: note: Revealed type is "Any" @@ -5445,7 +5453,60 @@ reveal_type(z) tmp/b.py:2: error: Cannot determine type of "y" tmp/c.py:2: note: Revealed type is "Any" [out2] -tmp/c.py:2: note: Revealed type is "a." +tmp/c.py:2: note: Revealed type is "a." + +[case testIsInstanceAdHocIntersectionIncrementalNestedClass] +import b +[file a.py] +class A: + class B: ... + class C: ... + class D: + def __init__(self) -> None: + x: A.B + assert isinstance(x, A.C) + self.x = x +[file b.py] +from a import A +[file b.py.2] +from a import A +reveal_type(A.D.x) +[builtins fixtures/isinstance.pyi] +[out] +[out2] +tmp/b.py:2: note: Revealed type is "a." + +[case testIsInstanceAdHocIntersectionIncrementalUnions] +import c +[file a.py] +import b +class A: + p: b.D +class B: + p: b.D +class C: + p: b.D + c: str +x: A +assert isinstance(x, (B, C)) +y = x +[file b.py] +class D: + p: int +[file c.py] +from a import y +[file c.py.2] +from a import y, C +reveal_type(y) +reveal_type(y.p.p) +assert isinstance(y, C) +reveal_type(y.c) +[builtins fixtures/isinstance.pyi] +[out] +[out2] +tmp/c.py:2: note: Revealed type is "Union[a., a.]" +tmp/c.py:3: note: Revealed type is "builtins.int" +tmp/c.py:5: note: Revealed type is "builtins.str" [case testStubFixupIssues] import a @@ -5621,10 +5682,12 @@ class FinalEnum(Enum): [builtins fixtures/isinstance.pyi] [out] main:3: error: Cannot override writable attribute "x" with a final one +main:3: error: Incompatible types in assignment (expression has type "Ok", base class "RegularEnum" defined the type as "int") main:4: error: Cannot extend enum with existing members: "FinalEnum" main:5: error: Cannot override final attribute "x" (previously declared in base class "FinalEnum") [out2] main:3: error: Cannot override writable attribute "x" with a final one +main:3: error: Incompatible types in assignment (expression has type "Ok", base class "RegularEnum" defined the type as "int") main:4: error: Cannot extend enum with existing members: "FinalEnum" main:5: error: Cannot override final attribute "x" (previously declared in base class "FinalEnum") @@ -5682,8 +5745,8 @@ import b b.xyz [file b.py] -from typing import NamedTuple, NewType -from typing_extensions import TypedDict, TypeAlias +from typing import NamedTuple, NewType, TypedDict +from typing_extensions import TypeAlias from enum import Enum from dataclasses import dataclass @@ -5719,6 +5782,7 @@ class C: n: N = N(NT1(c=1)) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out2] tmp/a.py:2: error: "object" has no attribute "xyz" @@ -5790,9 +5854,9 @@ reveal_type(a.n) [out] [out2] [out3] -tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" +tmp/c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") -tmp/c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" +tmp/c.py:7: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" [case testTupleTypeUpdateNonRecursiveToRecursiveCoarse] import c @@ -5823,7 +5887,7 @@ def f(x: a.N) -> None: [out] [out2] [out3] -tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" +tmp/c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypeAliasUpdateNonRecursiveToRecursiveCoarse] @@ -5855,7 +5919,7 @@ def f(x: a.N) -> None: [out] [out2] [out3] -tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int], None], builtins.int]" +tmp/c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int], None], builtins.int]" tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypedDictUpdateNonRecursiveToRecursiveCoarse] @@ -6021,7 +6085,8 @@ tmp/b.py:3: error: Incompatible types in assignment (expression has type "int", [case testUnpackKwargsSerialize] import m [file lib.py] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -6037,6 +6102,7 @@ foo(name='Jennifer', age=38) from lib import foo foo(name='Jennifer', age="38") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] tmp/m.py:2: error: Argument "age" to "foo" has incompatible type "str"; expected "int" @@ -6112,6 +6178,118 @@ class Base: [out2] main:5: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe +[case testLiteralCoarseGrainedChainedAliases] +from mod1 import Alias1 +from typing import Literal +x: Alias1 +def expect_int(x: int) -> None: pass +expect_int(x) +[file mod1.py] +from mod2 import Alias2 +Alias1 = Alias2 +[file mod2.py] +from mod3 import Alias3 +Alias2 = Alias3 +[file mod3.py] +from typing import Literal +Alias3 = int +[file mod3.py.2] +from typing import Literal +Alias3 = str +[builtins fixtures/tuple.pyi] +[out] +[out2] +main:5: error: Argument 1 to "expect_int" has incompatible type "str"; expected "int" + +[case testLiteralCoarseGrainedChainedAliases2] +from mod1 import Alias1 +from typing import Literal +x: Alias1 +def expect_3(x: Literal[3]) -> None: pass +expect_3(x) +[file mod1.py] +from mod2 import Alias2 +Alias1 = Alias2 +[file mod2.py] +from mod3 import Alias3 +Alias2 = Alias3 +[file mod3.py] +from typing import Literal +Alias3 = Literal[3] +[file mod3.py.2] +from typing import Literal +Alias3 = Literal[4] +[builtins fixtures/tuple.pyi] +[out] +[out2] +main:5: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expected "Literal[3]" + +[case testDoubleReexportFunctionUpdated] +import m + +[file m.py] +import f +[file m.py.3] +import f +reveal_type(f.foo) + +[file f.py] +import c +def foo(arg: c.C) -> None: pass + +[file c.py] +from types import C + +[file types.py] +import pb1 +C = pb1.C +[file types.py.2] +import pb1, pb2 +C = pb2.C + +[file pb1.py] +class C: ... +[file pb2.py] +class C: ... +[out] +[out2] +[out3] +tmp/m.py:2: note: Revealed type is "def (arg: pb2.C)" + +[case testDoubleReexportGenericUpdated] +import m + +[file m.py] +import f +[file m.py.3] +import f +x: f.F +reveal_type(x[0]) + +[file f.py] +import c +class FB(list[c.C]): ... +class F(FB): ... + +[file c.py] +from types import C + +[file types.py] +import pb1 +C = pb1.C +[file types.py.2] +import pb1, pb2 +C = pb2.C + +[file pb1.py] +class C: ... +[file pb2.py] +class C: ... +[out] +[out2] +[out3] +tmp/m.py:3: note: Revealed type is "pb2.C" + [case testNoCrashDoubleReexportFunctionEmpty] import m @@ -6144,6 +6322,38 @@ class C: ... [out2] [out3] +[case testNoCrashDoubleReexportAliasEmpty] +import m + +[file m.py] +import f +[file m.py.3] +import f +# modify + +[file f.py] +import c +D = list[c.C] + +[file c.py] +from types import C + +[file types.py] +import pb1 +C = pb1.C +[file types.py.2] +import pb2 +C = pb2.C + +[file pb1.py] +class C: ... +[file pb2.py.2] +class C: ... +[file pb1.py.2] +[out] +[out2] +[out3] + [case testNoCrashDoubleReexportBaseEmpty] import m @@ -6176,6 +6386,69 @@ class C: ... [out2] [out3] +[case testNoCrashDoubleReexportBaseEmpty2] +import m + +[file m.py] +import f +[file m.py.3] +import f +# modify + +[file f.py] +import c +class D(c.C): pass + +[file c.py] +from types import C + +[file types.py] +import pb1 +C = pb1.C +[file types.py.2] +import pb2 +C = pb2.C + +[file pb1.py] +class C: ... +[file pb2.py.2] +class C: ... +[file pb1.py.2] +[out] +[out2] +[out3] + +[case testDoubleReexportMetaUpdated] +import m +class C(metaclass=m.M): ... + +[file m.py] +from types import M + +[file types.py] +class M(type): ... +[file types.py.2] +class M: ... +[out] +[out2] +main:2: error: Metaclasses not inheriting from "type" are not supported + +[case testIncrementalOkChangeWithSave2] +import mod1 +x: int = mod1.x + +[file mod1.py] +from mod2 import x + +[file mod2.py] +x = 1 + +[file mod2.py.2] +x = "no way" +[out] +[out2] +main:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") + [case testNoCrashDoubleReexportMetaEmpty] import m @@ -6208,6 +6481,38 @@ class C(type): ... [out2] [out3] +[case testNoCrashDoubleReexportMetaEmpty2] +import m + +[file m.py] +import f +[file m.py.3] +import f +# modify + +[file f.py] +import c +class D(metaclass=c.C): pass + +[file c.py] +from types import C + +[file types.py] +import pb1 +C = pb1.C +[file types.py.2] +import pb2 +C = pb2.C + +[file pb1.py] +class C(type): ... +[file pb2.py.2] +class C(type): ... +[file pb1.py.2] +[out] +[out2] +[out3] + [case testNoCrashDoubleReexportTypedDictEmpty] import m @@ -6218,7 +6523,7 @@ import f # modify [file f.py] -from typing_extensions import TypedDict +from typing import TypedDict import c class D(TypedDict): x: c.C @@ -6239,6 +6544,7 @@ class C: ... class C: ... [file pb1.py.2] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] [out3] @@ -6317,6 +6623,7 @@ class C: ... [out3] [case testNoCrashOnPartialLambdaInference] +# flags: --no-local-partial-types import m [file m.py] from typing import TypeVar, Callable @@ -6405,8 +6712,7 @@ y: int = x [case testGenericTypedDictWithError] import b [file a.py] -from typing import Generic, TypeVar -from typing_extensions import TypedDict +from typing import Generic, TypeVar, TypedDict TValue = TypeVar("TValue") class Dict(TypedDict, Generic[TValue]): @@ -6428,6 +6734,7 @@ def f(d: Dict[TValue]) -> TValue: def g(d: Dict[TValue]) -> TValue: return d["y"] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] tmp/b.py:6: error: TypedDict "a.Dict[TValue]" has no key "x" [out2] @@ -6529,9 +6836,10 @@ import counts import counts # touch [file counts.py] -from typing_extensions import TypedDict +from typing import TypedDict Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNoIncrementalCrashOnInvalidTypedDictFunc] import m @@ -6541,10 +6849,11 @@ import counts import counts # touch [file counts.py] -from typing_extensions import TypedDict +from typing import TypedDict def test() -> None: Counts = TypedDict("Counts", {k: int for k in "abc"}) # type: ignore [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNoIncrementalCrashOnTypedDictMethod] import a @@ -6556,13 +6865,14 @@ from b import C x: C reveal_type(x.h) [file b.py] -from typing_extensions import TypedDict +from typing import TypedDict class C: def __init__(self) -> None: self.h: Hidden class Hidden(TypedDict): x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [out2] tmp/a.py:3: note: Revealed type is "TypedDict('b.C.Hidden@5', {'x': builtins.int})" @@ -6586,6 +6896,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" @@ -6639,7 +6950,7 @@ p3 = functools.partial(foo, b="a") [out] tmp/a.py:8: note: Revealed type is "functools.partial[builtins.int]" tmp/a.py:13: error: Argument 1 to "takes_callable_str" has incompatible type "partial[int]"; expected "Callable[..., str]" -tmp/a.py:13: note: "partial[int].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], int]" +tmp/a.py:13: note: "partial[int].__call__" has type "def __call__(__self, *args: Any, **kwargs: Any) -> int" tmp/a.py:18: error: Argument 1 to "foo" has incompatible type "int"; expected "str" tmp/a.py:19: error: Too many arguments for "foo" tmp/a.py:19: error: Argument 1 to "foo" has incompatible type "int"; expected "str" @@ -6743,3 +7054,585 @@ from typing_extensions import TypeAlias IntOrStr: TypeAlias = int | str assert isinstance(1, IntOrStr) [builtins fixtures/type.pyi] + +[case testPropertySetterTypeIncremental] +import b +[file a.py] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: str) -> None: + pass +[file b.py] +from a import A +[file b.py.2] +from a import A +a = A() +a.f = '' # OK +reveal_type(a.f) +a.f = 1 +reveal_type(a.f) +[builtins fixtures/property.pyi] +[out] +[out2] +tmp/b.py:4: note: Revealed type is "builtins.int" +tmp/b.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "str") +tmp/b.py:6: note: Revealed type is "builtins.int" + +[case testSerializeDeferredGenericNamedTuple] +import pkg +[file pkg/__init__.py] +from .lib import NT +[file pkg/lib.py] +from typing import Generic, NamedTuple, TypeVar +from pkg import does_not_exist # type: ignore +from pkg.missing import also_missing # type: ignore + +T = TypeVar("T", bound=does_not_exist) +class NT(NamedTuple, Generic[T]): + values: also_missing[T] +[file pkg/__init__.py.2] +# touch +from .lib import NT +[builtins fixtures/tuple.pyi] +[out] +[out2] + +[case testNewRedefineAffectsCache] +# flags: --local-partial-types --allow-redefinition-new +# flags2: --local-partial-types +# flags3: --local-partial-types --allow-redefinition-new +x = 0 +if int(): + x = "" +[out] +[out2] +main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testUntypedCallsExcludeAffectsCache] +# flags: --disallow-untyped-calls --untyped-calls-exclude=mod.Super +# flags2: --disallow-untyped-calls --untyped-calls-exclude=mod +# flags3: --disallow-untyped-calls --untyped-calls-exclude=mod.Super +import mod +[file mod.py] +class Super: + def draw(self): + ... +class Class(Super): + ... +Class().draw() +[out] +tmp/mod.py:6: error: Call to untyped function "draw" in typed context +[out2] +[out3] +tmp/mod.py:6: error: Call to untyped function "draw" in typed context + +[case testMethodMakeBoundIncremental] +from a import A +a = A() +a.f() +[file a.py] +class B: + def f(self, s: A) -> int: ... + +def f(s: A) -> int: ... + +class A: + f = f +[file a.py.2] +class B: + def f(self, s: A) -> int: ... + +def f(s: A) -> int: ... + +class A: + f = B().f +[out] +[out2] +main:3: error: Too few arguments + +[case testUnreachableAfterToplevelAssertImportThirdParty] +# flags: --platform unknown +import sys +assert sys.platform == 'linux' +import does_not_exist +[builtins fixtures/ops.pyi] +[out] +[out2] + +[case testIncrementalNoCrashOnParamSpecPrefixUpdateMethod] +import impl +[file impl.py] +from typing_extensions import ParamSpec +from lib import Sub + +P = ParamSpec("P") +class Impl(Sub[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + self.meth(1, *args, **kwargs) + +[file impl.py.2] +from typing_extensions import ParamSpec +from lib import Sub + +P = ParamSpec("P") +class Impl(Sub[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + self.meth("no", *args, **kwargs) + +[file lib.py] +from typing import Generic +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +class Base(Generic[P]): + def meth(self, *args: P.args, **kwargs: P.kwargs) -> None: ... +class Sub(Base[Concatenate[int, P]]): ... +[builtins fixtures/paramspec.pyi] +[out] +[out2] +tmp/impl.py:7: error: Argument 1 to "meth" of "Base" has incompatible type "str"; expected "int" + +[case testIncrementalNoCrashOnParamSpecPrefixUpdateMethodAlias] +import impl +[file impl.py] +from typing_extensions import ParamSpec +from lib import Sub + +P = ParamSpec("P") +class Impl(Sub[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + self.alias(1, *args, **kwargs) + +[file impl.py.2] +from typing_extensions import ParamSpec +from lib import Sub + +P = ParamSpec("P") +class Impl(Sub[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + self.alias("no", *args, **kwargs) + +[file lib.py] +from typing import Generic +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +class Base(Generic[P]): + def meth(self, *args: P.args, **kwargs: P.kwargs) -> None: ... + alias = meth +class Sub(Base[Concatenate[int, P]]): ... +[builtins fixtures/paramspec.pyi] +[out] +[out2] +tmp/impl.py:7: error: Argument 1 has incompatible type "str"; expected "int" + +[case testIncrementalDifferentSourcesFreshnessCorrect] +# cmd: mypy -m foo bar +# cmd2: mypy -m foo +# cmd3: mypy -m foo bar +[file foo.py] +foo = 5 +[file foo.py.2] +foo = None +[file bar.py] +from foo import foo +bar: int = foo +[out] +[out2] +[out3] +tmp/bar.py:2: error: Incompatible types in assignment (expression has type "None", variable has type "int") + +[case testIncrementalBlockingErrorRepeatAndUndo] +import m +[file m.py] +import f +reveal_type(f.x) +[file m.py.3] +import f +reveal_type(f.x) +# touch +[file f.py] +x = 1 +[file f.py.2] +no way +[file f.py.4] +x = 1 +[out] +tmp/m.py:2: note: Revealed type is "builtins.int" +[out2] +tmp/f.py:1: error: Invalid syntax +[out3] +tmp/f.py:1: error: Invalid syntax +[out4] +tmp/m.py:2: note: Revealed type is "builtins.int" + +[case testIncrementalSameErrorOrder] +import m +[file m.py] +import n +def accept_int(x: int) -> None: pass +accept_int(n.foo) +[file n.py] +import other +foo = "hello" +reveal_type(foo) +[file other.py] +[file other.py.2] +# touch +[rechecked other] +[stale] +[out] +tmp/n.py:3: note: Revealed type is "builtins.str" +tmp/m.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" +[out2] +tmp/n.py:3: note: Revealed type is "builtins.str" +tmp/m.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" + +[case testIncrementalNoIndirectDepFromLocal] +import foo +import bar + +[file foo.py] +# Having a local named 'bar' shouldn't generate a dependency on module 'bar' +def f(bar: int) -> int: + return bar + +[file bar.py] +import foo +x = 1 + +[file bar.py.2] +import foo +x = 2 + +[out] +[rechecked bar] +[stale] + + +[case testIncrementalTypedDictGetMethodTotalFalse] +import impl +[file lib.py] +from typing import TypedDict +class Unrelated: pass +D = TypedDict('D', {'x': int, 'y': str}, total=False) +[file impl.py] +pass +[file impl.py.2] +from typing import Literal +from lib import D, Unrelated +d: D +u: Unrelated +x: Literal['x'] +y: Literal['y'] +z: Literal['z'] +x_or_y: Literal['x', 'y'] +x_or_z: Literal['x', 'z'] +x_or_y_or_z: Literal['x', 'y', 'z'] + +# test with literal expression +reveal_type(d.get('x')) +reveal_type(d.get('y')) +reveal_type(d.get('z')) +reveal_type(d.get('x', u)) +reveal_type(d.get('x', 1)) +reveal_type(d.get('y', None)) + +# test with literal type / union of literal types with implicit default +reveal_type(d.get(x)) +reveal_type(d.get(y)) +reveal_type(d.get(z)) +reveal_type(d.get(x_or_y)) +reveal_type(d.get(x_or_z)) +reveal_type(d.get(x_or_y_or_z)) + +# test with literal type / union of literal types with explicit default +reveal_type(d.get(x, u)) +reveal_type(d.get(y, u)) +reveal_type(d.get(z, u)) +reveal_type(d.get(x_or_y, u)) +reveal_type(d.get(x_or_z, u)) +reveal_type(d.get(x_or_y_or_z, u)) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] +[out] +[out2] +tmp/impl.py:13: note: Revealed type is "Union[builtins.int, None]" +tmp/impl.py:14: note: Revealed type is "Union[builtins.str, None]" +tmp/impl.py:15: note: Revealed type is "builtins.object" +tmp/impl.py:16: note: Revealed type is "Union[builtins.int, lib.Unrelated]" +tmp/impl.py:17: note: Revealed type is "builtins.int" +tmp/impl.py:18: note: Revealed type is "Union[builtins.str, None]" +tmp/impl.py:21: note: Revealed type is "Union[builtins.int, None]" +tmp/impl.py:22: note: Revealed type is "Union[builtins.str, None]" +tmp/impl.py:23: note: Revealed type is "builtins.object" +tmp/impl.py:24: note: Revealed type is "Union[builtins.int, builtins.str, None]" +tmp/impl.py:25: note: Revealed type is "builtins.object" +tmp/impl.py:26: note: Revealed type is "builtins.object" +tmp/impl.py:29: note: Revealed type is "Union[builtins.int, lib.Unrelated]" +tmp/impl.py:30: note: Revealed type is "Union[builtins.str, lib.Unrelated]" +tmp/impl.py:31: note: Revealed type is "builtins.object" +tmp/impl.py:32: note: Revealed type is "Union[builtins.int, builtins.str, lib.Unrelated]" +tmp/impl.py:33: note: Revealed type is "builtins.object" +tmp/impl.py:34: note: Revealed type is "builtins.object" + +[case testIncrementalTypedDictGetMethodTotalTrue] +import impl +[file lib.py] +from typing import TypedDict +class Unrelated: pass +D = TypedDict('D', {'x': int, 'y': str}, total=True) +[file impl.py] +pass +[file impl.py.2] +from typing import Literal +from lib import D, Unrelated +d: D +u: Unrelated +x: Literal['x'] +y: Literal['y'] +z: Literal['z'] +x_or_y: Literal['x', 'y'] +x_or_z: Literal['x', 'z'] +x_or_y_or_z: Literal['x', 'y', 'z'] + +# test with literal expression +reveal_type(d.get('x')) +reveal_type(d.get('y')) +reveal_type(d.get('z')) +reveal_type(d.get('x', u)) +reveal_type(d.get('x', 1)) +reveal_type(d.get('y', None)) + +# test with literal type / union of literal types with implicit default +reveal_type(d.get(x)) +reveal_type(d.get(y)) +reveal_type(d.get(z)) +reveal_type(d.get(x_or_y)) +reveal_type(d.get(x_or_z)) +reveal_type(d.get(x_or_y_or_z)) + +# test with literal type / union of literal types with explicit default +reveal_type(d.get(x, u)) +reveal_type(d.get(y, u)) +reveal_type(d.get(z, u)) +reveal_type(d.get(x_or_y, u)) +reveal_type(d.get(x_or_z, u)) +reveal_type(d.get(x_or_y_or_z, u)) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] +[out] +[out2] +tmp/impl.py:13: note: Revealed type is "builtins.int" +tmp/impl.py:14: note: Revealed type is "builtins.str" +tmp/impl.py:15: note: Revealed type is "builtins.object" +tmp/impl.py:16: note: Revealed type is "builtins.int" +tmp/impl.py:17: note: Revealed type is "builtins.int" +tmp/impl.py:18: note: Revealed type is "builtins.str" +tmp/impl.py:21: note: Revealed type is "builtins.int" +tmp/impl.py:22: note: Revealed type is "builtins.str" +tmp/impl.py:23: note: Revealed type is "builtins.object" +tmp/impl.py:24: note: Revealed type is "Union[builtins.int, builtins.str]" +tmp/impl.py:25: note: Revealed type is "builtins.object" +tmp/impl.py:26: note: Revealed type is "builtins.object" +tmp/impl.py:29: note: Revealed type is "builtins.int" +tmp/impl.py:30: note: Revealed type is "builtins.str" +tmp/impl.py:31: note: Revealed type is "builtins.object" +tmp/impl.py:32: note: Revealed type is "Union[builtins.int, builtins.str]" +tmp/impl.py:33: note: Revealed type is "builtins.object" +tmp/impl.py:34: note: Revealed type is "builtins.object" + + +[case testIncrementalTypedDictGetMethodTotalMixed] +import impl +[file lib.py] +from typing import TypedDict +from typing_extensions import Required, NotRequired +class Unrelated: pass +D = TypedDict('D', {'x': Required[int], 'y': NotRequired[str]}) +[file impl.py] +pass +[file impl.py.2] +from typing import Literal +from lib import D, Unrelated +d: D +u: Unrelated +x: Literal['x'] +y: Literal['y'] +z: Literal['z'] +x_or_y: Literal['x', 'y'] +x_or_z: Literal['x', 'z'] +x_or_y_or_z: Literal['x', 'y', 'z'] + +# test with literal expression +reveal_type(d.get('x')) +reveal_type(d.get('y')) +reveal_type(d.get('z')) +reveal_type(d.get('x', u)) +reveal_type(d.get('x', 1)) +reveal_type(d.get('y', None)) + +# test with literal type / union of literal types with implicit default +reveal_type(d.get(x)) +reveal_type(d.get(y)) +reveal_type(d.get(z)) +reveal_type(d.get(x_or_y)) +reveal_type(d.get(x_or_z)) +reveal_type(d.get(x_or_y_or_z)) + +# test with literal type / union of literal types with explicit default +reveal_type(d.get(x, u)) +reveal_type(d.get(y, u)) +reveal_type(d.get(z, u)) +reveal_type(d.get(x_or_y, u)) +reveal_type(d.get(x_or_z, u)) +reveal_type(d.get(x_or_y_or_z, u)) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] +[out] +[out2] +tmp/impl.py:13: note: Revealed type is "builtins.int" +tmp/impl.py:14: note: Revealed type is "Union[builtins.str, None]" +tmp/impl.py:15: note: Revealed type is "builtins.object" +tmp/impl.py:16: note: Revealed type is "builtins.int" +tmp/impl.py:17: note: Revealed type is "builtins.int" +tmp/impl.py:18: note: Revealed type is "Union[builtins.str, None]" +tmp/impl.py:21: note: Revealed type is "builtins.int" +tmp/impl.py:22: note: Revealed type is "Union[builtins.str, None]" +tmp/impl.py:23: note: Revealed type is "builtins.object" +tmp/impl.py:24: note: Revealed type is "Union[builtins.int, builtins.str, None]" +tmp/impl.py:25: note: Revealed type is "builtins.object" +tmp/impl.py:26: note: Revealed type is "builtins.object" +tmp/impl.py:29: note: Revealed type is "builtins.int" +tmp/impl.py:30: note: Revealed type is "Union[builtins.str, lib.Unrelated]" +tmp/impl.py:31: note: Revealed type is "builtins.object" +tmp/impl.py:32: note: Revealed type is "Union[builtins.int, builtins.str, lib.Unrelated]" +tmp/impl.py:33: note: Revealed type is "builtins.object" +tmp/impl.py:34: note: Revealed type is "builtins.object" + +[case testIncrementalAccessSubmoduleWithoutExplicitImport] +import b +import a + +[file a.py] +import pkg + +pkg.submod.foo() + +[file a.py.2] +import pkg + +pkg.submod.foo() +x = 1 + +[file b.py] +import c + +[file c.py] +from pkg import submod + +[file pkg/__init__.pyi] +[file pkg/submod.pyi] +def foo() -> None: pass +[out] +tmp/a.py:3: error: "object" has no attribute "submod" +[out2] +tmp/a.py:3: error: "object" has no attribute "submod" + +[case testIncrementalAccessSubmoduleWithoutExplicitImportNested] +import a + +[file a.py] +import pandas +pandas.core.dtypes + +[file a.py.2] +import pandas +pandas.core.dtypes +# touch + +[file pandas/__init__.py] +import pandas.core.api + +[file pandas/core/__init__.py] +[file pandas/core/api.py] +import pandas.core.dtypes.dtypes + +[file pandas/core/dtypes/__init__.py] +[file pandas/core/dtypes/dtypes.py] +X = 0 +[out] +[out2] + +[case testIncrementalAccessSubmoduleWithoutExplicitImportNestedFrom] +import a + +[file a.py] +import pandas + +# Although this actually works at runtime, we do not support this, since +# this would cause major slowdown for a rare edge case. This test verifies +# that we fail consistently on cold and warm runs. +pandas.core.dtypes + +[file a.py.2] +import pandas +pandas.core.dtypes + +[file pandas/__init__.py] +import pandas.core.api + +[file pandas/core/__init__.py] +[file pandas/core/api.py] +from pandas.core.dtypes.dtypes import X + +[file pandas/core/dtypes/__init__.py] +[file pandas/core/dtypes/dtypes.py] +X = 0 +[out] +tmp/a.py:6: error: "object" has no attribute "dtypes" +[out2] +tmp/a.py:2: error: "object" has no attribute "dtypes" + +[case testStarImportCycleRedefinition] +import m + +[file m.py] +import a + +[file m.py.2] +import a +reveal_type(a.C) + +[file a/__init__.py] +from a.b import * +from a.c import * +x = 1 + +[file a/b.py] +from other import C +from a.c import y +class C: ... # type: ignore + +[file a/c.py] +from other import C +from a import x +y = 1 + +[file other.py] +class C: ... +[out2] +tmp/m.py:2: note: Revealed type is "def () -> other.C" + +[case testOutputFormatterIncremental] +# flags2: --output json +def wrong() -> int: + if wrong(): + return 0 +[out] +main:2: error: Missing return statement +[out2] +{"file": "main", "line": 2, "column": 0, "message": "Missing return statement", "hint": null, "code": "return", "severity": "error"} diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 17ae6d9934b75..b5b5d778d90fb 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -372,7 +372,7 @@ ao: List[object] a: A def f(): a, aa, ao # Prevent redefinition -a = [] # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A") +a = [] # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "A") aa = [] ao = [] @@ -424,7 +424,7 @@ class B(A): pass [case testLocalVariableInferenceFromEmptyList] import typing def f() -> None: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") b = [None] c = [B()] if int(): @@ -437,14 +437,14 @@ class B: pass [case testNestedListExpressions] # flags: --no-strict-optional from typing import List -aao = None # type: List[List[object]] -aab = None # type: List[List[B]] -ab = None # type: List[B] +aao = None # type: list[list[object]] +aab = None # type: list[list[B]] +ab = None # type: list[B] b = None # type: B o = None # type: object def f(): aao, aab # Prevent redefinition -aao = [[o], ab] # E: List item 1 has incompatible type "List[B]"; expected "List[object]" +aao = [[o], ab] # E: List item 1 has incompatible type "list[B]"; expected "list[object]" aab = [[], [o]] # E: List item 0 has incompatible type "object"; expected "B" aao = [[None], [b], [], [o]] @@ -693,7 +693,6 @@ f(lambda: None) g(lambda: None) [case testIsinstanceInInferredLambda] -# flags: --new-type-inference from typing import TypeVar, Callable, Optional T = TypeVar('T') S = TypeVar('S') @@ -710,6 +709,20 @@ f( A(), r=B()) [builtins fixtures/isinstance.pyi] +[case testLambdaWithFastContainerType] +from collections.abc import Callable +from typing import Never, TypeVar + +T = TypeVar("T") + +def f(a: Callable[[], T]) -> None: ... + +def foo(x: str) -> Never: ... + +f(lambda: [foo(0)]) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" +f(lambda: {"x": foo(0)}) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" +[builtins fixtures/tuple.pyi] + -- Overloads + generic functions -- ----------------------------- @@ -733,7 +746,7 @@ class B: pass m = map(g, [A()]) b = m # type: List[B] -a = m # type: List[A] # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") +a = m # type: List[A] # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]") [builtins fixtures/list.pyi] @@ -756,9 +769,9 @@ if int(): if int(): b = b or [C()] if int(): - a = a or b # E: Incompatible types in assignment (expression has type "Union[List[A], List[B]]", variable has type "List[A]") + a = a or b # E: Incompatible types in assignment (expression has type "Union[list[A], list[B]]", variable has type "list[A]") if int(): - b = b or c # E: Incompatible types in assignment (expression has type "Union[List[B], List[C]]", variable has type "List[B]") + b = b or c # E: Incompatible types in assignment (expression has type "Union[list[B], list[C]]", variable has type "list[B]") [builtins fixtures/list.pyi] @@ -814,7 +827,7 @@ s: List[str] if int(): i = i = [] if int(): - i = s = [] # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]") + i = s = [] # E: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int]") [builtins fixtures/list.pyi] [case testContextForAttributeDeclaredInInit] @@ -842,7 +855,7 @@ T = TypeVar('T') def f(x: Union[List[T], str]) -> None: pass f([1]) f('') -f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Union[List[Never], str]" +f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Union[list[Never], str]" [builtins fixtures/isinstancelist.pyi] [case testIgnoringInferenceContext] @@ -1009,7 +1022,7 @@ class D(C): ... def f(x: List[T], y: List[T]) -> List[T]: ... -f([C()], [D()]) # E: Cannot infer type argument 1 of "f" +f([C()], [D()]) # E: Cannot infer value of type parameter "T" of "f" [builtins fixtures/list.pyi] [case testInferTypeVariableFromTwoGenericTypes3] @@ -1495,3 +1508,93 @@ def g(b: Optional[str]) -> None: z: Callable[[], str] = lambda: reveal_type(b) # N: Revealed type is "builtins.str" f2(lambda: reveal_type(b)) # N: Revealed type is "builtins.str" lambda: reveal_type(b) # N: Revealed type is "builtins.str" + +[case testInferenceContextReturningTypeVarUnion] +from collections.abc import Callable, Iterable +from typing import TypeVar, Union + +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") + +def mymin( + iterable: Iterable[_T1], /, *, key: Callable[[_T1], int], default: _T2 +) -> Union[_T1, _T2]: ... + +def check(paths: Iterable[str], key: Callable[[str], int]) -> Union[str, None]: + return mymin(paths, key=key, default=None) +[builtins fixtures/tuple.pyi] + +[case testBinaryOpInferenceContext] +from typing import Literal, TypeVar + +T = TypeVar("T") + +def identity(x: T) -> T: + return x + +def check1(use: bool, val: str) -> "str | Literal[True]": + return use or identity(val) + +def check2(use: bool, val: str) -> "str | bool": + return use or identity(val) + +def check3(use: bool, val: str) -> "str | Literal[False]": + return use and identity(val) + +def check4(use: bool, val: str) -> "str | bool": + return use and identity(val) +[builtins fixtures/tuple.pyi] + +[case testDictAnyOrLiteralInContext] +from typing import Union, Optional, Any + +def f(x: dict[str, Union[str, None, int]]) -> None: + pass + +def g(x: Optional[dict[str, Any]], s: Optional[str]) -> None: + f(x or {'x': s}) +[builtins fixtures/dict.pyi] + +[case testReturnFallbackInferenceTuple] +from typing import TypeVar, Union + +T = TypeVar("T") +def foo(x: list[T]) -> tuple[T, ...]: ... + +def bar(x: list[int]) -> tuple[Union[str, int], ...]: + return foo(x) + +def bar2(x: list[int]) -> tuple[Union[str, int], ...]: + y = foo(x) + return y +[builtins fixtures/tuple.pyi] + +[case testReturnFallbackInferenceUnion] +from typing import Generic, TypeVar, Union + +T = TypeVar("T") + +class Cls(Generic[T]): + pass + +def inner(c: Cls[T]) -> Union[T, int]: + return 1 + +def outer(c: Cls[T]) -> Union[T, int]: + return inner(c) + +[case testReturnFallbackInferenceAsync] +from typing import Generic, TypeVar, Optional + +T = TypeVar("T") + +class Cls(Generic[T]): + pass + +async def inner(c: Cls[T]) -> Optional[T]: + return None + +async def outer(c: Cls[T]) -> Optional[T]: + return await inner(c) +[builtins fixtures/async_await.pyi] +[typing fixtures/typing-async.pyi] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 0dbefbc774a3b..bc4b56e49622c 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -275,14 +275,14 @@ from typing import Type class Foo: ... A: Type[Foo] = Foo -a, b = Foo # E: "Type[Foo]" object is not iterable -c, d = A # E: "Type[Foo]" object is not iterable +a, b = Foo # E: "type[Foo]" object is not iterable +c, d = A # E: "type[Foo]" object is not iterable class Meta(type): ... class Bar(metaclass=Meta): ... B: Type[Bar] = Bar -e, f = Bar # E: "Type[Bar]" object is not iterable -g, h = B # E: "Type[Bar]" object is not iterable +e, f = Bar # E: "type[Bar]" object is not iterable +g, h = B # E: "type[Bar]" object is not iterable reveal_type(a) # E: Cannot determine type of "a" # N: Revealed type is "Any" reveal_type(b) # E: Cannot determine type of "b" # N: Revealed type is "Any" @@ -330,8 +330,8 @@ a, b, c = Foo d, e, f = A g, h, i = B j, k, l = C -m, n, o = D # E: "Type[Baz]" object is not iterable -p, q, r = E # E: "Type[Spam]" object is not iterable +m, n, o = D # E: "type[Baz]" object is not iterable +p, q, r = E # E: "type[Spam]" object is not iterable s, t, u = Eggs v, w, x = F y, z, aa = G @@ -553,7 +553,7 @@ if int(): b = id(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = id(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") if int(): - a = id(c) # E: Incompatible types in assignment (expression has type "Tuple[A, object]", variable has type "A") + a = id(c) # E: Incompatible types in assignment (expression has type "tuple[A, object]", variable has type "A") if int(): a = id(a) @@ -693,8 +693,8 @@ class A(Generic[T]): pass class B: pass -f(ao, ab) # E: Cannot infer type argument 1 of "f" -f(ab, ao) # E: Cannot infer type argument 1 of "f" +f(ao, ab) # E: Cannot infer value of type parameter "T" of "f" +f(ab, ao) # E: Cannot infer value of type parameter "T" of "f" f(ao, ao) f(ab, ab) @@ -843,7 +843,7 @@ if int(): l = [A()] lb = [b] if int(): - l = lb # E: Incompatible types in assignment (expression has type "List[bool]", variable has type "List[A]") + l = lb # E: Incompatible types in assignment (expression has type "list[bool]", variable has type "list[A]") [builtins fixtures/for.pyi] [case testGenericFunctionWithTypeTypeAsCallable] @@ -871,15 +871,15 @@ f(1, 1)() # E: "int" not callable def g(x: Union[T, List[T]]) -> List[T]: pass def h(x: List[str]) -> None: pass -g('a')() # E: "List[str]" not callable +g('a')() # E: "list[str]" not callable # The next line is a case where there are multiple ways to satisfy a constraint -# involving a Union. Either T = List[str] or T = str would turn out to be valid, +# involving a Union. Either T = list[str] or T = str would turn out to be valid, # but mypy doesn't know how to branch on these two options (and potentially have # to backtrack later) and defaults to T = Never. The result is an # awkward error message. Either a better error message, or simply accepting the # call, would be preferable here. -g(['a']) # E: Argument 1 to "g" has incompatible type "List[str]"; expected "List[Never]" +g(['a']) # E: Argument 1 to "g" has incompatible type "list[str]"; expected "list[Never]" h(g(['a'])) @@ -888,7 +888,7 @@ a = [1] b = ['b'] i(a, a, b) i(b, a, b) -i(a, b, b) # E: Argument 1 to "i" has incompatible type "List[int]"; expected "List[str]" +i(a, b, b) # E: Argument 1 to "i" has incompatible type "list[int]"; expected "list[str]" [builtins fixtures/list.pyi] [case testCallableListJoinInference] @@ -972,7 +972,7 @@ from typing import TypeVar, Union, List T = TypeVar('T') def f() -> List[T]: pass d1 = f() # type: Union[List[int], str] -d2 = f() # type: Union[int, str] # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "Union[int, str]") +d2 = f() # type: Union[int, str] # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "Union[int, str]") def g(x: T) -> List[T]: pass d3 = g(1) # type: Union[List[int], List[str]] [builtins fixtures/list.pyi] @@ -988,7 +988,7 @@ a = k2 if int(): a = k2 if int(): - a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T@k1]], List[Union[T@k1, int]]]", variable has type "Callable[[S, List[T@k2]], List[Union[T@k2, int]]]") + a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, list[T@k1]], list[Union[T@k1, int]]]", variable has type "Callable[[S, list[T@k2]], list[Union[T@k2, int]]]") b = k1 if int(): b = k1 @@ -1041,7 +1041,7 @@ d = {a:b} if int(): d = d_ab() if int(): - d = d_aa() # E: Incompatible types in assignment (expression has type "Dict[A, A]", variable has type "Dict[A, B]") + d = d_aa() # E: Incompatible types in assignment (expression has type "dict[A, A]", variable has type "dict[A, B]") [builtins fixtures/dict.pyi] [case testSetLiteral] @@ -1056,7 +1056,7 @@ if int(): if int(): s = s_i() if int(): - s = s_s() # E: Incompatible types in assignment (expression has type "Set[str]", variable has type "Set[int]") + s = s_s() # E: Incompatible types in assignment (expression has type "set[str]", variable has type "set[int]") [builtins fixtures/set.pyi] [case testSetWithStarExpr] @@ -1109,7 +1109,7 @@ def f(*, x: int) -> int: ... def g(*, y: int) -> int: ... def h(*, x: int) -> int: ... -list_1 = [f, g] # E: List item 0 has incompatible type "Callable[[NamedArg(int, 'x')], int]"; expected "Callable[[NamedArg(int, 'y')], int]" +list_1 = [f, g] # E: List item 0 has incompatible type "def f(*, x: int) -> int"; expected "def g(*, y: int) -> int" list_2 = [f, h] [builtins fixtures/list.pyi] @@ -1238,6 +1238,36 @@ class B: pass [builtins fixtures/for.pyi] [out] +[case testForStatementIndexNarrowing] +from typing 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] +[typing fixtures/typing-full.pyi] + -- Regression tests -- ---------------- @@ -1361,28 +1391,26 @@ from typing import List, Callable li = [1] l = lambda: li f1 = l # type: Callable[[], List[int]] -f2 = l # type: Callable[[], List[str]] # E: Incompatible types in assignment (expression has type "Callable[[], List[int]]", variable has type "Callable[[], List[str]]") +f2 = l # type: Callable[[], List[str]] # E: Incompatible types in assignment (expression has type "Callable[[], list[int]]", variable has type "Callable[[], list[str]]") [builtins fixtures/list.pyi] [case testInferLambdaType2] from typing import List, Callable l = lambda: [B()] f1 = l # type: Callable[[], List[B]] -f2 = l # type: Callable[[], List[A]] # E: Incompatible types in assignment (expression has type "Callable[[], List[B]]", variable has type "Callable[[], List[A]]") +f2 = l # type: Callable[[], List[A]] # E: Incompatible types in assignment (expression has type "Callable[[], list[B]]", variable has type "Callable[[], list[A]]") class A: pass class B: pass [builtins fixtures/list.pyi] [case testUninferableLambda] -# flags: --new-type-inference from typing import TypeVar, Callable X = TypeVar('X') def f(x: Callable[[X], X]) -> X: pass y = f(lambda x: x) # E: Need type annotation for "y" [case testUninferableLambdaWithTypeError] -# flags: --new-type-inference from typing import TypeVar, Callable X = TypeVar('X') def f(x: Callable[[X], X], y: str) -> X: pass @@ -1406,7 +1434,7 @@ from typing import Callable def f(a: Callable[..., None] = lambda *a, **k: None): pass -def g(a: Callable[..., None] = lambda *a, **k: 1): # E: Incompatible default for argument "a" (default has type "Callable[[VarArg(Any), KwArg(Any)], int]", argument has type "Callable[..., None]") +def g(a: Callable[..., None] = lambda *a, **k: 1): # E: Incompatible default for argument "a" (default has type "def (*a: Any, **k: Any) -> int", argument has type "Callable[..., None]") pass [builtins fixtures/dict.pyi] @@ -1461,7 +1489,7 @@ o: List[object] a2 = a or [] if int(): a = a2 - a2 = o # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[A]") + a2 = o # E: Incompatible types in assignment (expression has type "list[object]", variable has type "list[A]") class A: pass [builtins fixtures/list.pyi] @@ -1505,8 +1533,8 @@ if int(): a = x2 if int(): a = x3 \ - # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]") \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [typing fixtures/typing-medium.pyi] @@ -1528,8 +1556,8 @@ if int(): a = x2 if int(): a = x3 \ - # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]") \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [typing fixtures/typing-medium.pyi] @@ -1552,28 +1580,28 @@ a.append(0) # E: Argument 1 to "append" of "list" has incompatible type "int"; [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyAndNotAnnotated] -a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") +a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyAndReadBeforeAppend] -a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") +a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") if a: pass -a.xyz # E: "List[Any]" has no attribute "xyz" +a.xyz # E: "list[Any]" has no attribute "xyz" a.append('') [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyAndIncompleteTypeInAppend] -a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") +a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") a.append([]) -a() # E: "List[Any]" not callable +a() # E: "list[Any]" not callable [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyAndMultipleAssignment] a, b = [], [] a.append(1) b.append('') -a() # E: "List[int]" not callable -b() # E: "List[str]" not callable +a() # E: "list[int]" not callable +b() # E: "list[str]" not callable [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyInFunction] @@ -1585,7 +1613,7 @@ def f() -> None: [case testInferListInitializedToEmptyAndNotAnnotatedInFunction] def f() -> None: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def g() -> None: pass @@ -1595,9 +1623,9 @@ a.append(1) [case testInferListInitializedToEmptyAndReadBeforeAppendInFunction] def f() -> None: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") if a: pass - a.xyz # E: "List[Any]" has no attribute "xyz" + a.xyz # E: "list[Any]" has no attribute "xyz" a.append('') [builtins fixtures/list.pyi] @@ -1610,7 +1638,7 @@ class A: [case testInferListInitializedToEmptyAndNotAnnotatedInClassBody] class A: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") class B: a = [] @@ -1628,7 +1656,7 @@ class A: [case testInferListInitializedToEmptyAndNotAnnotatedInMethod] class A: def f(self) -> None: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyInMethodViaAttribute] @@ -1640,12 +1668,12 @@ class A: self.a.append('') # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" [builtins fixtures/list.pyi] -[case testInferListInitializedToEmptyInClassBodyAndOverriden] +[case testInferListInitializedToEmptyInClassBodyAndOverridden] from typing import List class A: def __init__(self) -> None: - self.x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + self.x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") class B(A): @property @@ -1674,37 +1702,39 @@ a.add('') # E: Argument 1 to "add" of "set" has incompatible type "str"; expect [case testInferDictInitializedToEmpty] a = {} a[1] = '' -a() # E: "Dict[int, str]" not callable +a() # E: "dict[int, str]" not callable [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyUsingUpdate] a = {} a.update({'': 42}) -a() # E: "Dict[str, int]" not callable +a() # E: "dict[str, int]" not callable [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyUsingUpdateError] -a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") -a.update([1, 2]) # E: Argument 1 to "update" of "dict" has incompatible type "List[int]"; expected "SupportsKeysAndGetItem[Any, Any]" \ +a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") +a.update([1, 2]) # E: Argument 1 to "update" of "dict" has incompatible type "list[int]"; expected "SupportsKeysAndGetItem[Any, Any]" \ # N: "list" is missing following "SupportsKeysAndGetItem" protocol member: \ # N: keys -a() # E: "Dict[Any, Any]" not callable +a() # E: "dict[Any, Any]" not callable [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyAndIncompleteTypeInUpdate] -a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") +a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") a[1] = {} -b = {} # E: Need type annotation for "b" (hint: "b: Dict[, ] = ...") +b = {} # E: Need type annotation for "b" (hint: "b: dict[, ] = ...") b[{}] = 1 [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyAndUpdatedFromMethod] +# flags: --no-local-partial-types map = {} def add() -> None: map[1] = 2 [builtins fixtures/dict.pyi] [case testInferDictInitializedToEmptyAndUpdatedFromMethodUnannotated] +# flags: --no-local-partial-types map = {} def add(): map[1] = 2 @@ -1722,8 +1752,8 @@ def f(blocks: object): to_process = [] to_process = list(blocks) # E: No overload variant of "list" matches argument type "object" \ # N: Possible overload variants: \ - # N: def [T] __init__(self) -> List[T] \ - # N: def [T] __init__(self, x: Iterable[T]) -> List[T] + # N: def [T] __init__(self) -> list[T] \ + # N: def [T] __init__(self, x: Iterable[T]) -> list[T] [builtins fixtures/list.pyi] [case testInferListInitializedToEmptyAndAssigned] @@ -1744,9 +1774,9 @@ if bool(): d = {1: 'x'} reveal_type(d) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" -dd = {} # E: Need type annotation for "dd" (hint: "dd: Dict[, ] = ...") +dd = {} # E: Need type annotation for "dd" (hint: "dd: dict[, ] = ...") if bool(): - dd = [1] # E: Incompatible types in assignment (expression has type "List[int]", variable has type "Dict[Any, Any]") + dd = [1] # E: Incompatible types in assignment (expression has type "list[int]", variable has type "dict[Any, Any]") reveal_type(dd) # N: Revealed type is "builtins.dict[Any, Any]" [builtins fixtures/dict.pyi] @@ -1764,27 +1794,27 @@ reveal_type(oo) # N: Revealed type is "collections.OrderedDict[builtins.int, bui [builtins fixtures/dict.pyi] [case testEmptyCollectionAssignedToVariableTwiceIncremental] -x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") +x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") y = x x = [] reveal_type(x) # N: Revealed type is "builtins.list[Any]" -d = {} # E: Need type annotation for "d" (hint: "d: Dict[, ] = ...") +d = {} # E: Need type annotation for "d" (hint: "d: dict[, ] = ...") z = d d = {} reveal_type(d) # N: Revealed type is "builtins.dict[Any, Any]" [builtins fixtures/dict.pyi] [out2] -main:1: error: Need type annotation for "x" (hint: "x: List[] = ...") +main:1: error: Need type annotation for "x" (hint: "x: list[] = ...") main:4: note: Revealed type is "builtins.list[Any]" -main:5: error: Need type annotation for "d" (hint: "d: Dict[, ] = ...") +main:5: error: Need type annotation for "d" (hint: "d: dict[, ] = ...") main:8: note: Revealed type is "builtins.dict[Any, Any]" [case testEmptyCollectionAssignedToVariableTwiceNoReadIncremental] -x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") +x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") x = [] [builtins fixtures/list.pyi] [out2] -main:1: error: Need type annotation for "x" (hint: "x: List[] = ...") +main:1: error: Need type annotation for "x" (hint: "x: list[] = ...") [case testInferAttributeInitializedToEmptyAndAssigned] class C: @@ -1824,7 +1854,7 @@ reveal_type(C().a) # N: Revealed type is "Union[builtins.int, None]" [case testInferAttributeInitializedToEmptyNonSelf] class C: def __init__(self) -> None: - self.a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + self.a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") if bool(): a = self a.a = [1] @@ -1835,7 +1865,7 @@ reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" [case testInferAttributeInitializedToEmptyAndAssignedOtherMethod] class C: def __init__(self) -> None: - self.a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + self.a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def meth(self) -> None: self.a = [1] reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" @@ -1844,7 +1874,7 @@ reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" [case testInferAttributeInitializedToEmptyAndAppendedOtherMethod] class C: def __init__(self) -> None: - self.a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + self.a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def meth(self) -> None: self.a.append(1) reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" @@ -1853,7 +1883,7 @@ reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" [case testInferAttributeInitializedToEmptyAndAssignedItemOtherMethod] class C: def __init__(self) -> None: - self.a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") + self.a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") def meth(self) -> None: self.a[0] = 'yes' reveal_type(C().a) # N: Revealed type is "builtins.dict[Any, Any]" @@ -1869,7 +1899,7 @@ reveal_type(C().a) # N: Revealed type is "None" [case testInferAttributeInitializedToEmptyAndAssignedClassBody] class C: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def __init__(self) -> None: self.a = [1] reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" @@ -1877,7 +1907,7 @@ reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" [case testInferAttributeInitializedToEmptyAndAppendedClassBody] class C: - a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") + a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def __init__(self) -> None: self.a.append(1) reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" @@ -1885,13 +1915,14 @@ reveal_type(C().a) # N: Revealed type is "builtins.list[Any]" [case testInferAttributeInitializedToEmptyAndAssignedItemClassBody] class C: - a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") + a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") def __init__(self) -> None: self.a[0] = 'yes' reveal_type(C().a) # N: Revealed type is "builtins.dict[Any, Any]" [builtins fixtures/dict.pyi] [case testInferAttributeInitializedToNoneAndAssignedClassBody] +# flags: --no-local-partial-types class C: a = None def __init__(self) -> None: @@ -1933,6 +1964,31 @@ if 'x' in d: # E: "None" has no attribute "__iter__" (not iterable) reveal_type(d) # N: Revealed type is "None" [builtins fixtures/dict.pyi] +[case testNoWrongUnreachableWarningWithNoStrictOptionalAndFinalInstance] +# flags: --no-strict-optional --warn-unreachable +from typing import final, Optional + +@final +class C: ... + +x: Optional[C] +if not x: + x = C() +[builtins fixtures/dict.pyi] + +[case testNoWrongUnreachableWarningWithNoStrictOptionalAndEnumLiteral] +# flags: --no-strict-optional --warn-unreachable +from enum import Enum +from typing import Literal, Optional + +class E(Enum): + a = 1 + +x: Optional[Literal[E.a]] +if not x: + x = E.a +[builtins fixtures/dict.pyi] + [case testInferFromEmptyListWhenUsingInWithStrictEquality] # flags: --strict-equality def f() -> None: @@ -2009,7 +2065,7 @@ x.append('') # E: Argument 1 to "append" of "list" has incompatible type "str"; x = None if object(): # Promote from partial None to partial list. - x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") x [builtins fixtures/list.pyi] @@ -2018,7 +2074,7 @@ def f() -> None: x = None if object(): # Promote from partial None to partial list. - x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") [builtins fixtures/list.pyi] [out] @@ -2040,6 +2096,7 @@ x = 1 [out] [case testPartiallyInitializedVariableDoesNotEscapeScope2] +# flags: --no-local-partial-types x = None def f() -> None: x = None @@ -2085,36 +2142,32 @@ class C: -- ------------------------ [case testPartialTypeErrorSpecialCase1] +# flags: --no-local-partial-types # This used to crash. class A: x = None def f(self) -> None: - for a in self.x: + for a in self.x: # E: "None" has no attribute "__iter__" (not iterable) pass [builtins fixtures/for.pyi] -[out] -main:5: error: "None" has no attribute "__iter__" (not iterable) [case testPartialTypeErrorSpecialCase2] # This used to crash. class A: - x = [] + x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") def f(self) -> None: for a in self.x: pass [builtins fixtures/for.pyi] -[out] -main:3: error: Need type annotation for "x" (hint: "x: List[] = ...") [case testPartialTypeErrorSpecialCase3] +# flags: --no-local-partial-types class A: x = None def f(self) -> None: - for a in A.x: + for a in A.x: # E: "None" has no attribute "__iter__" (not iterable) pass [builtins fixtures/for.pyi] -[out] -main:4: error: "None" has no attribute "__iter__" (not iterable) [case testPartialTypeErrorSpecialCase4] # This used to crash. @@ -2227,7 +2280,7 @@ def g(d: Dict[str, int]) -> None: pass def f() -> None: x = {} x[1] = y - g(x) # E: Argument 1 to "g" has incompatible type "Dict[int, str]"; expected "Dict[str, int]" + g(x) # E: Argument 1 to "g" has incompatible type "dict[int, str]"; expected "dict[str, int]" x[1] = 1 # E: Incompatible types in assignment (expression has type "int", target has type "str") x[1] = '' y = '' @@ -2241,7 +2294,7 @@ def f() -> None: x = {} y x[1] = 1 - g(x) # E: Argument 1 to "g" has incompatible type "Dict[int, int]"; expected "Dict[str, int]" + g(x) # E: Argument 1 to "g" has incompatible type "dict[int, int]"; expected "dict[str, int]" y = '' [builtins fixtures/dict.pyi] [out] @@ -2260,7 +2313,7 @@ def f() -> None: y = o x = [] x.append(y) - x() # E: "List[int]" not callable + x() # E: "list[int]" not callable o = 1 [builtins fixtures/list.pyi] [out] @@ -2270,16 +2323,16 @@ def f() -> None: y = o x = {} x[''] = y - x() # E: "Dict[str, int]" not callable + x() # E: "dict[str, int]" not callable o = 1 [builtins fixtures/dict.pyi] [out] [case testMultipassAndPartialTypesSpecialCase3] def f() -> None: - x = {} # E: Need type annotation for "x" (hint: "x: Dict[, ] = ...") + x = {} # E: Need type annotation for "x" (hint: "x: dict[, ] = ...") y = o - z = {} # E: Need type annotation for "z" (hint: "z: Dict[, ] = ...") + z = {} # E: Need type annotation for "z" (hint: "z: dict[, ] = ...") o = 1 [builtins fixtures/dict.pyi] [out] @@ -2360,7 +2413,7 @@ b: Union[str, tuple] def f(): pass def g(x: Union[int, str]): pass c = a if f() else b -g(c) # E: Argument 1 to "g" has incompatible type "Union[int, str, Tuple[Any, ...]]"; expected "Union[int, str]" +g(c) # E: Argument 1 to "g" has incompatible type "Union[int, str, tuple[Any, ...]]"; expected "Union[int, str]" [builtins fixtures/tuple.pyi] [case testUnificationMultipleInheritance] @@ -2399,58 +2452,58 @@ a2.foo2() [case testUnificationEmptyListLeft] def f(): pass a = [] if f() else [0] -a() # E: "List[int]" not callable +a() # E: "list[int]" not callable [builtins fixtures/list.pyi] [case testUnificationEmptyListRight] def f(): pass a = [0] if f() else [] -a() # E: "List[int]" not callable +a() # E: "list[int]" not callable [builtins fixtures/list.pyi] [case testUnificationEmptyListLeftInContext] from typing import List def f(): pass -a = [] if f() else [0] # type: List[int] -a() # E: "List[int]" not callable +a = [] if f() else [0] # type: list[int] +a() # E: "list[int]" not callable [builtins fixtures/list.pyi] [case testUnificationEmptyListRightInContext] # TODO Find an example that really needs the context from typing import List def f(): pass -a = [0] if f() else [] # type: List[int] -a() # E: "List[int]" not callable +a = [0] if f() else [] # type: list[int] +a() # E: "list[int]" not callable [builtins fixtures/list.pyi] [case testUnificationEmptySetLeft] def f(): pass a = set() if f() else {0} -a() # E: "Set[int]" not callable +a() # E: "set[int]" not callable [builtins fixtures/set.pyi] [case testUnificationEmptyDictLeft] def f(): pass a = {} if f() else {0: 0} -a() # E: "Dict[int, int]" not callable +a() # E: "dict[int, int]" not callable [builtins fixtures/dict.pyi] [case testUnificationEmptyDictRight] def f(): pass a = {0: 0} if f() else {} -a() # E: "Dict[int, int]" not callable +a() # E: "dict[int, int]" not callable [builtins fixtures/dict.pyi] [case testUnificationDictWithEmptyListLeft] def f(): pass a = {0: []} if f() else {0: [0]} -a() # E: "Dict[int, List[int]]" not callable +a() # E: "dict[int, list[int]]" not callable [builtins fixtures/dict.pyi] [case testUnificationDictWithEmptyListRight] def f(): pass a = {0: [0]} if f() else {0: []} -a() # E: "Dict[int, List[int]]" not callable +a() # E: "dict[int, list[int]]" not callable [builtins fixtures/dict.pyi] [case testMisguidedSetItem] @@ -2459,16 +2512,16 @@ T = TypeVar('T') class C(Sequence[T], Generic[T]): pass C[0] = 0 [out] -main:4: error: Unsupported target for indexed assignment ("Type[C[Any]]") +main:4: error: Unsupported target for indexed assignment ("type[C[T]]") main:4: error: Invalid type: try using Literal[0] instead? [case testNoCrashOnPartialMember] +# flags: --no-local-partial-types class C: x = None def __init__(self) -> None: - self.x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + self.x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") [builtins fixtures/list.pyi] -[out] [case testNoCrashOnPartialVariable] from typing import Tuple, TypeVar @@ -2480,19 +2533,18 @@ x = None (x,) = f('') reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/tuple.pyi] -[out] [case testNoCrashOnPartialVariable2] +# flags: --no-local-partial-types from typing import Tuple, TypeVar T = TypeVar('T', bound=str) def f() -> Tuple[T]: ... -x = None +x = None # E: Need type annotation for "x" if int(): (x,) = f() [builtins fixtures/tuple.pyi] -[out] [case testNoCrashOnPartialVariable3] from typing import Tuple, TypeVar @@ -2504,7 +2556,76 @@ x = None (x, x) = f('') reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/tuple.pyi] -[out] + +[case testRejectsPartialWithUninhabited] +from typing import Generic, TypeVar +T = TypeVar('T') + +class Foo(Generic[T]): ... + +def check() -> None: + x = None # E: Need type annotation for "x" + if int(): + x = Foo() + reveal_type(x) # N: Revealed type is "__main__.Foo[Any]" + reveal_type(x) # N: Revealed type is "Union[__main__.Foo[Any], None]" + +[case testRejectsPartialWithUninhabited2] +from typing import Generic, TypeVar +T = TypeVar('T') + +class Foo(Generic[T]): ... + +x = None # E: Need type annotation for "x" + +def check() -> None: + global x + x = Foo() + reveal_type(x) # N: Revealed type is "__main__.Foo[Any]" + +reveal_type(x) # N: Revealed type is "Union[__main__.Foo[Any], None]" + +[case testRejectsPartialWithUninhabited3] +# Without force-rejecting Partial, this crashes: +# https://github.com/python/mypy/issues/16573 +from typing import Generic, TypeVar +T = TypeVar('T') + +class Foo(Generic[T]): ... + +def check() -> None: + client = None # E: Need type annotation for "client" + + if client := Foo(): + reveal_type(client) # N: Revealed type is "__main__.Foo[Any]" + + reveal_type(client) # N: Revealed type is "Union[__main__.Foo[Any], None]" + + client = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Optional[Foo[Any]]") + reveal_type(client) # N: Revealed type is "Union[__main__.Foo[Any], None]" + +[case testRejectsPartialWithUninhabitedIndependently] +from typing import Generic, TypeVar +T = TypeVar('T') + +class Foo(Generic[T]): ... + +client = None # E: Need type annotation for "client" + +def bad() -> None: + global client + client = Foo() + reveal_type(client) # N: Revealed type is "__main__.Foo[Any]" + +def good() -> None: + global client + client = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Optional[Foo[Any]]") + reveal_type(client) # N: Revealed type is "Union[__main__.Foo[Any], None]" + +def bad2() -> None: + global client + client = Foo() + reveal_type(client) # N: Revealed type is "__main__.Foo[Any]" [case testInferenceNestedTuplesFromGenericIterable] from typing import Tuple, TypeVar @@ -2519,7 +2640,6 @@ def main() -> None: reveal_type(a) # N: Revealed type is "builtins.int" reveal_type(b) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] -[out] [case testDontMarkUnreachableAfterInferenceUninhabited] from typing import TypeVar @@ -2644,7 +2764,7 @@ class A: [case testLocalPartialTypesWithClassAttributeInitializedToEmptyDict] # flags: --local-partial-types class A: - x = {} # E: Need type annotation for "x" (hint: "x: Dict[, ] = ...") + x = {} # E: Need type annotation for "x" (hint: "x: dict[, ] = ...") def f(self) -> None: self.x[0] = '' @@ -2667,7 +2787,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" [case testLocalPartialTypesWithGlobalInitializedToEmptyList2] # flags: --local-partial-types -a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") +a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def f() -> None: a.append(1) @@ -2678,7 +2798,7 @@ reveal_type(a) # N: Revealed type is "builtins.list[Any]" [case testLocalPartialTypesWithGlobalInitializedToEmptyList3] # flags: --local-partial-types -a = [] # E: Need type annotation for "a" (hint: "a: List[] = ...") +a = [] # E: Need type annotation for "a" (hint: "a: list[] = ...") def f(): a.append(1) @@ -2700,7 +2820,7 @@ reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" [case testLocalPartialTypesWithGlobalInitializedToEmptyDict2] # flags: --local-partial-types -a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") +a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") def f() -> None: a[0] = '' @@ -2711,7 +2831,7 @@ reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]" [case testLocalPartialTypesWithGlobalInitializedToEmptyDict3] # flags: --local-partial-types -a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") +a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") def f(): a[0] = '' @@ -3274,7 +3394,7 @@ class A: s = self s.y['x'].append(1) -x = {} # E: Need type annotation for "x" (hint: "x: Dict[, ] = ...") +x = {} # E: Need type annotation for "x" (hint: "x: dict[, ] = ...") x['x'].append(1) y = defaultdict(list) # E: Need type annotation for "y" @@ -3428,7 +3548,7 @@ if x: [builtins fixtures/dict.pyi] [case testSuggestPep604AnnotationForPartialNone] -# flags: --local-partial-types --python-version 3.10 +# flags: --local-partial-types --python-version 3.10 --no-force-union-syntax x = None # E: Need type annotation for "x" (hint: "x: | None = ...") [case testTupleContextFromIterable] @@ -3507,13 +3627,13 @@ class P: class M: x: List[str] class C(P, M): - x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") reveal_type(C.x) # N: Revealed type is "builtins.list[Any]" [builtins fixtures/list.pyi] [case testNoPartialInSupertypeAsContext] class A: - args = {} # E: Need type annotation for "args" (hint: "args: Dict[, ] = ...") + args = {} # E: Need type annotation for "args" (hint: "args: dict[, ] = ...") def f(self) -> None: value = {1: "Hello"} class B(A): @@ -3574,7 +3694,7 @@ S = TypeVar('S') def f(x: Callable[[T, S], None]) -> Tuple[T, S]: ... def g(*x: int) -> None: ... -reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(g)) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableStarVsPos] @@ -3588,7 +3708,7 @@ class Call(Protocol[T, S]): def f(x: Call[T, S]) -> Tuple[T, S]: ... def g(*x: int) -> None: ... -reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(g)) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsStar] @@ -3602,7 +3722,7 @@ class Call(Protocol[T, S]): def f(x: Call[T, S]) -> Tuple[T, S]: ... def g(**kwargs: int) -> None: ... -reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(g)) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableStarVsNamed] @@ -3616,7 +3736,7 @@ class Call(Protocol[T, S]): def f(x: Call[T, S]) -> Tuple[T, S]: ... def g(**kwargs: int) -> None: pass -reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(g)) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsNamed] @@ -3632,7 +3752,7 @@ def f(x: Call[T, S]) -> Tuple[T, S]: ... # Note: order of names is different w.r.t. protocol def g(*, y: int, x: str) -> None: pass -reveal_type(f(g)) # N: Revealed type is "Tuple[builtins.str, builtins.int]" +reveal_type(f(g)) # N: Revealed type is "tuple[builtins.str, builtins.int]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallablePosOnlyVsNamed] @@ -3647,8 +3767,9 @@ class Call(Protocol[T]): 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]" +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]" \ + # N: "Call[Never].__call__" has type "def __call__(self, *, x: Never) -> None" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsPosOnly] @@ -3663,8 +3784,9 @@ class Call(Protocol[T]): 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]" +reveal_type(f(g)) # N: Revealed type is "tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "def g(*, x: str) -> None"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[Never], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallablePosOnlyVsKwargs] @@ -3679,8 +3801,9 @@ class Call(Protocol[T]): 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]" +reveal_type(f(g)) # N: Revealed type is "tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "def g(**x: str) -> None"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[Never], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsArgs] @@ -3695,8 +3818,9 @@ class Call(Protocol[T]): 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]" +reveal_type(f(g)) # N: Revealed type is "tuple[Never, Never]" \ + # E: Argument 1 to "f" has incompatible type "def g(*args: str) -> None"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "def __call__(self, *, x: Never) -> None" [builtins fixtures/list.pyi] [case testInferenceAgainstTypeVarActualBound] @@ -3738,8 +3862,8 @@ reveal_type(f(x, [])) # N: Revealed type is "builtins.str" reveal_type(f(["yes"], [])) # N: Revealed type is "builtins.str" empty: List[NoReturn] -f(x, empty) # E: Cannot infer type argument 1 of "f" -f(["no"], empty) # E: Cannot infer type argument 1 of "f" +f(x, empty) # E: Cannot infer value of type parameter "T" of "f" +f(["no"], empty) # E: Cannot infer value of type parameter "T" of "f" [builtins fixtures/list.pyi] [case testInferenceWorksWithEmptyCollectionsUnion] @@ -3747,10 +3871,18 @@ from typing import Any, Dict, NoReturn, NoReturn, Union def foo() -> Union[Dict[str, Any], Dict[int, Any]]: return {} +[builtins fixtures/dict.pyi] + +[case testExistingEmptyCollectionDoesNotUpcast] +from typing import Any, Dict, NoReturn, NoReturn, Union empty: Dict[NoReturn, NoReturn] + +def foo() -> Dict[str, Any]: + return empty # E: Incompatible return value type (got "dict[Never, Never]", expected "dict[str, Any]") + def bar() -> Union[Dict[str, Any], Dict[int, Any]]: - return empty + return empty # E: Incompatible return value type (got "dict[Never, Never]", expected "Union[dict[str, Any], dict[int, Any]]") [builtins fixtures/dict.pyi] [case testUpperBoundInferenceFallbackNotOverused] @@ -3837,7 +3969,7 @@ def a2(check: bool, a: B[str]) -> None: reveal_type(a if check else {}) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]" def a3() -> None: - a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") + a = {} # E: Need type annotation for "a" (hint: "a: dict[, ] = ...") b = {1: {}} # E: Need type annotation for "b" c = {1: {}, 2: {"key": {}}} # E: Need type annotation for "c" reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]" @@ -3851,3 +3983,281 @@ def a4(x: List[str], y: List[Never]) -> None: reveal_type(z2) # N: Revealed type is "builtins.list[builtins.object]" z1[1].append("asdf") # E: "object" has no attribute "append" [builtins fixtures/dict.pyi] + + +[case testDeterminismCommutativityWithJoinInvolvingProtocolBaseAndPromotableType] +# flags: --python-version 3.11 +# Regression test for https://github.com/python/mypy/issues/16979#issuecomment-1982246306 +from __future__ import annotations + +from typing import Any, Generic, Protocol, TypeVar, overload, cast +from typing_extensions import Never + +T = TypeVar("T") +U = TypeVar("U") + +class _SupportsCompare(Protocol): + def __lt__(self, other: Any, /) -> bool: + return True + +class Comparable(_SupportsCompare): + pass + +comparable: Comparable = Comparable() + +from typing import _promote + +class floatlike: + def __lt__(self, other: floatlike, /) -> bool: ... + +@_promote(floatlike) +class intlike: + def __lt__(self, other: intlike, /) -> bool: ... + + +class A(Generic[T, U]): + @overload + def __init__(self: A[T, T], a: T, b: T, /) -> None: ... # type: ignore[overload-overlap] + @overload + def __init__(self: A[T, U], a: T, b: U, /) -> Never: ... + def __init__(self, *a) -> None: ... + +def join(a: T, b: T) -> T: ... + +reveal_type(join(intlike(), comparable)) # N: Revealed type is "__main__._SupportsCompare" +reveal_type(join(comparable, intlike())) # N: Revealed type is "__main__._SupportsCompare" +reveal_type(A(intlike(), comparable)) # N: Revealed type is "__main__.A[__main__._SupportsCompare, __main__._SupportsCompare]" +reveal_type(A(comparable, intlike())) # N: Revealed type is "__main__.A[__main__._SupportsCompare, __main__._SupportsCompare]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-medium.pyi] + +[case testTupleJoinFallbackInference] +foo = [ + (1, ("a", "b")), + (2, []), +] +reveal_type(foo) # N: Revealed type is "builtins.list[tuple[builtins.int, typing.Sequence[builtins.str]]]" +[builtins fixtures/tuple.pyi] + +[case testForLoopIndexVaribaleNarrowing1] +# flags: --local-partial-types +from typing import Union +x: Union[int, str] +x = "abc" +for x in list[int](): + reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testForLoopIndexVaribaleNarrowing2] +# flags: --enable-error-code=redundant-expr +from typing import Union +x: Union[int, str] +x = "abc" +for x in list[int](): + reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNarrowInFunctionDefer] +from typing import Optional, Callable, TypeVar + +def top() -> None: + x: Optional[int] + assert x is not None + + def foo() -> None: + defer() + reveal_type(x) # N: Revealed type is "builtins.int" + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def defer() -> int: ... + +[case testDeferMethodOfNestedClass] +from typing import Optional, Callable, TypeVar + +class Out: + def meth(self) -> None: + class In: + def meth(self) -> None: + reveal_type(defer()) # N: Revealed type is "builtins.int" + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def defer() -> int: ... + +[case testVariableDeferredWithNestedFunction] +from typing import Callable, TypeVar + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def f() -> None: + x = 1 + f() # defer current node + x = x + + def nested() -> None: + ... + + # The type below should not be Any. + reveal_type(x) # N: Revealed type is "builtins.int" + +[case testInferenceMappingTypeVarGet] +from typing import Generic, TypeVar, Union + +_T = TypeVar("_T") +_K = TypeVar("_K") +_V = TypeVar("_V") + +class Mapping(Generic[_K, _V]): + def get(self, key: _K, default: Union[_V, _T]) -> Union[_V, _T]: ... + +def check(mapping: Mapping[str, _T]) -> None: + ok1 = mapping.get("", "") + reveal_type(ok1) # N: Revealed type is "Union[_T`-1, builtins.str]" + ok2: Union[_T, str] = mapping.get("", "") +[builtins fixtures/tuple.pyi] + +[case testInferWalrusAssignmentAttrInCondition] +class Foo: + def __init__(self, value: bool) -> None: + self.value = value + +def check_and(maybe: bool) -> None: + foo = None + if maybe and (foo := Foo(True)).value: + reveal_type(foo) # N: Revealed type is "__main__.Foo" + else: + reveal_type(foo) # N: Revealed type is "Union[__main__.Foo, None]" + +def check_and_nested(maybe: bool) -> None: + foo = None + bar = None + baz = None + if maybe and (foo := (bar := (baz := Foo(True)))).value: + reveal_type(foo) # N: Revealed type is "__main__.Foo" + reveal_type(bar) # N: Revealed type is "__main__.Foo" + reveal_type(baz) # N: Revealed type is "__main__.Foo" + else: + reveal_type(foo) # N: Revealed type is "Union[__main__.Foo, None]" + reveal_type(bar) # N: Revealed type is "Union[__main__.Foo, None]" + reveal_type(baz) # N: Revealed type is "Union[__main__.Foo, None]" + +def check_or(maybe: bool) -> None: + foo = None + if maybe or (foo := Foo(True)).value: + reveal_type(foo) # N: Revealed type is "Union[__main__.Foo, None]" + else: + reveal_type(foo) # N: Revealed type is "__main__.Foo" + +def check_or_nested(maybe: bool) -> None: + foo = None + bar = None + baz = None + if maybe and (foo := (bar := (baz := Foo(True)))).value: + reveal_type(foo) # N: Revealed type is "__main__.Foo" + reveal_type(bar) # N: Revealed type is "__main__.Foo" + reveal_type(baz) # N: Revealed type is "__main__.Foo" + else: + reveal_type(foo) # N: Revealed type is "Union[__main__.Foo, None]" + reveal_type(bar) # N: Revealed type is "Union[__main__.Foo, None]" + reveal_type(baz) # N: Revealed type is "Union[__main__.Foo, None]" + +[case testInferWalrusAssignmentIndexInCondition] +def check_and(maybe: bool) -> None: + foo = None + bar = None + if maybe and (foo := [1])[(bar := 0)]: + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(bar) # N: Revealed type is "builtins.int" + else: + reveal_type(foo) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + reveal_type(bar) # N: Revealed type is "Union[builtins.int, None]" + +def check_and_nested(maybe: bool) -> None: + foo = None + bar = None + baz = None + if maybe and (foo := (bar := (baz := [1])))[0]: + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(bar) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(baz) # N: Revealed type is "builtins.list[builtins.int]" + else: + reveal_type(foo) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + reveal_type(bar) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + reveal_type(baz) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + +def check_or(maybe: bool) -> None: + foo = None + bar = None + if maybe or (foo := [1])[(bar := 0)]: + reveal_type(foo) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + reveal_type(bar) # N: Revealed type is "Union[builtins.int, None]" + else: + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(bar) # N: Revealed type is "builtins.int" + +def check_or_nested(maybe: bool) -> None: + foo = None + bar = None + baz = None + if maybe or (foo := (bar := (baz := [1])))[0]: + reveal_type(foo) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + reveal_type(bar) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + reveal_type(baz) # N: Revealed type is "Union[builtins.list[builtins.int], None]" + else: + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(bar) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(baz) # N: Revealed type is "builtins.list[builtins.int]" + +[case testInferOptionalAgainstAny] +from typing import Any, Optional, TypeVar + +a: Any +oa: Optional[Any] +T = TypeVar("T") +def f(x: Optional[T]) -> T: ... +reveal_type(f(a)) # N: Revealed type is "Any" +reveal_type(f(oa)) # N: Revealed type is "Any" + +[case testNoCrashOnPartialTypeAsContext] +from typing import overload, TypeVar, Optional, Protocol + +T = TypeVar("T") +class DbManager(Protocol): + @overload + def get(self, key: str) -> Optional[T]: + pass + + @overload + def get(self, key: str, default: T) -> T: + pass + +class Foo: + def __init__(self, db: DbManager, bar: bool) -> None: + if bar: + self.qux = db.get("qux") + else: + self.qux = {} # E: Need type annotation for "qux" (hint: "qux: dict[, ] = ...") +[builtins fixtures/dict.pyi] + +[case testConstraintSolvingFailureShowsCorrectArgument] +from typing import Callable, TypeVar + +T1 = TypeVar('T1') +T2 = TypeVar('T2') +def foo( + a: T1, + b: T2, + c: Callable[[T2], T2], +) -> tuple[T1, T2]: ... + +def bar(y: float) -> float: ... + +foo(1, None, bar) # E: Cannot infer value of type parameter "T2" of "foo" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index bedba811d95b9..37d59a84c873d 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -61,7 +61,7 @@ import a [file a.py] # mypy: allow-any-generics, disallow-untyped-globals -x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") +x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") from typing import List def foo() -> List: @@ -211,6 +211,99 @@ enable_error_code = ignore-without-code, truthy-bool \[mypy-tests.*] disable_error_code = ignore-without-code +[case testInlineErrorCodesOverrideConfigSmall] +# flags: --config-file tmp/mypy.ini +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + +[case testInlineErrorCodesOverrideConfigSmall2] +# flags: --config-file tmp/mypy.ini +import tests.bar +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore +[file tests/bar.py] +# mypy: enable-error-code="ignore-without-code" + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + + +[case testInlineErrorCodesOverrideConfigSmallBackward] +# flags: --config-file tmp/mypy.ini +import tests.bar +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) +[file tests/bar.py] +# mypy: disable-error-code="ignore-without-code" +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +enable_error_code = ignore-without-code + +[case testInlineOverrideConfig] +# flags: --config-file tmp/mypy.ini +import foo +import tests.bar +import tests.baz +[file foo.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 # type: ignore # E: Unused "type: ignore" comment + +[file tests/__init__.py] +[file tests/bar.py] +# mypy: warn_unused_ignores + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 # type: ignore # E: Unused "type: ignore" comment + +[file tests/baz.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 # type: ignore + +[file mypy.ini] +\[mypy] +warn_unused_ignores = True + +\[mypy-tests.*] +warn_unused_ignores = False + + [case testIgnoreErrorsSimple] # mypy: ignore-errors=True @@ -323,3 +416,62 @@ class Foo: foo = Foo() if foo: ... 42 + "no" # type: ignore + +[case testInlinePythonVersion] +# mypy: python-version=3.10 # E: python_version not supported in inline configuration + +[case testInlineErrorCodesArentRuinedByOthersBaseCase] +# mypy: disable-error-code=name-defined +a + +[case testInlineErrorCodesArentRuinedByOthersInvalid] +# mypy: disable-error-code=name-defined +# mypy: AMONGUS +a +[out] +main:2: error: Unrecognized option: amongus = True + +[case testInlineErrorCodesArentRuinedByOthersInvalidBefore] +# mypy: AMONGUS +# mypy: disable-error-code=name-defined +a +[out] +main:1: error: Unrecognized option: amongus = True + +[case testInlineErrorCodesArentRuinedByOthersSe] +# mypy: disable-error-code=name-defined +# mypy: strict-equality +def is_magic(x: bytes) -> bool: + y + return x == 'magic' # E: Unsupported left operand type for == ("bytes") + +[case testInlineConfigErrorCodesOffAndOn] +# mypy: disable-error-code=name-defined +# mypy: enable-error-code=name-defined +a # E: Name "a" is not defined + +[case testInlineConfigErrorCodesOnAndOff] +# mypy: enable-error-code=name-defined +# mypy: disable-error-code=name-defined +a # E: Name "a" is not defined + +[case testConfigFileErrorCodesOnAndOff] +# flags: --config-file tmp/mypy.ini +import foo +[file foo.py] +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code +disable_error_code = ignore-without-code + +[case testInlineConfigBaseCaseWui] +# mypy: warn_unused_ignores +x = 1 # type: ignore # E: Unused "type: ignore" comment + +[case testInlineConfigIsntRuinedByOthersInvalidWui] +# mypy: warn_unused_ignores +# mypy: AMONGUS +x = 1 # type: ignore # E: Unused "type: ignore" comment +[out] +main:2: error: Unrecognized option: amongus = True diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 8fa1bc1ca1ac3..acd4b588f98ca 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -115,8 +115,8 @@ if int(): x = B() x.z x = foo() - x.z # E: "A" has no attribute "z" - x.y + reveal_type(x) # N: Revealed type is "Any" +reveal_type(x) # N: Revealed type is "__main__.A" [case testSingleMultiAssignment] x = 'a' @@ -423,16 +423,16 @@ def f(x: Union[List[int], List[str], int]) -> None: # type of a? reveal_type(x) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]" - x + 1 # E: Unsupported operand types for + ("List[int]" and "int") \ - # E: Unsupported operand types for + ("List[str]" and "int") \ - # N: Left operand is of type "Union[List[int], List[str]]" + x + 1 # E: Unsupported operand types for + ("list[int]" and "int") \ + # E: Unsupported operand types for + ("list[str]" and "int") \ + # N: Left operand is of type "Union[list[int], list[str]]" else: x[0] # E: Value of type "int" is not indexable x + 1 - x[0] # E: Value of type "Union[List[int], List[str], int]" is not indexable - x + 1 # E: Unsupported operand types for + ("List[int]" and "int") \ - # E: Unsupported operand types for + ("List[str]" and "int") \ - # N: Left operand is of type "Union[List[int], List[str], int]" + x[0] # E: Value of type "Union[list[int], list[str], int]" is not indexable + x + 1 # E: Unsupported operand types for + ("list[int]" and "int") \ + # E: Unsupported operand types for + ("list[str]" and "int") \ + # N: Left operand is of type "Union[list[int], list[str], int]" [builtins fixtures/isinstancelist.pyi] [case testUnionListIsinstance2] @@ -696,12 +696,12 @@ while bool(): else: x + [1] x + 'a' # E: Unsupported operand types for + ("int" and "str") \ - # E: Unsupported operand types for + ("List[int]" and "str") \ - # N: Left operand is of type "Union[int, str, List[int]]" + # E: Unsupported operand types for + ("list[int]" and "str") \ + # N: Left operand is of type "Union[int, str, list[int]]" -x + [1] # E: Unsupported operand types for + ("int" and "List[int]") \ - # E: Unsupported operand types for + ("str" and "List[int]") \ - # N: Left operand is of type "Union[int, str, List[int]]" +x + [1] # E: Unsupported operand types for + ("int" and "list[int]") \ + # E: Unsupported operand types for + ("str" and "list[int]") \ + # N: Left operand is of type "Union[int, str, list[int]]" [builtins fixtures/isinstancelist.pyi] [case testIsInstanceThreeUnion2] @@ -715,10 +715,10 @@ while bool(): x + 'a' break x + [1] - x + 'a' # E: Unsupported operand types for + ("List[int]" and "str") -x + [1] # E: Unsupported operand types for + ("int" and "List[int]") \ - # E: Unsupported operand types for + ("str" and "List[int]") \ - # N: Left operand is of type "Union[int, str, List[int]]" + x + 'a' # E: Unsupported operand types for + ("list[int]" and "str") +x + [1] # E: Unsupported operand types for + ("int" and "list[int]") \ + # E: Unsupported operand types for + ("str" and "list[int]") \ + # N: Left operand is of type "Union[int, str, list[int]]" [builtins fixtures/isinstancelist.pyi] [case testIsInstanceThreeUnion3] @@ -736,9 +736,9 @@ while bool(): break x + [1] # These lines aren't reached because x was an int x + 'a' -x + [1] # E: Unsupported operand types for + ("int" and "List[int]") \ - # E: Unsupported operand types for + ("str" and "List[int]") \ - # N: Left operand is of type "Union[int, str, List[int]]" +x + [1] # E: Unsupported operand types for + ("int" and "list[int]") \ + # E: Unsupported operand types for + ("str" and "list[int]") \ + # N: Left operand is of type "Union[int, str, list[int]]" [builtins fixtures/isinstancelist.pyi] [case testRemovingTypeRepeatedly] @@ -1359,7 +1359,7 @@ class B: pass x = B() if isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "__main__.B" reveal_type(x) # N: Revealed type is "__main__.B" @@ -1483,11 +1483,12 @@ def f(x: Union[int, A], a: Type[A]) -> None: [builtins fixtures/isinstancelist.pyi] [case testIsInstanceWithEmtpy2ndArg] +# flags: --warn-unreachable from typing import Union def f(x: Union[int, str]) -> None: if isinstance(x, ()): - reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(x) # E: Statement is unreachable else: reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/isinstancelist.pyi] @@ -1520,7 +1521,7 @@ class Z(X): pass a: Union[Type[Y], Type[Z]] if issubclass(a, X): - reveal_type(a) # N: Revealed type is "Union[Type[__main__.Y], Type[__main__.Z]]" + reveal_type(a) # N: Revealed type is "Union[type[__main__.Y], type[__main__.Z]]" else: reveal_type(a) # unreachable block [builtins fixtures/isinstancelist.pyi] @@ -1529,20 +1530,20 @@ else: from typing import Union, List, Tuple, Dict, Type def f(x: Union[Type[int], Type[str], Type[List]]) -> None: if issubclass(x, (str, (int,))): - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str]" x()[1] # E: Value of type "Union[int, str]" is not indexable else: - reveal_type(x) # N: Revealed type is "Type[builtins.list[Any]]" + reveal_type(x) # N: Revealed type is "type[builtins.list[Any]]" reveal_type(x()) # N: Revealed type is "builtins.list[Any]" x()[1] - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" if issubclass(x, (str, (list,))): - reveal_type(x) # N: Revealed type is "Union[Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.str, builtins.list[Any]]" x()[1] - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" [builtins fixtures/isinstancelist.pyi] @@ -1551,20 +1552,20 @@ from typing import Union, List, Tuple, Dict, Type def f(x: Type[Union[int, str, List]]) -> None: if issubclass(x, (str, (int,))): - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str]" x()[1] # E: Value of type "Union[int, str]" is not indexable else: - reveal_type(x) # N: Revealed type is "Type[builtins.list[Any]]" + reveal_type(x) # N: Revealed type is "type[builtins.list[Any]]" reveal_type(x()) # N: Revealed type is "builtins.list[Any]" x()[1] - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" if issubclass(x, (str, (list,))): - reveal_type(x) # N: Revealed type is "Union[Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.str, builtins.list[Any]]" x()[1] - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" [builtins fixtures/isinstancelist.pyi] @@ -1572,23 +1573,23 @@ def f(x: Type[Union[int, str, List]]) -> None: from typing import Union, List, Tuple, Dict, Type def f(x: Type[Union[int, str, List]]) -> None: - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" if issubclass(x, (str, (int,))): - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str]" x()[1] # E: Value of type "Union[int, str]" is not indexable else: - reveal_type(x) # N: Revealed type is "Type[builtins.list[Any]]" + reveal_type(x) # N: Revealed type is "type[builtins.list[Any]]" reveal_type(x()) # N: Revealed type is "builtins.list[Any]" x()[1] - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" if issubclass(x, (str, (list,))): - reveal_type(x) # N: Revealed type is "Union[Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.str, builtins.list[Any]]" x()[1] - reveal_type(x) # N: Revealed type is "Union[Type[builtins.int], Type[builtins.str], Type[builtins.list[Any]]]" + reveal_type(x) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], type[builtins.list[Any]]]" reveal_type(x()) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]" [builtins fixtures/isinstancelist.pyi] @@ -1603,7 +1604,7 @@ class GoblinAmbusher(Goblin): def test_issubclass(cls: Type[Goblin]) -> None: if issubclass(cls, GoblinAmbusher): - reveal_type(cls) # N: Revealed type is "Type[__main__.GoblinAmbusher]" + reveal_type(cls) # N: Revealed type is "type[__main__.GoblinAmbusher]" cls.level cls.job ga = cls() @@ -1611,9 +1612,9 @@ def test_issubclass(cls: Type[Goblin]) -> None: ga.job ga.job = "Warrior" # E: Cannot assign to class variable "job" via instance else: - reveal_type(cls) # N: Revealed type is "Type[__main__.Goblin]" + reveal_type(cls) # N: Revealed type is "type[__main__.Goblin]" cls.level - cls.job # E: "Type[Goblin]" has no attribute "job" + cls.job # E: "type[Goblin]" has no attribute "job" g = cls() g.level = 15 g.job # E: "Goblin" has no attribute "job" @@ -1632,14 +1633,14 @@ class GoblinAmbusher(Goblin): def test_issubclass(cls: Type[Mob]) -> None: if issubclass(cls, Goblin): - reveal_type(cls) # N: Revealed type is "Type[__main__.Goblin]" + reveal_type(cls) # N: Revealed type is "type[__main__.Goblin]" cls.level - cls.job # E: "Type[Goblin]" has no attribute "job" + cls.job # E: "type[Goblin]" has no attribute "job" g = cls() g.level = 15 g.job # E: "Goblin" has no attribute "job" if issubclass(cls, GoblinAmbusher): - reveal_type(cls) # N: Revealed type is "Type[__main__.GoblinAmbusher]" + reveal_type(cls) # N: Revealed type is "type[__main__.GoblinAmbusher]" cls.level cls.job g = cls() @@ -1647,14 +1648,14 @@ def test_issubclass(cls: Type[Mob]) -> None: g.job g.job = 'Warrior' # E: Cannot assign to class variable "job" via instance else: - reveal_type(cls) # N: Revealed type is "Type[__main__.Mob]" - cls.job # E: "Type[Mob]" has no attribute "job" - cls.level # E: "Type[Mob]" has no attribute "level" + reveal_type(cls) # N: Revealed type is "type[__main__.Mob]" + cls.job # E: "type[Mob]" has no attribute "job" + cls.level # E: "type[Mob]" has no attribute "level" m = cls() m.level = 15 # E: "Mob" has no attribute "level" m.job # E: "Mob" has no attribute "job" if issubclass(cls, GoblinAmbusher): - reveal_type(cls) # N: Revealed type is "Type[__main__.GoblinAmbusher]" + reveal_type(cls) # N: Revealed type is "type[__main__.GoblinAmbusher]" cls.job cls.level ga = cls() @@ -1663,7 +1664,7 @@ def test_issubclass(cls: Type[Mob]) -> None: ga.job = 'Warrior' # E: Cannot assign to class variable "job" via instance if issubclass(cls, GoblinAmbusher): - reveal_type(cls) # N: Revealed type is "Type[__main__.GoblinAmbusher]" + reveal_type(cls) # N: Revealed type is "type[__main__.GoblinAmbusher]" cls.level cls.job ga = cls() @@ -1688,29 +1689,29 @@ class GoblinDigger(Goblin): def test_issubclass(cls: Type[Mob]) -> None: if issubclass(cls, (Goblin, GoblinAmbusher)): - reveal_type(cls) # N: Revealed type is "Type[__main__.Goblin]" + reveal_type(cls) # N: Revealed type is "type[__main__.Goblin]" cls.level - cls.job # E: "Type[Goblin]" has no attribute "job" + cls.job # E: "type[Goblin]" has no attribute "job" g = cls() g.level = 15 g.job # E: "Goblin" has no attribute "job" if issubclass(cls, GoblinAmbusher): cls.level - reveal_type(cls) # N: Revealed type is "Type[__main__.GoblinAmbusher]" + reveal_type(cls) # N: Revealed type is "type[__main__.GoblinAmbusher]" cls.job ga = cls() ga.level = 15 ga.job ga.job = "Warrior" # E: Cannot assign to class variable "job" via instance else: - reveal_type(cls) # N: Revealed type is "Type[__main__.Mob]" - cls.job # E: "Type[Mob]" has no attribute "job" - cls.level # E: "Type[Mob]" has no attribute "level" + reveal_type(cls) # N: Revealed type is "type[__main__.Mob]" + cls.job # E: "type[Mob]" has no attribute "job" + cls.level # E: "type[Mob]" has no attribute "level" m = cls() m.level = 15 # E: "Mob" has no attribute "level" m.job # E: "Mob" has no attribute "job" if issubclass(cls, GoblinAmbusher): - reveal_type(cls) # N: Revealed type is "Type[__main__.GoblinAmbusher]" + reveal_type(cls) # N: Revealed type is "type[__main__.GoblinAmbusher]" cls.job cls.level ga = cls() @@ -1719,7 +1720,7 @@ def test_issubclass(cls: Type[Mob]) -> None: ga.job = "Warrior" # E: Cannot assign to class variable "job" via instance if issubclass(cls, (GoblinDigger, GoblinAmbusher)): - reveal_type(cls) # N: Revealed type is "Union[Type[__main__.GoblinDigger], Type[__main__.GoblinAmbusher]]" + reveal_type(cls) # N: Revealed type is "Union[type[__main__.GoblinDigger], type[__main__.GoblinAmbusher]]" cls.level cls.job g = cls() @@ -1736,14 +1737,14 @@ class MyIntList(List[int]): pass def f(cls: Type[object]) -> None: if issubclass(cls, MyList): - reveal_type(cls) # N: Revealed type is "Type[__main__.MyList]" + reveal_type(cls) # N: Revealed type is "type[__main__.MyList]" cls()[0] else: - reveal_type(cls) # N: Revealed type is "Type[builtins.object]" + reveal_type(cls) # N: Revealed type is "type[builtins.object]" cls()[0] # E: Value of type "object" is not indexable if issubclass(cls, MyIntList): - reveal_type(cls) # N: Revealed type is "Type[__main__.MyIntList]" + reveal_type(cls) # N: Revealed type is "type[__main__.MyIntList]" cls()[0] + 1 [builtins fixtures/isinstancelist.pyi] @@ -1795,7 +1796,7 @@ class Bar: ... fm: FooMetaclass reveal_type(fm) # N: Revealed type is "__main__.FooMetaclass" if issubclass(fm, Foo): - reveal_type(fm) # N: Revealed type is "Type[__main__.Foo]" + reveal_type(fm) # N: Revealed type is "type[__main__.Foo]" if issubclass(fm, Bar): reveal_type(fm) # N: Revealed type is "None" [builtins fixtures/isinstance.pyi] @@ -1810,30 +1811,34 @@ class Baz: ... fm: FooMetaclass reveal_type(fm) # N: Revealed type is "__main__.FooMetaclass" if issubclass(fm, Foo): - reveal_type(fm) # N: Revealed type is "Type[__main__.Foo]" + reveal_type(fm) # N: Revealed type is "type[__main__.Foo]" if issubclass(fm, Bar): - reveal_type(fm) # N: Revealed type is "Never" + reveal_type(fm) # N: Revealed type is "type[__main__.Bar]" if issubclass(fm, Baz): - reveal_type(fm) # N: Revealed type is "Never" + reveal_type(fm) # N: Revealed type is "type[__main__.Baz]" [builtins fixtures/isinstance.pyi] [case testIsinstanceAndNarrowTypeVariable] from typing import TypeVar class A: pass -class B(A): pass +class B(A): + attr: int T = TypeVar('T', bound=A) def f(x: T) -> None: if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__.B" + reveal_type(x) # N: Revealed type is "T`-1" + reveal_type(x.attr) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "T`-1" + x.attr # E: "T" has no attribute "attr" reveal_type(x) # N: Revealed type is "T`-1" + x.attr # E: "T" has no attribute "attr" [builtins fixtures/isinstance.pyi] -[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound] +[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound1] from typing import Union, TypeVar class A: @@ -1845,9 +1850,11 @@ 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" + reveal_type(x) # N: Revealed type is "T`-1" x.a - x.b # E: "A" has no attribute "b" + x.b # E: "T" has no attribute "b" + if bool(): + return x else: reveal_type(x) # N: Revealed type is "T`-1" x.a # E: "T" has no attribute "a" @@ -1857,14 +1864,32 @@ def f(x: T) -> T: return x [builtins fixtures/isinstance.pyi] +[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound2] +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): + return x + x.a # E: "T" has no attribute "a" + x.b # OK + return x +[builtins fixtures/isinstance.pyi] + [case testIsinstanceAndTypeType] from typing import Type def f(x: Type[int]) -> None: if isinstance(x, type): - reveal_type(x) # N: Revealed type is "Type[builtins.int]" + reveal_type(x) # N: Revealed type is "type[builtins.int]" else: reveal_type(x) # Unreachable - reveal_type(x) # N: Revealed type is "Type[builtins.int]" + reveal_type(x) # N: Revealed type is "type[builtins.int]" [builtins fixtures/isinstance.pyi] [case testIsinstanceVariableSubstitution] @@ -1899,15 +1924,15 @@ from typing import Type issubclass() # E: Missing positional arguments "x", "t" in call to "issubclass" y: Type[object] if issubclass(): # E: Missing positional arguments "x", "t" in call to "issubclass" - reveal_type(y) # N: Revealed type is "Type[builtins.object]" + reveal_type(y) # N: Revealed type is "type[builtins.object]" if issubclass(y): # E: Missing positional argument "t" in call to "issubclass" - reveal_type(y) # N: Revealed type is "Type[builtins.object]" + reveal_type(y) # N: Revealed type is "type[builtins.object]" [builtins fixtures/isinstancelist.pyi] [case testIsInstanceTooManyArgs] isinstance(1, 1, 1) # E: Too many arguments for "isinstance" \ - # E: Argument 2 to "isinstance" has incompatible type "int"; expected "Union[type, Tuple[Any, ...]]" + # E: Argument 2 to "isinstance" has incompatible type "int"; expected "Union[type, tuple[Any, ...]]" x: object if isinstance(x, str, 1): # E: Too many arguments for "isinstance" reveal_type(x) # N: Revealed type is "builtins.object" @@ -1915,17 +1940,28 @@ if isinstance(x, str, 1): # E: Too many arguments for "isinstance" reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/isinstancelist.pyi] -[case testIsinstanceNarrowAny] +[case testIsinstanceNarrowAnyExplicit] from typing import Any def narrow_any_to_str_then_reassign_to_int() -> None: - v = 1 # type: Any + v: Any = 1 if isinstance(v, str): reveal_type(v) # N: Revealed type is "builtins.str" v = 2 reveal_type(v) # N: Revealed type is "Any" +[builtins fixtures/isinstance.pyi] + +[case testIsinstanceNarrowAnyImplicit] +def foo(): ... +def narrow_any_to_str_then_reassign_to_int() -> None: + v = foo() + + if isinstance(v, str): + reveal_type(v) # N: Revealed type is "builtins.str" + v = 2 + reveal_type(v) # N: Revealed type is "builtins.int" [builtins fixtures/isinstance.pyi] [case testNarrowTypeAfterInList] @@ -2087,8 +2123,7 @@ else: [out] [case testNarrowTypeAfterInTypedDict] -from typing import Optional -from mypy_extensions import TypedDict +from typing import Optional, TypedDict class TD(TypedDict): a: int b: str @@ -2099,8 +2134,8 @@ def f() -> None: if x not in td: return reveal_type(x) # N: Revealed type is "builtins.str" -[typing fixtures/typing-typeddict.pyi] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testIsinstanceWidensWithAnyArg] @@ -2178,7 +2213,7 @@ def foo2(x: Optional[str]) -> None: if x is None: reveal_type(x) # N: Revealed type is "None" elif isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/isinstance.pyi] @@ -2202,28 +2237,29 @@ def foo2(x: Optional[str]) -> None: if x is None: reveal_type(x) # N: Revealed type is "None" elif isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: 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] @@ -2280,7 +2316,7 @@ def bar(x: Union[List[str], List[int], None]) -> None: from typing import Union, List, Tuple def f(var: Union[List[str], Tuple[str, str], str]) -> None: - reveal_type(var) # N: Revealed type is "Union[builtins.list[builtins.str], Tuple[builtins.str, builtins.str], builtins.str]" + reveal_type(var) # N: Revealed type is "Union[builtins.list[builtins.str], tuple[builtins.str, builtins.str], builtins.str]" if isinstance(var, (list, *(str, int))): reveal_type(var) # N: Revealed type is "Union[builtins.list[builtins.str], builtins.str]" [builtins fixtures/isinstancelist.pyi] @@ -2312,15 +2348,15 @@ class C: x: A if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, C): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." reveal_type(x.f1()) # N: Revealed type is "builtins.int" reveal_type(x.f2()) # N: Revealed type is "builtins.int" reveal_type(x.f3()) # N: Revealed type is "builtins.int" - x.bad() # E: "" has no attribute "bad" + x.bad() # E: "" has no attribute "bad" else: - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "__main__.A" [builtins fixtures/isinstance.pyi] @@ -2333,11 +2369,11 @@ class B: pass x: A if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, A): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionIncompatibleClasses] @@ -2358,7 +2394,7 @@ else: y: C if isinstance(y, B): - reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "__main__." if isinstance(y, A): # E: Subclass of "C", "B", and "A" cannot exist: would have incompatible method signatures reveal_type(y) # E: Statement is unreachable [builtins fixtures/isinstance.pyi] @@ -2367,7 +2403,7 @@ if isinstance(y, B): # flags: --warn-unreachable from abc import abstractmethod -from typing_extensions import Literal +from typing import Literal class A0: def f(self) -> Literal[0]: @@ -2392,19 +2428,19 @@ class B: def t1(self) -> None: if isinstance(self, A1): - reveal_type(self) # N: Revealed type is "__main__." + reveal_type(self) # N: Revealed type is "__main__." x0: Literal[0] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[0]") x1: Literal[1] = self.f() def t2(self) -> None: if isinstance(self, (A0, A1)): - reveal_type(self) # N: Revealed type is "__main__.1" + reveal_type(self) # N: Revealed type is "__main__." x0: Literal[0] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[0]") x1: Literal[1] = self.f() def t3(self) -> None: if isinstance(self, (A1, A2)): - reveal_type(self) # N: Revealed type is "Union[__main__.2, __main__.]" + reveal_type(self) # N: Revealed type is "Union[__main__., __main__.]" x0: Literal[0] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1, 2]", variable has type "Literal[0]") x1: Literal[1] = self.f() # E: Incompatible types in assignment (expression has type "Literal[1, 2]", variable has type "Literal[1]") @@ -2431,14 +2467,14 @@ else: y: A[Parent] if isinstance(y, B): - reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "__main__." reveal_type(y.f()) # N: Revealed type is "__main__.Parent" else: reveal_type(y) # N: Revealed type is "__main__.A[__main__.Parent]" z: A[Child] if isinstance(z, B): - reveal_type(z) # N: Revealed type is "__main__.1" + reveal_type(z) # N: Revealed type is "__main__." reveal_type(z.f()) # N: Revealed type is "__main__.Child" else: reveal_type(z) # N: Revealed type is "__main__.A[__main__.Child]" @@ -2459,10 +2495,10 @@ T1 = TypeVar('T1', A, B) def f1(x: T1) -> T1: if isinstance(x, A): reveal_type(x) # N: Revealed type is "__main__.A" \ - # N: Revealed type is "__main__." + # N: Revealed type is "__main__." if isinstance(x, B): - reveal_type(x) # N: Revealed type is "__main__." \ - # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." \ + # N: Revealed type is "__main__." else: reveal_type(x) # N: Revealed type is "__main__.A" else: @@ -2501,7 +2537,7 @@ T1 = TypeVar('T1', A, B) def f1(x: T1) -> T1: if isinstance(x, A): # The error message is confusing, but we indeed do run into problems if - # 'x' is a subclass of A and B + # 'x' is a subclass of __main__.A and __main__.B return A() # E: Incompatible return value type (got "A", expected "B") else: return B() @@ -2516,6 +2552,33 @@ def f2(x: T2) -> T2: return C() [builtins fixtures/isinstance.pyi] +[case testIsInstanceDisjointBase] +# flags: --warn-unreachable +from typing_extensions import disjoint_base + +@disjoint_base +class Disjoint1: pass +@disjoint_base +class Disjoint2: pass +@disjoint_base +class Disjoint3(Disjoint1): pass +class Child(Disjoint1): pass +class Unrelated: pass + +def f(d1: Disjoint1, u: Unrelated, c: Child) -> object: + if isinstance(d1, Disjoint2): # E: Subclass of "Disjoint1" and "Disjoint2" cannot exist: have distinct disjoint bases + return u # E: Statement is unreachable + if isinstance(u, Disjoint1): # OK + return d1 + if isinstance(c, Disjoint3): # OK + return c + if isinstance(c, Disjoint2): # E: Subclass of "Child" and "Disjoint2" cannot exist: have distinct disjoint bases + return c # E: Statement is unreachable + return d1 + +[builtins fixtures/isinstance.pyi] + + [case testIsInstanceAdHocIntersectionUsage] # flags: --warn-unreachable class A: pass @@ -2529,10 +2592,10 @@ def accept_concrete(c: Concrete) -> None: pass x: A if isinstance(x, B): var = x - reveal_type(var) # N: Revealed type is "__main__." + reveal_type(var) # N: Revealed type is "__main__." accept_a(var) accept_b(var) - accept_concrete(var) # E: Argument 1 to "accept_concrete" has incompatible type ""; expected "Concrete" + accept_concrete(var) # E: Argument 1 to "accept_concrete" has incompatible type ""; expected "Concrete" [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionReinfer] @@ -2542,14 +2605,14 @@ class B: pass x: A assert isinstance(x, B) -reveal_type(x) # N: Revealed type is "__main__." +reveal_type(x) # N: Revealed type is "__main__." y: A assert isinstance(y, B) -reveal_type(y) # N: Revealed type is "__main__.1" +reveal_type(y) # N: Revealed type is "__main__." x = y -reveal_type(x) # N: Revealed type is "__main__.1" +reveal_type(x) # N: Revealed type is "__main__." [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionWithUnions] @@ -2562,15 +2625,15 @@ class D: pass v1: A if isinstance(v1, (B, C)): - reveal_type(v1) # N: Revealed type is "Union[__main__., __main__.]" + reveal_type(v1) # N: Revealed type is "Union[__main__., __main__.]" v2: Union[A, B] if isinstance(v2, C): - reveal_type(v2) # N: Revealed type is "Union[__main__.1, __main__.]" + reveal_type(v2) # N: Revealed type is "Union[__main__., __main__.]" v3: Union[A, B] if isinstance(v3, (C, D)): - reveal_type(v3) # N: Revealed type is "Union[__main__.2, __main__., __main__.1, __main__.]" + reveal_type(v3) # N: Revealed type is "Union[__main__., __main__., __main__., __main__.]" [builtins fixtures/isinstance.pyi] [case testIsInstanceAdHocIntersectionSameNames] @@ -2580,7 +2643,7 @@ class A: pass x: A if isinstance(x, A2): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." [file foo.py] class A: pass @@ -2610,7 +2673,7 @@ class Ambiguous: # We bias towards assuming these two classes could be overlapping foo: Concrete if isinstance(foo, Ambiguous): - reveal_type(foo) # N: Revealed type is "__main__." + reveal_type(foo) # N: Revealed type is "__main__." reveal_type(foo.x) # N: Revealed type is "builtins.int" [builtins fixtures/isinstance.pyi] @@ -2627,13 +2690,13 @@ class C: x: Type[A] if issubclass(x, B): - reveal_type(x) # N: Revealed type is "Type[__main__.]" + reveal_type(x) # N: Revealed type is "type[__main__.]" if issubclass(x, C): # E: Subclass of "A", "B", and "C" cannot exist: would have incompatible method signatures reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Type[__main__.]" + reveal_type(x) # N: Revealed type is "type[__main__.]" else: - reveal_type(x) # N: Revealed type is "Type[__main__.A]" + reveal_type(x) # N: Revealed type is "type[__main__.A]" [builtins fixtures/isinstance.pyi] [case testTypeEqualsCheck] @@ -2660,6 +2723,33 @@ y: Any if type(y) is int: reveal_type(y) # N: Revealed type is "builtins.int" +[case testTypeEqualsCheckUsingIsNonOverlapping] +# flags: --warn-unreachable +from typing import Union + +y: str +if type(y) is int: # E: Subclass of "str" and "int" cannot exist: would have incompatible method signatures + y # E: Statement is unreachable +else: + reveal_type(y) # N: Revealed type is "builtins.str" +[builtins fixtures/isinstance.pyi] + +[case testTypeEqualsCheckUsingIsNonOverlappingChild-xfail] +# flags: --warn-unreachable +from typing import Union + +class A: ... +class B: ... +class C(A): ... +x: Union[B, C] +# C instance cannot be exactly its parent A, we need reversed subtyping relationship +# here (type(parent) is Child). +if type(x) is A: + reveal_type(x) # E: Statement is unreachable +else: + reveal_type(x) # N: Revealed type is "Union[__main__.B, __main__.C]" +[builtins fixtures/isinstance.pyi] + [case testTypeEqualsNarrowingUnionWithElse] from typing import Union @@ -2916,3 +3006,31 @@ if hasattr(mod, "y"): [file mod.py] def __getattr__(attr: str) -> str: ... [builtins fixtures/module.pyi] + +[case testTypeIsntLostAfterNarrowing] +from typing import Any + +var: Any +reveal_type(var) # N: Revealed type is "Any" +assert isinstance(var, (bool, str)) +reveal_type(var) # N: Revealed type is "Union[builtins.bool, builtins.str]" + +if isinstance(var, bool): + reveal_type(var) # N: Revealed type is "builtins.bool" + +# Type of var shouldn't fall back to Any +reveal_type(var) # N: Revealed type is "Union[builtins.bool, builtins.str]" +[builtins fixtures/isinstance.pyi] + +[case testReuseIntersectionForRepeatedIsinstanceCalls] + +class A: ... +class B: ... + +a: A +if isinstance(a, B): + c = a +if isinstance(a, B): + c = a + +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index 4beac047e278b..689553445e9db 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -263,8 +263,8 @@ f(A(), z=A()) # E: Unexpected keyword argument "z" for "f" from typing import Dict, Any def f( **kwargs: 'A') -> None: d1 = kwargs # type: Dict[str, A] - d2 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "Dict[str, A]", variable has type "Dict[A, Any]") - d3 = kwargs # type: Dict[Any, str] # E: Incompatible types in assignment (expression has type "Dict[str, A]", variable has type "Dict[Any, str]") + d2 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "dict[str, A]", variable has type "dict[A, Any]") + d3 = kwargs # type: Dict[Any, str] # E: Incompatible types in assignment (expression has type "dict[str, A]", variable has type "dict[Any, str]") class A: pass [builtins fixtures/dict.pyi] [out] @@ -274,7 +274,7 @@ from typing import Dict, Any def f(**kwargs) -> None: d1 = kwargs # type: Dict[str, A] d2 = kwargs # type: Dict[str, str] - d3 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "Dict[str, Any]", variable has type "Dict[A, Any]") + d3 = kwargs # type: Dict[A, Any] # E: Incompatible types in assignment (expression has type "dict[str, Any]", variable has type "dict[A, Any]") class A: pass [builtins fixtures/dict.pyi] [out] @@ -301,9 +301,9 @@ d: Dict[str, A] f(**d) f(x=A(), **d) d2: Dict[str, B] -f(**d2) # E: Argument 1 to "f" has incompatible type "**Dict[str, B]"; expected "A" -f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type "**Dict[str, B]"; expected "A" -f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**Dict[str, B]"; expected "A" +f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A" +f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type "**dict[str, B]"; expected "A" +f(**{'x': B()}) # E: Argument 1 to "f" has incompatible type "**dict[str, B]"; expected "A" [builtins fixtures/dict.pyi] [case testKwargsAllowedInDunderCall] @@ -345,7 +345,7 @@ from typing import Mapping class MappingSubclass(Mapping[str, str]): pass def f(**kwargs: 'A') -> None: pass d: MappingSubclass -f(**d) +f(**d) # E: Argument 1 to "f" has incompatible type "**MappingSubclass"; expected "A" class A: pass [builtins fixtures/dict.pyi] @@ -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] @@ -366,7 +369,7 @@ def f(a: 'A', b: 'B') -> None: pass d: Dict[str, Any] f(**d) d2: Dict[str, A] -f(**d2) # E: Argument 1 to "f" has incompatible type "**Dict[str, A]"; expected "B" +f(**d2) # E: Argument 1 to "f" has incompatible type "**dict[str, A]"; expected "B" class A: pass class B: pass [builtins fixtures/dict.pyi] @@ -435,15 +438,15 @@ def f(a: int) -> None: pass s = ('',) -f(*s) # E: Argument 1 to "f" has incompatible type "*Tuple[str]"; expected "int" +f(*s) # E: Argument 1 to "f" has incompatible type "*tuple[str]"; expected "int" a = {'': 0} -f(a) # E: Argument 1 to "f" has incompatible type "Dict[str, int]"; expected "int" +f(a) # E: Argument 1 to "f" has incompatible type "dict[str, int]"; expected "int" f(**a) # okay b = {'': ''} -f(b) # E: Argument 1 to "f" has incompatible type "Dict[str, str]"; expected "int" -f(**b) # E: Argument 1 to "f" has incompatible type "**Dict[str, str]"; expected "int" +f(b) # E: Argument 1 to "f" has incompatible type "dict[str, str]"; expected "int" +f(**b) # E: Argument 1 to "f" has incompatible type "**dict[str, str]"; expected "int" c = {0: 0} f(**c) # E: Keywords must be strings @@ -488,7 +491,7 @@ def g(arg: int = 0, **kwargs: object) -> None: d = {} # type: Dict[str, object] f(**d) -g(**d) # E: Argument 1 to "g" has incompatible type "**Dict[str, object]"; expected "int" +g(**d) # E: Argument 1 to "g" has incompatible type "**dict[str, object]"; expected "int" m = {} # type: Mapping[str, object] f(**m) @@ -562,5 +565,5 @@ main:36: error: Argument 1 to "foo" has incompatible type "**A[str, str]"; expec main:37: error: Argument 1 to "foo" has incompatible type "**B[str, str]"; expected "float" main:38: error: Argument after ** must be a mapping, not "C[str, float]" main:39: error: Argument after ** must be a mapping, not "D" -main:41: error: Argument 1 to "foo" has incompatible type "**Dict[str, str]"; expected "float" +main:41: error: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "float" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-lists.test b/test-data/unit/check-lists.test index 77acdafd33199..ee3115421e407 100644 --- a/test-data/unit/check-lists.test +++ b/test-data/unit/check-lists.test @@ -94,3 +94,12 @@ def foo(x: object) -> None: [reveal_type(x) for x in [1, 2, 3]] # N: Revealed type is "builtins.int" [builtins fixtures/isinstancelist.pyi] + +[case testUnpackAssignmentWithStarExpr] +a: A +b: list[B] +if int(): + (a,) = [*b] # E: Incompatible types in assignment (expression has type "B", variable has type "A") + +class A: pass +class B: pass diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 6d76ce176aaf8..ce0ae2844bae1 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -4,7 +4,7 @@ -- [case testLiteralInvalidString] -from typing_extensions import Literal +from typing import Literal def f1(x: 'A[') -> None: pass # E: Invalid type comment or annotation def g1(x: Literal['A[']) -> None: pass reveal_type(f1) # N: Revealed type is "def (x: Any)" @@ -22,13 +22,13 @@ reveal_type(i2) # N: Revealed type is "def (x: Literal['A|B'])" [out] [case testLiteralInvalidTypeComment] -from typing_extensions import Literal +from typing import Literal def f(x): # E: Syntax error in type comment "(A[) -> None" # type: (A[) -> None pass [case testLiteralInvalidTypeComment2] -from typing_extensions import Literal +from typing import Literal def f(x): # E: Invalid type comment or annotation # type: ("A[") -> None pass @@ -52,24 +52,32 @@ y: Literal[43] y = 43 [typing fixtures/typing-medium.pyi] -[case testLiteralInsideOtherTypes] -from typing import Tuple +[case testLiteralFromTypingExtensionsWorks] from typing_extensions import Literal +x: Literal[42] +x = 43 # E: Incompatible types in assignment (expression has type "Literal[43]", variable has type "Literal[42]") + +y: Literal[43] +y = 43 +[builtins fixtures/tuple.pyi] + +[case testLiteralInsideOtherTypes] +from typing import Literal, Tuple + x: Tuple[1] # E: Invalid type: try using Literal[1] instead? def foo(x: Tuple[1]) -> None: ... # E: Invalid type: try using Literal[1] instead? y: Tuple[Literal[2]] def bar(x: Tuple[Literal[2]]) -> None: ... -reveal_type(x) # N: Revealed type is "Tuple[Any]" -reveal_type(y) # N: Revealed type is "Tuple[Literal[2]]" -reveal_type(bar) # N: Revealed type is "def (x: Tuple[Literal[2]])" +reveal_type(x) # N: Revealed type is "tuple[Any]" +reveal_type(y) # N: Revealed type is "tuple[Literal[2]]" +reveal_type(bar) # N: Revealed type is "def (x: tuple[Literal[2]])" [builtins fixtures/tuple.pyi] [out] [case testLiteralInsideOtherTypesTypeCommentsPython3] -from typing import Tuple, Optional -from typing_extensions import Literal +from typing import Literal, Tuple, Optional x = None # type: Optional[Tuple[1]] # E: Invalid type: try using Literal[1] instead? def foo(x): # E: Invalid type: try using Literal[1] instead? @@ -80,9 +88,9 @@ y = None # type: Optional[Tuple[Literal[2]]] def bar(x): # type: (Tuple[Literal[2]]) -> None pass -reveal_type(x) # N: Revealed type is "Union[Tuple[Any], None]" -reveal_type(y) # N: Revealed type is "Union[Tuple[Literal[2]], None]" -reveal_type(bar) # N: Revealed type is "def (x: Tuple[Literal[2]])" +reveal_type(x) # N: Revealed type is "Union[tuple[Any], None]" +reveal_type(y) # N: Revealed type is "Union[tuple[Literal[2]], None]" +reveal_type(bar) # N: Revealed type is "def (x: tuple[Literal[2]])" [builtins fixtures/tuple.pyi] [out] @@ -90,7 +98,7 @@ reveal_type(bar) # N: Revealed type is "def (x: Tuple[Literal from wrapper import * [file wrapper.pyi] -from typing_extensions import Literal +from typing import Literal alias_1 = Literal['a+b'] alias_2 = Literal['1+2'] @@ -153,7 +161,7 @@ reveal_type(expr_com_6) # N: Revealed type is "Literal['"foo"']" [out] [case testLiteralMixingUnicodeAndBytesPython3] -from typing_extensions import Literal +from typing import Literal a_ann: Literal[u"foo"] b_ann: Literal["foo"] @@ -217,8 +225,7 @@ accepts_bytes(c_alias) [out] [case testLiteralMixingUnicodeAndBytesPython3ForwardStrings] -from typing import TypeVar, Generic -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic a_unicode_wrapper: u"Literal[u'foo']" b_unicode_wrapper: u"Literal['foo']" @@ -282,8 +289,7 @@ reveal_type(c_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Liter [out] [case testLiteralUnicodeWeirdCharacters-skip_path_normalization] -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal a1: Literal["\x00\xAC\x62 \u2227 \u03bb(p)"] b1: Literal["\x00¬b ∧ λ(p)"] @@ -340,7 +346,7 @@ a1 = c3 # E: Incompatible types in assignment (expression has type "Literal['¬ [out] [case testLiteralRenamingImportWorks] -from typing_extensions import Literal as Foo +from typing import Literal as Foo x: Foo[3] reveal_type(x) # N: Revealed type is "Literal[3]" @@ -360,13 +366,13 @@ reveal_type(x) # N: Revealed type is "Literal[3]" reveal_type(y) # N: Revealed type is "Literal[4]" [file other_module.py] -from typing_extensions import Literal as Foo +from typing import Literal as Foo Bar = Foo[4] [builtins fixtures/tuple.pyi] [out] [case testLiteralRenamingImportNameConfusion] -from typing_extensions import Literal as Foo +from typing import Literal as Foo x: Foo["Foo"] reveal_type(x) # N: Revealed type is "Literal['Foo']" @@ -396,7 +402,7 @@ indirect = f() -- [case testLiteralBasicIntUsage] -from typing_extensions import Literal +from typing import Literal a1: Literal[4] b1: Literal[0x2a] @@ -435,7 +441,7 @@ reveal_type(f4) # N: Revealed type is "def (x: Literal[8]) -> Literal[8]" [out] [case testLiteralBasicBoolUsage] -from typing_extensions import Literal +from typing import Literal a1: Literal[True] b1: Literal[False] @@ -460,7 +466,7 @@ reveal_type(f2) # N: Revealed type is "def (x: Literal[False]) -> Literal[False [out] [case testLiteralBasicStrUsage] -from typing_extensions import Literal +from typing import Literal a: Literal[""] b: Literal[" foo bar "] @@ -489,7 +495,7 @@ reveal_type(f5) # N: Revealed type is "def (x: Literal['foo']) -> Literal['foo' [out] [case testLiteralBasicStrUsageSlashes-skip_path_normalization] -from typing_extensions import Literal +from typing import Literal a: Literal[r"foo\nbar"] b: Literal["foo\nbar"] @@ -503,7 +509,7 @@ main:7: note: Revealed type is "Literal['foo\nbar']" [case testLiteralBasicNoneUsage] # Note: Literal[None] and None are equivalent -from typing_extensions import Literal +from typing import Literal a: Literal[None] reveal_type(a) # N: Revealed type is "None" @@ -518,7 +524,7 @@ reveal_type(f3) # N: Revealed type is "def (x: None)" [out] [case testLiteralCallingUnionFunction] -from typing_extensions import Literal +from typing import Literal def func(x: Literal['foo', 'bar', ' foo ']) -> None: ... @@ -544,8 +550,7 @@ func(f) # E: Argument 1 to "func" has incompatible type "Literal['foo', 'bar', [out] [case testLiteralDisallowAny] -from typing import Any -from typing_extensions import Literal +from typing import Any, Literal from missing_module import BadAlias # E: Cannot find implementation or library stub for module named "missing_module" \ # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports @@ -558,7 +563,7 @@ reveal_type(b) # N: Revealed type is "Any" [out] [case testLiteralDisallowActualTypes] -from typing_extensions import Literal +from typing import Literal a: Literal[int] # E: Parameter 1 of Literal[...] is invalid b: Literal[float] # E: Parameter 1 of Literal[...] is invalid @@ -574,7 +579,7 @@ reveal_type(d) # N: Revealed type is "Any" [case testLiteralDisallowFloatsAndComplex] -from typing_extensions import Literal +from typing import Literal a1: Literal[3.14] # E: Parameter 1 of Literal[...] cannot be of type "float" b1: 3.14 # E: Invalid type: float literals cannot be used as a type c1: Literal[3j] # E: Parameter 1 of Literal[...] cannot be of type "complex" @@ -597,7 +602,7 @@ d2: d2t # E: Variable "__main__.d2t" is not valid as a type \ [out] [case testLiteralDisallowComplexExpressions] -from typing_extensions import Literal +from typing import Literal def dummy() -> int: return 3 a: Literal[3 + 4] # E: Invalid type: Literal[...] cannot contain arbitrary expressions b: Literal[" foo ".trim()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions @@ -607,7 +612,7 @@ e: Literal[dummy()] # E: Invalid type: Literal[...] cannot contain a [out] [case testLiteralDisallowCollections] -from typing_extensions import Literal +from typing import Literal 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: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict \ @@ -618,7 +623,7 @@ d: {1, 2, 3} # E: Invalid type comment or annotation [typing fixtures/typing-full.pyi] [case testLiteralDisallowCollections2] -from typing_extensions import Literal +from typing 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 @@ -626,7 +631,7 @@ c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid a [builtins fixtures/tuple.pyi] [case testLiteralDisallowCollectionsTypeAlias] -from typing_extensions import Literal +from typing import Literal at = Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid bt = {"a": 1, "b": 2} a: at @@ -637,7 +642,7 @@ b: bt # E: Variable "__main__.bt" is not valid as a ty [typing fixtures/typing-typeddict.pyi] [case testLiteralDisallowCollectionsTypeAlias2] -from typing_extensions import Literal +from typing import Literal at = Literal[{1, 2, 3}] # E: Invalid type alias: expression is not a valid type bt = {1, 2, 3} a: at # E: Variable "__main__.at" is not valid as a type \ @@ -645,11 +650,11 @@ a: at # E: Variable "__main__.at" is not valid as a ty 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/set.pyi] +[typing fixtures/typing-full.pyi] [out] [case testLiteralDisallowTypeVar] -from typing import TypeVar, Tuple -from typing_extensions import Literal +from typing import Literal, TypeVar, Tuple T = TypeVar('T') @@ -666,7 +671,7 @@ def foo(b: Literal[T]) -> Tuple[T]: pass # E: Parameter 1 of Literal[...] is i -- [case testLiteralMultipleValues] -from typing_extensions import Literal +from typing import Literal a: Literal[1, 2, 3] b: Literal["a", "b", "c"] c: Literal[1, "b", True, None] @@ -684,7 +689,7 @@ reveal_type(e) # N: Revealed type is "Union[None, None, None]" [out] [case testLiteralMultipleValuesExplicitTuple] -from typing_extensions import Literal +from typing import Literal # Unfortunately, it seems like typed_ast is unable to distinguish this from # Literal[1, 2, 3]. So we treat the two as being equivalent for now. a: Literal[1, 2, 3] @@ -696,7 +701,7 @@ reveal_type(b) # N: Revealed type is "Union[Literal[1], Literal[2], Literal[3]] [case testLiteralNestedUsage] -from typing_extensions import Literal +from typing import Literal a: Literal[Literal[3], 4, Literal["foo"]] reveal_type(a) # N: Revealed type is "Union[Literal[3], Literal[4], Literal['foo']]" @@ -716,7 +721,7 @@ reveal_type(combined) # N: Revealed type is "Union[Literal['r'], Literal['w'], [out] [case testLiteralBiasTowardsAssumingForwardReference] -from typing_extensions import Literal +from typing import Literal a: "Foo" reveal_type(a) # N: Revealed type is "__main__.Foo" @@ -734,7 +739,7 @@ class Foo: pass [out] [case testLiteralBiasTowardsAssumingForwardReferenceForTypeAliases] -from typing_extensions import Literal +from typing import Literal a: "Foo" reveal_type(a) # N: Revealed type is "Literal[5]" @@ -756,7 +761,7 @@ Foo = Literal[5] [out] [case testLiteralBiasTowardsAssumingForwardReferencesForTypeComments] -from typing_extensions import Literal +from typing import Literal a: Foo reveal_type(a) # N: Revealed type is "__main__.Foo" @@ -779,7 +784,7 @@ class Foo: pass -- [case testLiteralCallingFunction] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[3]) -> None: pass a: Literal[1] @@ -793,7 +798,7 @@ foo(c) # E: Argument 1 to "foo" has incompatible type "int"; expected "Literal[ [out] [case testLiteralCallingFunctionWithUnionLiteral] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[1, 2, 3]) -> None: pass a: Literal[1] @@ -809,7 +814,7 @@ foo(d) # E: Argument 1 to "foo" has incompatible type "int"; expected "Literal[ [out] [case testLiteralCallingFunctionWithStandardBase] -from typing_extensions import Literal +from typing import Literal def foo(x: int) -> None: pass a: Literal[1] @@ -823,8 +828,7 @@ foo(c) # E: Argument 1 to "foo" has incompatible type "Literal[4, 'foo']"; expe [out] [case testLiteralCheckSubtypingStrictOptional] -from typing import Any, NoReturn -from typing_extensions import Literal +from typing import Any, Literal, NoReturn lit: Literal[1] def f_lit(x: Literal[1]) -> None: pass @@ -848,8 +852,7 @@ f_lit(c) # E: Argument 1 to "f_lit" has incompatible type "None"; expected "Lite [case testLiteralCheckSubtypingNoStrictOptional] # flags: --no-strict-optional -from typing import Any, NoReturn -from typing_extensions import Literal +from typing import Any, Literal, NoReturn lit: Literal[1] def f_lit(x: Literal[1]) -> None: pass @@ -872,8 +875,7 @@ f_lit(c) [builtins fixtures/tuple.pyi] [case testLiteralCallingOverloadedFunction] -from typing import overload, Generic, TypeVar, Any -from typing_extensions import Literal +from typing import overload, Generic, Literal, TypeVar, Any T = TypeVar('T') class IOLike(Generic[T]): pass @@ -905,8 +907,7 @@ foo(d) [out] [case testLiteralVariance] -from typing import Generic, TypeVar -from typing_extensions import Literal +from typing import Generic, Literal, TypeVar T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -937,8 +938,7 @@ c2 = c3 [out] [case testLiteralInListAndSequence] -from typing import List, Sequence -from typing_extensions import Literal +from typing import List, Literal, Sequence def foo(x: List[Literal[1, 2]]) -> None: pass def bar(x: Sequence[Literal[1, 2]]) -> None: pass @@ -946,17 +946,17 @@ def bar(x: Sequence[Literal[1, 2]]) -> None: pass a: List[Literal[1]] b: List[Literal[1, 2, 3]] -foo(a) # E: Argument 1 to "foo" has incompatible type "List[Literal[1]]"; expected "List[Literal[1, 2]]" \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +foo(a) # E: Argument 1 to "foo" has incompatible type "list[Literal[1]]"; expected "list[Literal[1, 2]]" \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant -foo(b) # E: Argument 1 to "foo" has incompatible type "List[Literal[1, 2, 3]]"; expected "List[Literal[1, 2]]" +foo(b) # E: Argument 1 to "foo" has incompatible type "list[Literal[1, 2, 3]]"; expected "list[Literal[1, 2]]" bar(a) -bar(b) # E: Argument 1 to "bar" has incompatible type "List[Literal[1, 2, 3]]"; expected "Sequence[Literal[1, 2]]" +bar(b) # E: Argument 1 to "bar" has incompatible type "list[Literal[1, 2, 3]]"; expected "Sequence[Literal[1, 2]]" [builtins fixtures/list.pyi] [out] [case testLiteralRenamingDoesNotChangeTypeChecking] -from typing_extensions import Literal as Foo +from typing import Literal as Foo from other_module import Bar1, Bar2, c def func(x: Foo[15]) -> None: pass @@ -968,7 +968,7 @@ func(b) # E: Argument 1 to "func" has incompatible type "Literal[14]"; expected func(c) [file other_module.py] -from typing_extensions import Literal +from typing import Literal Bar1 = Literal[15] Bar2 = Literal[14] @@ -982,7 +982,7 @@ c: Literal[15] -- [case testLiteralInferredInAssignment] -from typing_extensions import Literal +from typing import Literal int1: Literal[1] = 1 int2 = 1 @@ -1016,7 +1016,7 @@ reveal_type(none3) # N: Revealed type is "None" [out] [case testLiteralInferredOnlyForActualLiterals] -from typing_extensions import Literal +from typing import Literal w: Literal[1] x: Literal["foo"] @@ -1057,7 +1057,7 @@ combined = h [out] [case testLiteralInferredTypeMustMatchExpected] -from typing_extensions import Literal +from typing import Literal a: Literal[1] = 2 # E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]") b: Literal["foo"] = "bar" # E: Incompatible types in assignment (expression has type "Literal['bar']", variable has type "Literal['foo']") @@ -1071,7 +1071,7 @@ f: Literal[True, 4] = False # E: Incompatible types in assignment (expre [out] [case testLiteralInferredInCall] -from typing_extensions import Literal +from typing import Literal def f_int_lit(x: Literal[1]) -> None: pass def f_int(x: int) -> None: pass @@ -1117,7 +1117,7 @@ f_none_lit(n1) [out] [case testLiteralInferredInReturnContext] -from typing_extensions import Literal +from typing import Literal def f1() -> int: return 1 @@ -1138,8 +1138,7 @@ def f5(x: Literal[2]) -> Literal[1]: [out] [case testLiteralInferredInListContext] -from typing import List -from typing_extensions import Literal +from typing import List, Literal a: List[Literal[1]] = [1, 1, 1] b = [1, 1, 1] @@ -1183,22 +1182,20 @@ bad: List[Literal[1, 2]] = [1, 2, 3] # E: List item 2 has incompatible type "Li [case testLiteralInferredInTupleContext] # Note: most of the 'are we handling context correctly' tests should have been # handled up above, so we keep things comparatively simple for tuples and dicts. -from typing import Tuple -from typing_extensions import Literal +from typing import Literal, Tuple a: Tuple[Literal[1], Literal[2]] = (1, 2) b: Tuple[int, Literal[1, 2], Literal[3], Tuple[Literal["foo"]]] = (1, 2, 3, ("foo",)) -c: Tuple[Literal[1], Literal[2]] = (2, 1) # E: Incompatible types in assignment (expression has type "Tuple[Literal[2], Literal[1]]", variable has type "Tuple[Literal[1], Literal[2]]") +c: Tuple[Literal[1], Literal[2]] = (2, 1) # E: Incompatible types in assignment (expression has type "tuple[Literal[2], Literal[1]]", variable has type "tuple[Literal[1], Literal[2]]") d = (1, 2) -reveal_type(d) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(d) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/tuple.pyi] [out] [case testLiteralInferredInDictContext] -from typing import Dict -from typing_extensions import Literal +from typing import Dict, Literal a = {"x": 1, "y": 2} b: Dict[str, Literal[1, 2]] = {"x": 1, "y": 2} @@ -1210,8 +1207,7 @@ reveal_type(a) # N: Revealed type is "builtins.dict[builtins.str, builtins.int] [out] [case testLiteralInferredInOverloadContextBasic] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func(x: Literal[1]) -> str: ... @@ -1239,8 +1235,7 @@ reveal_type(func(c)) # N: Revealed type is "builtins.object" [out] [case testLiteralOverloadProhibitUnsafeOverlaps] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func1(x: Literal[1]) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types @@ -1264,8 +1259,7 @@ def func3(x): pass [out] [case testLiteralInferredInOverloadContextUnionMath] -from typing import overload, Union -from typing_extensions import Literal +from typing import overload, Literal, Union class A: pass class B: pass @@ -1310,8 +1304,7 @@ reveal_type(func(f)) # E: No overload variant of "func" matches argument type " [case testLiteralInferredInOverloadContextUnionMathOverloadingReturnsBestType] # This test is a transliteration of check-overloading::testUnionMathOverloadingReturnsBestType -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def f(x: Literal[1, 2]) -> int: ... @@ -1333,8 +1326,7 @@ reveal_type(f(z)) # N: Revealed type is "builtins.int" \ [out] [case testLiteralInferredInOverloadContextWithTypevars] -from typing import TypeVar, overload, Union -from typing_extensions import Literal +from typing import Literal, TypeVar, overload, Union T = TypeVar('T') @@ -1385,8 +1377,7 @@ reveal_type(f4(b)) # N: Revealed type is "builtins.str" [case testLiteralInferredInOverloadContextUnionMathTrickyOverload] # This test is a transliteration of check-overloading::testUnionMathTrickyOverload1 -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def f(x: Literal['a'], y: Literal['a']) -> int: ... @@ -1408,7 +1399,7 @@ f(x, y) # E: Argument 1 to "f" has incompatible type "Literal['a', 'b']"; expec --- [case testLiteralFallbackOperatorsWorkCorrectly] -from typing_extensions import Literal +from typing import Literal a: Literal[3] b: int @@ -1440,7 +1431,7 @@ reveal_type(b) # N: Revealed type is "builtins.int" [builtins fixtures/primitives.pyi] [case testLiteralFallbackInheritedMethodsWorkCorrectly] -from typing_extensions import Literal +from typing import Literal a: Literal['foo'] b: str @@ -1452,7 +1443,7 @@ reveal_type(a.strip()) # N: Revealed type is "builtins.str" [out] [case testLiteralFallbackMethodsDoNotCoerceToLiteral] -from typing_extensions import Literal +from typing import Literal a: Literal[3] b: int @@ -1486,13 +1477,13 @@ Alias = Literal[3] isinstance(3, Literal[3]) # E: Cannot use isinstance() with Literal type isinstance(3, Alias) # E: Cannot use isinstance() with Literal type \ - # E: Argument 2 to "isinstance" has incompatible type ""; expected "Union[type, Tuple[Any, ...]]" + # E: Argument 2 to "isinstance" has incompatible type ""; expected "Union[type, tuple[Any, ...]]" isinstance(3, Renamed[3]) # E: Cannot use isinstance() with Literal type isinstance(3, indirect.Literal[3]) # E: Cannot use isinstance() with Literal type issubclass(int, Literal[3]) # E: Cannot use issubclass() with Literal type issubclass(int, Alias) # E: Cannot use issubclass() with Literal type \ - # E: Argument 2 to "issubclass" has incompatible type ""; expected "Union[type, Tuple[Any, ...]]" + # E: Argument 2 to "issubclass" has incompatible type ""; expected "Union[type, tuple[Any, ...]]" issubclass(int, Renamed[3]) # E: Cannot use issubclass() with Literal type issubclass(int, indirect.Literal[3]) # E: Cannot use issubclass() with Literal type [builtins fixtures/isinstancelist.pyi] @@ -1500,10 +1491,9 @@ issubclass(int, indirect.Literal[3]) # E: Cannot use issubclass() with Literal [out] [case testLiteralErrorsWhenSubclassed] - -from typing_extensions import Literal -from typing_extensions import Literal as Renamed -import typing_extensions as indirect +from typing import Literal +from typing import Literal as Renamed +import typing as indirect Alias = Literal[3] @@ -1518,9 +1508,9 @@ class Bad4(Alias): pass # E: Invalid base class "Alias" # TODO: We don't seem to correctly handle invoking types like # 'Final' and 'Protocol' as well. When fixing this, also fix # those types? -from typing_extensions import Literal -from typing_extensions import Literal as Renamed -import typing_extensions as indirect +from typing import Literal +from typing import Literal as Renamed +import typing as indirect Alias = Literal[3] @@ -1542,8 +1532,7 @@ indirect.Literal() -- [case testLiteralAndGenericsWithSimpleFunctions] -from typing import TypeVar -from typing_extensions import Literal +from typing import Literal, TypeVar T = TypeVar('T') def foo(x: T) -> T: pass @@ -1573,8 +1562,7 @@ expects_int(foo(foo(a))) [out] [case testLiteralAndGenericWithUnion] -from typing import TypeVar, Union -from typing_extensions import Literal +from typing import Literal, TypeVar, Union T = TypeVar('T') def identity(x: T) -> T: return x @@ -1585,8 +1573,7 @@ b: Union[int, Literal['foo']] = identity('bar') # E: Argument 1 to "identity" h [out] [case testLiteralAndGenericsNoMatch] -from typing import TypeVar, Union, List -from typing_extensions import Literal +from typing import Literal, TypeVar, Union, List def identity(x: T) -> T: return x @@ -1602,8 +1589,7 @@ z: Bad = identity([42]) # E: List item 0 has incompatible type "Literal[42]"; e [out] [case testLiteralAndGenericsWithSimpleClasses] -from typing import TypeVar, Generic -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic T = TypeVar('T') class Wrapper(Generic[T]): @@ -1639,8 +1625,7 @@ expects_literal_wrapper(Wrapper(5)) # E: Argument 1 to "Wrapper" has incompatib [out] [case testLiteralAndGenericsRespectsUpperBound] -from typing import TypeVar -from typing_extensions import Literal +from typing import Literal, TypeVar TLiteral = TypeVar('TLiteral', bound=Literal[3]) TInt = TypeVar('TInt', bound=int) @@ -1679,8 +1664,7 @@ reveal_type(func2(c)) # N: Revealed type is "builtins.int" [out] [case testLiteralAndGenericsRespectsValueRestriction] -from typing import TypeVar -from typing_extensions import Literal +from typing import Literal, TypeVar TLiteral = TypeVar('TLiteral', Literal[3], Literal['foo']) TNormal = TypeVar('TNormal', int, str) @@ -1736,8 +1720,7 @@ reveal_type(func2(s2)) # N: Revealed type is "builtins.str" [out] [case testLiteralAndGenericsWithOverloads] -from typing import TypeVar, overload, Union -from typing_extensions import Literal +from typing import Literal, TypeVar, overload, Union @overload def func1(x: Literal[4]) -> Literal[19]: ... @@ -1762,8 +1745,7 @@ reveal_type(func1(identity(b))) # N: Revealed type is "builtins.int" -- [case testLiteralMeets] -from typing import TypeVar, List, Callable, Union, Optional -from typing_extensions import Literal +from typing import TypeVar, List, Literal, Callable, Union, Optional a: Callable[[Literal[1]], int] b: Callable[[Literal[2]], str] @@ -1809,8 +1791,7 @@ reveal_type(unify(f6)) # N: Revealed type is "None" [out] [case testLiteralMeetsWithStrictOptional] -from typing import TypeVar, Callable, Union -from typing_extensions import Literal +from typing import TypeVar, Callable, Literal, Union a: Callable[[Literal[1]], int] b: Callable[[Literal[2]], str] @@ -1835,8 +1816,7 @@ reveal_type(unify(func)) # N: Revealed type is "Never" -- [case testLiteralIntelligentIndexingTuples] -from typing import Tuple, NamedTuple, Optional, Final -from typing_extensions import Literal +from typing import Literal, Tuple, NamedTuple, Optional, Final class A: pass class B: pass @@ -1862,8 +1842,8 @@ reveal_type(tup1[idx3]) # N: Revealed type is "__main__.D" reveal_type(tup1[idx4]) # N: Revealed type is "__main__.E" reveal_type(tup1[idx_neg1]) # N: Revealed type is "__main__.E" tup1[idx5] # E: Tuple index out of range -reveal_type(tup1[idx2:idx4]) # N: Revealed type is "Tuple[Union[__main__.C, None], __main__.D]" -reveal_type(tup1[::idx2]) # N: Revealed type is "Tuple[__main__.A, Union[__main__.C, None], __main__.E]" +reveal_type(tup1[idx2:idx4]) # N: Revealed type is "tuple[Union[__main__.C, None], __main__.D]" +reveal_type(tup1[::idx2]) # N: Revealed type is "tuple[__main__.A, Union[__main__.C, None], __main__.E]" if tup1[idx2] is not None: reveal_type(tup1[idx2]) # N: Revealed type is "Union[__main__.C, None]" if tup1[idx_final] is not None: @@ -1878,14 +1858,13 @@ reveal_type(tup2[idx3]) # N: Revealed type is "__main__.D" reveal_type(tup2[idx4]) # N: Revealed type is "__main__.E" reveal_type(tup2[idx_neg1]) # N: Revealed type is "__main__.E" tup2[idx5] # E: Tuple index out of range -reveal_type(tup2[idx2:idx4]) # N: Revealed type is "Tuple[__main__.C, __main__.D]" -reveal_type(tup2[::idx2]) # N: Revealed type is "Tuple[__main__.A, __main__.C, __main__.E]" -tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression has type "Tuple[A, B, C, D, E]", variable has type "Tup2Class") +reveal_type(tup2[idx2:idx4]) # N: Revealed type is "tuple[__main__.C, __main__.D]" +reveal_type(tup2[::idx2]) # N: Revealed type is "tuple[__main__.A, __main__.C, __main__.E]" +tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression has type "tuple[A, B, C, D, E]", variable has type "Tup2Class") [builtins fixtures/slice.pyi] [case testLiteralIntelligentIndexingTypedDict] -from typing_extensions import Literal -from mypy_extensions import TypedDict +from typing import Literal, TypedDict class Unrelated: pass u: Unrelated @@ -1905,12 +1884,13 @@ reveal_type(d[a_key]) # N: Revealed type is "builtins.int" reveal_type(d[b_key]) # N: Revealed type is "builtins.str" d[c_key] # E: TypedDict "Outer" has no key "c" -reveal_type(d.get(a_key, u)) # N: Revealed type is "Union[builtins.int, __main__.Unrelated]" +reveal_type(d.get(a_key, u)) # N: Revealed type is "builtins.int" reveal_type(d.get(b_key, u)) # N: Revealed type is "Union[builtins.str, __main__.Unrelated]" reveal_type(d.get(c_key, u)) # N: Revealed type is "builtins.object" -reveal_type(d.pop(a_key)) # E: Key "a" of TypedDict "Outer" cannot be deleted \ - # N: Revealed type is "builtins.int" +reveal_type(d.pop(a_key)) # N: Revealed type is "builtins.int" \ + # E: Key "a" of TypedDict "Outer" cannot be deleted + reveal_type(d.pop(b_key)) # N: Revealed type is "builtins.str" d.pop(c_key) # E: TypedDict "Outer" has no key "c" @@ -1922,9 +1902,7 @@ del d[c_key] # E: TypedDict "Outer" has no key "c" [out] [case testLiteralIntelligentIndexingUsingFinal] -from typing import Tuple, NamedTuple -from typing_extensions import Literal, Final -from mypy_extensions import TypedDict +from typing import Final, Literal, Tuple, NamedTuple, TypedDict int_key_good: Final = 0 int_key_bad: Final = 3 @@ -1950,7 +1928,7 @@ u: Unrelated reveal_type(a[int_key_good]) # N: Revealed type is "builtins.int" reveal_type(b[int_key_good]) # N: Revealed type is "builtins.int" reveal_type(c[str_key_good]) # N: Revealed type is "builtins.int" -reveal_type(c.get(str_key_good, u)) # N: Revealed type is "Union[builtins.int, __main__.Unrelated]" +reveal_type(c.get(str_key_good, u)) # N: Revealed type is "builtins.int" reveal_type(c.get(str_key_bad, u)) # N: Revealed type is "builtins.object" a[int_key_bad] # E: Tuple index out of range @@ -1961,8 +1939,7 @@ c[str_key_bad] # E: TypedDict "MyDict" has no key "missing [out] [case testLiteralIntelligentIndexingTupleUnions] -from typing import Tuple, NamedTuple -from typing_extensions import Literal +from typing import Literal, Tuple, NamedTuple class A: pass class B: pass @@ -1979,20 +1956,19 @@ Tup2Class = NamedTuple('Tup2Class', [('a', A), ('b', B), ('c', C), ('d', D), ('e tup2: Tup2Class reveal_type(tup1[idx1]) # N: Revealed type is "Union[__main__.B, __main__.C]" -reveal_type(tup1[idx1:idx2]) # N: Revealed type is "Union[Tuple[__main__.B, __main__.C], Tuple[__main__.B, __main__.C, __main__.D], Tuple[__main__.C], Tuple[__main__.C, __main__.D]]" -reveal_type(tup1[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], Tuple[__main__.A, __main__.C, __main__.E]]" +reveal_type(tup1[idx1:idx2]) # N: Revealed type is "Union[tuple[__main__.B, __main__.C], tuple[__main__.B, __main__.C, __main__.D], tuple[__main__.C], tuple[__main__.C, __main__.D]]" +reveal_type(tup1[0::idx1]) # N: Revealed type is "Union[tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], tuple[__main__.A, __main__.C, __main__.E]]" tup1[idx_bad] # E: Tuple index out of range reveal_type(tup2[idx1]) # N: Revealed type is "Union[__main__.B, __main__.C]" -reveal_type(tup2[idx1:idx2]) # N: Revealed type is "Union[Tuple[__main__.B, __main__.C], Tuple[__main__.B, __main__.C, __main__.D], Tuple[__main__.C], Tuple[__main__.C, __main__.D]]" -reveal_type(tup2[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], Tuple[__main__.A, __main__.C, __main__.E]]" +reveal_type(tup2[idx1:idx2]) # N: Revealed type is "Union[tuple[__main__.B, __main__.C], tuple[__main__.B, __main__.C, __main__.D], tuple[__main__.C], tuple[__main__.C, __main__.D]]" +reveal_type(tup2[0::idx1]) # N: Revealed type is "Union[tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], tuple[__main__.A, __main__.C, __main__.E]]" tup2[idx_bad] # E: Tuple index out of range [builtins fixtures/slice.pyi] [out] [case testLiteralIntelligentIndexingTypedDictUnions] -from typing_extensions import Literal, Final -from mypy_extensions import TypedDict +from typing import Final, Literal, TypedDict class A: pass class B: pass @@ -2017,8 +1993,8 @@ optional_keys: Literal["d", "e"] bad_keys: Literal["a", "bad"] reveal_type(test[good_keys]) # N: Revealed type is "Union[__main__.A, __main__.B]" -reveal_type(test.get(good_keys)) # N: Revealed type is "Union[__main__.A, __main__.B, None]" -reveal_type(test.get(good_keys, 3)) # N: Revealed type is "Union[__main__.A, Literal[3]?, __main__.B]" +reveal_type(test.get(good_keys)) # N: Revealed type is "Union[__main__.A, __main__.B]" +reveal_type(test.get(good_keys, 3)) # N: Revealed type is "Union[__main__.A, __main__.B]" reveal_type(test.pop(optional_keys)) # N: Revealed type is "Union[__main__.D, __main__.E]" reveal_type(test.pop(optional_keys, 3)) # N: Revealed type is "Union[__main__.D, __main__.E, Literal[3]?]" reveal_type(test.setdefault(good_keys, AAndB())) # N: Revealed type is "Union[__main__.A, __main__.B]" @@ -2043,9 +2019,7 @@ del test[bad_keys] # E: Key "a" of TypedDict "Test" cannot be delet [out] [case testLiteralIntelligentIndexingMultiTypedDict] -from typing import Union -from typing_extensions import Literal -from mypy_extensions import TypedDict +from typing import Literal, TypedDict, Union class A: pass class B: pass @@ -2063,15 +2037,18 @@ class D2(TypedDict): d: D x: Union[D1, D2] -bad_keys: Literal['a', 'b', 'c', 'd'] good_keys: Literal['b', 'c'] +mixed_keys: Literal['a', 'b', 'c', 'd'] +bad_keys: Literal['e', 'f'] -x[bad_keys] # E: TypedDict "D1" has no key "d" \ +x[mixed_keys] # E: TypedDict "D1" has no key "d" \ # E: TypedDict "D2" has no key "a" reveal_type(x[good_keys]) # N: Revealed type is "Union[__main__.B, __main__.C]" -reveal_type(x.get(good_keys)) # N: Revealed type is "Union[__main__.B, __main__.C, None]" -reveal_type(x.get(good_keys, 3)) # N: Revealed type is "Union[__main__.B, Literal[3]?, __main__.C]" +reveal_type(x.get(good_keys)) # N: Revealed type is "Union[__main__.B, __main__.C]" +reveal_type(x.get(good_keys, 3)) # N: Revealed type is "Union[__main__.B, __main__.C]" +reveal_type(x.get(mixed_keys)) # N: Revealed type is "builtins.object" +reveal_type(x.get(mixed_keys, 3)) # N: Revealed type is "builtins.object" reveal_type(x.get(bad_keys)) # N: Revealed type is "builtins.object" reveal_type(x.get(bad_keys, 3)) # N: Revealed type is "builtins.object" @@ -2083,7 +2060,7 @@ reveal_type(x.get(bad_keys, 3)) # N: Revealed type is "builtins.object" -- [case testLiteralFinalInferredAsLiteral] -from typing_extensions import Final, Literal +from typing import Final, Literal var1: Final = 1 var2: Final = "foo" @@ -2137,8 +2114,8 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [builtins fixtures/primitives.pyi] [out] -[case testLiteralFinalDirectInstanceTypesSupercedeInferredLiteral] -from typing_extensions import Final, Literal +[case testLiteralFinalDirectInstanceTypesSupersedeInferredLiteral] +from typing import Final, Literal var1: Final[int] = 1 var2: Final[str] = "foo" @@ -2193,7 +2170,7 @@ force4(f.instancevar4) [out] [case testLiteralFinalDirectLiteralTypesForceLiteral] -from typing_extensions import Final, Literal +from typing import Final, Literal var1: Final[Literal[1]] = 1 var2: Final[Literal["foo"]] = "foo" @@ -2248,17 +2225,17 @@ force4(reveal_type(f.instancevar4)) # N: Revealed type is "None" [out] [case testLiteralFinalErasureInMutableDatastructures1] -from typing_extensions import Final +from typing import Final var1: Final = [0, None] var2: Final = (0, None) reveal_type(var1) # N: Revealed type is "builtins.list[Union[builtins.int, None]]" -reveal_type(var2) # N: Revealed type is "Tuple[Literal[0]?, None]" +reveal_type(var2) # N: Revealed type is "tuple[Literal[0]?, None]" [builtins fixtures/tuple.pyi] [case testLiteralFinalErasureInMutableDatastructures2] -from typing_extensions import Final, Literal +from typing import Final, Literal var1: Final = [] var1.append(0) @@ -2276,7 +2253,7 @@ reveal_type(var3) # N: Revealed type is "builtins.list[Literal[0]]" [builtins fixtures/list.pyi] [case testLiteralFinalMismatchCausesError] -from typing_extensions import Final, Literal +from typing import Final, Literal var1: Final[Literal[4]] = 1 # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[4]") var2: Final[Literal['bad']] = "foo" # E: Incompatible types in assignment (expression has type "Literal['foo']", variable has type "Literal['bad']") @@ -2306,8 +2283,7 @@ Foo().instancevar1 = 10 # E: Cannot assign to final attribute "instancevar1" \ [out] [case testLiteralFinalGoesOnlyOneLevelDown] -from typing import Tuple -from typing_extensions import Final, Literal +from typing import Final, Literal, Tuple a: Final = 1 b: Final = (1, 2) @@ -2316,7 +2292,7 @@ def force1(x: Literal[1]) -> None: pass def force2(x: Tuple[Literal[1], Literal[2]]) -> None: pass reveal_type(a) # N: Revealed type is "Literal[1]?" -reveal_type(b) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?]" +reveal_type(b) # N: Revealed type is "tuple[Literal[1]?, Literal[2]?]" force1(a) # ok force2(b) # ok @@ -2324,8 +2300,7 @@ force2(b) # ok [out] [case testLiteralFinalCollectionPropagation] -from typing import List -from typing_extensions import Final, Literal +from typing import Final, List, Literal a: Final = 1 implicit = [a] @@ -2336,7 +2311,7 @@ def force1(x: List[Literal[1]]) -> None: pass def force2(x: Literal[1]) -> None: pass reveal_type(implicit) # N: Revealed type is "builtins.list[builtins.int]" -force1(reveal_type(implicit)) # E: Argument 1 to "force1" has incompatible type "List[int]"; expected "List[Literal[1]]" \ +force1(reveal_type(implicit)) # E: Argument 1 to "force1" has incompatible type "list[int]"; expected "list[Literal[1]]" \ # N: Revealed type is "builtins.list[builtins.int]" force2(reveal_type(implicit[0])) # E: Argument 1 to "force2" has incompatible type "int"; expected "Literal[1]" \ # N: Revealed type is "builtins.int" @@ -2346,7 +2321,7 @@ force1(reveal_type(explicit)) # N: Revealed type is "builtins.list[Literal[1] force2(reveal_type(explicit[0])) # N: Revealed type is "Literal[1]" reveal_type(direct) # N: Revealed type is "builtins.list[builtins.int]" -force1(reveal_type(direct)) # E: Argument 1 to "force1" has incompatible type "List[int]"; expected "List[Literal[1]]" \ +force1(reveal_type(direct)) # E: Argument 1 to "force1" has incompatible type "list[int]"; expected "list[Literal[1]]" \ # N: Revealed type is "builtins.list[builtins.int]" force2(reveal_type(direct[0])) # E: Argument 1 to "force2" has incompatible type "int"; expected "Literal[1]" \ # N: Revealed type is "builtins.int" @@ -2354,7 +2329,7 @@ force2(reveal_type(direct[0])) # E: Argument 1 to "force2" has incompatible ty [out] [case testLiteralFinalStringTypesPython3] -from typing_extensions import Final, Literal +from typing import Final, Literal a: Final = u"foo" b: Final = "foo" @@ -2377,8 +2352,7 @@ force_bytes(reveal_type(c)) # N: Revealed type is "Literal[b'foo']" [out] [case testLiteralFinalPropagatesThroughGenerics] -from typing import TypeVar, Generic -from typing_extensions import Final, Literal +from typing import TypeVar, Generic, Final, Literal T = TypeVar('T') @@ -2433,8 +2407,7 @@ over_literal(reveal_type(WrapperClass(var3))) # N: Revealed type is "__main__. [out] [case testLiteralFinalUsedInLiteralType] - -from typing_extensions import Literal, Final +from typing import Final, Literal a: Final[int] = 3 b: Final = 3 c: Final[Literal[3]] = 3 @@ -2448,7 +2421,7 @@ d_wrap: Literal[4, d] # E: Parameter 2 of Literal[...] is invalid [out] [case testLiteralWithFinalPropagation] -from typing_extensions import Final, Literal +from typing import Final, Literal a: Final = 3 b: Final = a @@ -2462,7 +2435,7 @@ expect_3(c) # E: Argument 1 to "expect_3" has incompatible type "int"; expected [out] [case testLiteralWithFinalPropagationIsNotLeaking] -from typing_extensions import Final, Literal +from typing import Final, Literal final_tuple_direct: Final = (2, 3) final_tuple_indirect: Final = final_tuple_direct @@ -2492,8 +2465,7 @@ expect_2(final_set_2.pop()) # E: Argument 1 to "expect_2" has incompatible type -- [case testLiteralWithEnumsBasic] - -from typing_extensions import Literal +from typing import Literal from enum import Enum class Color(Enum): @@ -2530,7 +2502,7 @@ reveal_type(r.func()) # N: Revealed type is "builtins.int" [out] [case testLiteralWithEnumsDefinedInClass] -from typing_extensions import Literal +from typing import Literal from enum import Enum class Wrapper: @@ -2553,7 +2525,7 @@ reveal_type(r) # N: Revealed type is "Literal[__main__.Wrapper.Color.RED]" [out] [case testLiteralWithEnumsSimilarDefinitions] -from typing_extensions import Literal +from typing import Literal import mod_a import mod_b @@ -2588,7 +2560,7 @@ class Test(Enum): [out] [case testLiteralWithEnumsDeclaredUsingCallSyntax] -from typing_extensions import Literal +from typing import Literal from enum import Enum A = Enum('A', 'FOO BAR') @@ -2609,7 +2581,7 @@ reveal_type(d) # N: Revealed type is "Literal[__main__.D.FOO]" [out] [case testLiteralWithEnumsDerivedEnums] -from typing_extensions import Literal +from typing import Literal from enum import Enum, IntEnum, IntFlag, Flag def expects_int(x: int) -> None: pass @@ -2639,7 +2611,7 @@ expects_int(d) # E: Argument 1 to "expects_int" has incompatible type "Literal[ [out] [case testLiteralWithEnumsAliases] -from typing_extensions import Literal +from typing import Literal from enum import Enum class Test(Enum): @@ -2654,7 +2626,7 @@ reveal_type(x) # N: Revealed type is "Literal[__main__.Test.FOO]" [out] [case testLiteralUsingEnumAttributesInLiteralContexts] -from typing_extensions import Literal, Final +from typing import Final, Literal from enum import Enum class Test1(Enum): @@ -2688,7 +2660,7 @@ expects_test2_foo(final2) [out] [case testLiteralUsingEnumAttributeNamesInLiteralContexts] -from typing_extensions import Literal, Final +from typing import Final, Literal from enum import Enum class Test1(Enum): @@ -2723,8 +2695,7 @@ reveal_type(Test5.FOO.name) # N: Revealed type is "Literal['FOO']?" [case testLiteralBinderLastValueErased] # mypy: strict-equality - -from typing_extensions import Literal +from typing import Literal def takes_three(x: Literal[3]) -> None: ... x: object @@ -2748,7 +2719,7 @@ def test() -> None: [builtins fixtures/bool.pyi] [case testUnaryOpLiteral] -from typing_extensions import Literal +from typing import Literal a: Literal[-2] = -2 b: Literal[-1] = -1 @@ -2768,7 +2739,7 @@ z: Literal[~0] = 0 # E: Invalid type: Literal[...] cannot contain arbitrary exp [builtins fixtures/ops.pyi] [case testNegativeIntLiteralWithFinal] -from typing_extensions import Literal, Final +from typing import Final, Literal ONE: Final = 1 x: Literal[-1] = -ONE @@ -2783,7 +2754,7 @@ if bool(): [builtins fixtures/ops.pyi] [case testAliasForEnumTypeAsLiteral] -from typing_extensions import Literal +from typing import Literal from enum import Enum class Foo(Enum): @@ -2797,6 +2768,28 @@ reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.A]" reveal_type(y) # N: Revealed type is "Literal[__main__.Foo.A]" [builtins fixtures/tuple.pyi] +[case testLiteralUnionEnumAliasAssignable] +from enum import Enum +from typing import Literal, Union + +class E(Enum): + A = 'a' + B = 'b' + C = 'c' + +A = Literal[E.A] +B = Literal[E.B, E.C] + +def f(x: Union[A, B]) -> None: ... +def f2(x: Union[A, Literal[E.B, E.C]]) -> None: ... +def f3(x: Union[Literal[E.A], B]) -> None: ... + +def main(x: E) -> None: + f(x) + f2(x) + f3(x) +[builtins fixtures/tuple.pyi] + [case testStrictEqualityLiteralTrueVsFalse] # mypy: strict-equality @@ -2814,9 +2807,7 @@ assert c.a is False [case testConditionalBoolLiteralUnionNarrowing] # flags: --warn-unreachable - -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Truth: def __bool__(self) -> Literal[True]: ... @@ -2878,8 +2869,7 @@ else: [case testLiteralAndInstanceSubtyping] # https://github.com/python/mypy/issues/7399 # https://github.com/python/mypy/issues/11232 -from typing import Tuple, Union -from typing_extensions import Literal, Final +from typing import Final, Literal, Tuple, Union x: bool @@ -2889,7 +2879,7 @@ def f() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]: else: return (False, 'oops') -reveal_type(f()) # N: Revealed type is "Union[Tuple[Literal[True], builtins.int], Tuple[Literal[False], builtins.str]]" +reveal_type(f()) # N: Revealed type is "Union[tuple[Literal[True], builtins.int], tuple[Literal[False], builtins.str]]" def does_work() -> Tuple[Literal[1]]: x: Final = (1,) @@ -2901,27 +2891,27 @@ def also_works() -> Tuple[Literal[1]]: def invalid_literal_value() -> Tuple[Literal[1]]: x: Final = (2,) - return x # E: Incompatible return value type (got "Tuple[int]", expected "Tuple[Literal[1]]") + return x # E: Incompatible return value type (got "tuple[int]", expected "tuple[Literal[1]]") def invalid_literal_type() -> Tuple[Literal[1]]: x: Final = (True,) - return x # E: Incompatible return value type (got "Tuple[bool]", expected "Tuple[Literal[1]]") + return x # E: Incompatible return value type (got "tuple[bool]", expected "tuple[Literal[1]]") def incorrect_return1() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]: if x: - return (False, 5) # E: Incompatible return value type (got "Tuple[bool, int]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]") + return (False, 5) # E: Incompatible return value type (got "tuple[bool, int]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]") else: - return (True, 'oops') # E: Incompatible return value type (got "Tuple[bool, str]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]") + return (True, 'oops') # E: Incompatible return value type (got "tuple[bool, str]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]") def incorrect_return2() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]: if x: - return (bool(), 5) # E: Incompatible return value type (got "Tuple[bool, int]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]") + return (bool(), 5) # E: Incompatible return value type (got "tuple[bool, int]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]") else: - return (bool(), 'oops') # E: Incompatible return value type (got "Tuple[bool, str]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]") + return (bool(), 'oops') # E: Incompatible return value type (got "tuple[bool, str]", expected "Union[tuple[Literal[True], int], tuple[Literal[False], str]]") [builtins fixtures/bool.pyi] [case testLiteralSubtypeContext] -from typing_extensions import Literal +from typing import Literal class A: foo: Literal['bar', 'spam'] @@ -2932,8 +2922,7 @@ reveal_type(B().foo) # N: Revealed type is "Literal['spam']" [builtins fixtures/tuple.pyi] [case testLiteralSubtypeContextNested] -from typing import List -from typing_extensions import Literal +from typing import List, Literal class A: foo: List[Literal['bar', 'spam']] @@ -2944,8 +2933,7 @@ reveal_type(B().foo) # N: Revealed type is "builtins.list[Union[Literal['bar'], [builtins fixtures/tuple.pyi] [case testLiteralSubtypeContextGeneric] -from typing_extensions import Literal -from typing import Generic, List, TypeVar +from typing import Generic, List, Literal, TypeVar T = TypeVar("T", bound=str) @@ -2960,3 +2948,53 @@ 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 import Literal, 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/primitives.pyi] + +[case testLiteralInsideAType] +from typing import Literal, Type, Union + +x: Type[Literal[1]] # E: Type[...] can't contain "Literal[...]" +y: Type[Union[Literal[1], Literal[2]]] # E: Type[...] can't contain "Union[Literal[...], Literal[...]]" +z: Type[Literal[1, 2]] # E: Type[...] can't contain "Union[Literal[...], Literal[...]]" +[builtins fixtures/tuple.pyi] + +[case testJoinLiteralAndInstance] +from typing import Generic, TypeVar, Literal + +T = TypeVar("T") + +class A(Generic[T]): ... + +def f(a: A[T], t: T) -> T: ... +def g(a: T, t: A[T]) -> T: ... + +def check(obj: A[Literal[1]]) -> None: + reveal_type(f(obj, 1)) # N: Revealed type is "Literal[1]" + reveal_type(f(obj, '')) # E: Cannot infer value of type parameter "T" of "f" \ + # N: Revealed type is "Any" + reveal_type(g(1, obj)) # N: Revealed type is "Literal[1]" + reveal_type(g('', obj)) # E: Cannot infer value of type parameter "T" of "g" \ + # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-lowercase.test b/test-data/unit/check-lowercase.test index ab6d68929f8e8..d19500327255f 100644 --- a/test-data/unit/check-lowercase.test +++ b/test-data/unit/check-lowercase.test @@ -1,64 +1,34 @@ - -[case testTupleLowercaseSettingOff] -# flags: --python-version 3.9 --force-uppercase-builtins -x = (3,) -x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "Tuple[int]") -[builtins fixtures/tuple.pyi] - -[case testTupleLowercaseSettingOn] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testTupleLowercase] x = (3,) x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "tuple[int]") [builtins fixtures/tuple.pyi] -[case testListLowercaseSettingOff] -# flags: --python-version 3.9 --force-uppercase-builtins -x = [3] -x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "List[int]") - -[case testListLowercaseSettingOn] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testListLowercase] x = [3] x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "list[int]") -[case testDictLowercaseSettingOff] -# flags: --python-version 3.9 --force-uppercase-builtins -x = {"key": "value"} -x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "Dict[str, str]") - -[case testDictLowercaseSettingOn] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testDictLowercase] x = {"key": "value"} x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "dict[str, str]") -[case testSetLowercaseSettingOff] -# flags: --python-version 3.9 --force-uppercase-builtins -x = {3} -x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "Set[int]") -[builtins fixtures/set.pyi] - -[case testSetLowercaseSettingOn] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testSetLowercase] x = {3} x = 3 # E: Incompatible types in assignment (expression has type "int", variable has type "set[int]") [builtins fixtures/set.pyi] -[case testTypeLowercaseSettingOff] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testTypeLowercase] x: type[type] y: int y = x # E: Incompatible types in assignment (expression has type "type[type]", variable has type "int") -[case testLowercaseSettingOnTypeAnnotationHint] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testLowercaseTypeAnnotationHint] x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") y = {} # E: Need type annotation for "y" (hint: "y: dict[, ] = ...") z = set() # E: Need type annotation for "z" (hint: "z: set[] = ...") [builtins fixtures/primitives.pyi] -[case testLowercaseSettingOnRevealTypeType] -# flags: --python-version 3.9 --no-force-uppercase-builtins +[case testLowercaseRevealTypeType] def f(t: type[int]) -> None: reveal_type(t) # N: Revealed type is "type[builtins.int]" reveal_type(f) # N: Revealed type is "def (t: type[builtins.int])" diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 5fd48577e436e..862cd8ea39055 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -423,7 +423,35 @@ import typing __all__ = [1, 2, 3] [builtins fixtures/module_all.pyi] [out] -main:2: error: Type of __all__ must be "Sequence[str]", not "List[int]" +main:2: error: List item 0 has incompatible type "int"; expected "str" +main:2: error: List item 1 has incompatible type "int"; expected "str" +main:2: error: List item 2 has incompatible type "int"; expected "str" + +[case testAllMustBeSequenceStr2] +import typing +__all__ = 1 # E: Type of __all__ must be "Sequence[str]", not "int" +reveal_type(__all__) # N: Revealed type is "builtins.int" +[builtins fixtures/module_all.pyi] + +[case testAllMustBeSequenceStr3] +import typing +__all__ = set() # E: Need type annotation for "__all__" (hint: "__all__: set[] = ...") \ + # E: Type of __all__ must be "Sequence[str]", not "set[Any]" +reveal_type(__all__) # N: Revealed type is "builtins.set[Any]" +[builtins fixtures/set.pyi] + +[case testModuleAllEmptyList] +__all__ = [] +reveal_type(__all__) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/module_all.pyi] + +[case testDunderAllNotGlobal] +class A: + __all__ = 1 + +def foo() -> None: + __all__ = 1 +[builtins fixtures/module_all.pyi] [case testUnderscoreExportedValuesInImportAll] import typing @@ -519,8 +547,7 @@ def bar(x: Both, y: Both = ...) -> Both: [out] [case testEllipsisDefaultArgValueInNonStubsMethods] -from typing import Generic, TypeVar -from typing_extensions import Protocol +from typing import Generic, Protocol, TypeVar from abc import abstractmethod T = TypeVar('T') @@ -667,9 +694,9 @@ import mod X: Type[mod.A] Y: Type[mod.B] from mod import B as X -from mod import A as Y # E: Incompatible import of "Y" (imported name has type "Type[A]", local name has type "Type[B]") +from mod import A as Y # E: Incompatible import of "Y" (imported name has type "type[A]", local name has type "type[B]") -import mod as X # E: Incompatible import of "X" (imported name has type "object", local name has type "Type[A]") +import mod as X # E: Incompatible import of "X" (imported name has type "object", local name has type "type[A]") [file mod.py] class A: ... @@ -1050,7 +1077,7 @@ class z: pass [out] main:2: error: Incompatible import of "x" (imported name has type "str", local name has type "int") main:2: error: Incompatible import of "y" (imported name has type "Callable[[], str]", local name has type "Callable[[], int]") -main:2: error: Incompatible import of "z" (imported name has type "Type[b.z]", local name has type "Type[a.z]") +main:2: error: Incompatible import of "z" (imported name has type "type[b.z]", local name has type "type[a.z]") -- Misc @@ -1389,14 +1416,14 @@ import b import b class C: def f1(self) -> None: - self.x2 + reveal_type(self.x2) def f2(self) -> None: self.x2 = b.b [file b.py] import a b = 1 + int() [out] -tmp/a.py:4: error: Cannot determine type of "x2" +tmp/a.py:4: note: Revealed type is "builtins.int" [case testErrorInPassTwo1] import b @@ -1583,8 +1610,8 @@ def f() -> types.ModuleType: return types reveal_type(f()) # N: Revealed type is "types.ModuleType" reveal_type(types) # N: Revealed type is "types.ModuleType" - [builtins fixtures/module.pyi] +[typing fixtures/typing-full.pyi] [case testClassImportAccessedInMethod] class C: @@ -1998,6 +2025,7 @@ from typing import TypeVar T = TypeVar('T') def whatever(x: T) -> T: pass [builtins fixtures/module.pyi] +[typing fixtures/typing-full.pyi] [case testModuleAliasToQualifiedImport2] import mod @@ -2013,8 +2041,8 @@ from typing import TypeVar T = TypeVar('T') def whatever(x: T) -> T: pass [file othermod.py] - [builtins fixtures/module.pyi] +[typing fixtures/typing-full.pyi] [case testModuleLevelGetattr] import has_getattr @@ -2883,7 +2911,7 @@ CustomDict(foo="abc", bar="def") [file foo/__init__.py] [file foo/bar/__init__.py] [file foo/bar/custom_dict.py] -from typing_extensions import TypedDict +from typing import TypedDict CustomDict = TypedDict( "CustomDict", @@ -2927,7 +2955,7 @@ reveal_type(abc.__name__) # N: Revealed type is "builtins.str" reveal_type(builtins.__name__) # N: Revealed type is "builtins.str" reveal_type(typing.__name__) # N: Revealed type is "builtins.str" -[case testSpecialAttrsAreAvaliableInClasses] +[case testSpecialAttrsAreAvailableInClasses] class Some: name = __name__ reveal_type(Some.name) # N: Revealed type is "builtins.str" @@ -3136,15 +3164,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 @@ -3210,13 +3235,13 @@ class Bar(Foo): def frobnicate(self, *args: int) -> None: pass # type: ignore[override] # I know [builtins fixtures/dict.pyi] [out1] -tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo" +tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "a.Foo" tmp/b.py:3: note: Superclass: tmp/b.py:3: note: def frobnicate(self, x: str, *args: Any, **kwargs: Any) -> Any tmp/b.py:3: note: Subclass: tmp/b.py:3: note: def frobnicate(self) -> None [out2] -tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "Foo" +tmp/b.py:3: error: Signature of "frobnicate" incompatible with supertype "a.Foo" tmp/b.py:3: note: Superclass: tmp/b.py:3: note: def frobnicate(self, x: str, *args: Any, **kwargs: Any) -> Any tmp/b.py:3: note: Subclass: diff --git a/test-data/unit/check-multiple-inheritance.test b/test-data/unit/check-multiple-inheritance.test index d03f2e35e1c46..9cb3bd2e7ca2e 100644 --- a/test-data/unit/check-multiple-inheritance.test +++ b/test-data/unit/check-multiple-inheritance.test @@ -706,3 +706,29 @@ class C34(B3, B4): ... class C41(B4, B1): ... class C42(B4, B2): ... class C43(B4, B3): ... + +[case testMultipleInheritanceExplicitDiamondResolution] +# Adapted from #14279 +class A: + class M: + pass + +class B0(A): + class M(A.M): + pass + +class B1(A): + class M(A.M): + pass + +class C(B0,B1): + class M(B0.M, B1.M): + pass + +class D0(B0): + pass +class D1(B1): + pass + +class D(D0,D1,C): + pass diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index df2c7ffc80672..66eb555421f44 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -69,8 +69,7 @@ a.y = 5 # E: Property "y" defined in "X" is read-only [case testTypingNamedTupleAttributesAreReadOnly] -from typing import NamedTuple -from typing_extensions import Protocol +from typing import NamedTuple, Protocol class HasX(Protocol): x: str @@ -82,8 +81,8 @@ a: HasX = A("foo") a.x = "bar" [builtins fixtures/tuple.pyi] [out] -main:10: error: Incompatible types in assignment (expression has type "A", variable has type "HasX") -main:10: note: Protocol member HasX.x expected settable variable, got read-only attribute +main:9: error: Incompatible types in assignment (expression has type "A", variable has type "HasX") +main:9: note: Protocol member HasX.x expected settable variable, got read-only attribute [case testNamedTupleCreateWithPositionalArguments] @@ -306,9 +305,9 @@ t: Tuple[int, str] if int(): b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") if int(): - a = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A") + a = t # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "A") if int(): - b = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B") + b = t # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "B") if int(): t = a if int(): @@ -333,14 +332,14 @@ if int(): if int(): l = [A(1)] if int(): - a = (1,) # E: Incompatible types in assignment (expression has type "Tuple[int]", \ + a = (1,) # E: Incompatible types in assignment (expression has type "tuple[int]", \ variable has type "A") [builtins fixtures/list.pyi] [case testNamedTupleMissingClassAttribute] import collections MyNamedTuple = collections.namedtuple('MyNamedTuple', ['spam', 'eggs']) -MyNamedTuple.x # E: "Type[MyNamedTuple]" has no attribute "x" +MyNamedTuple.x # E: "type[MyNamedTuple]" has no attribute "x" [builtins fixtures/list.pyi] @@ -377,7 +376,7 @@ from collections import namedtuple X = namedtuple('X', ['x', 'y']) x: X -reveal_type(x._replace()) # N: Revealed type is "Tuple[Any, Any, fallback=__main__.X]" +reveal_type(x._replace()) # N: Revealed type is "tuple[Any, Any, fallback=__main__.X]" x._replace(y=5) x._replace(x=3) x._replace(x=3, y=5) @@ -402,7 +401,7 @@ from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) x: X -reveal_type(x._replace()) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]" +reveal_type(x._replace()) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.X]" x._replace(x=5) x._replace(y=5) # E: Argument "y" to "_replace" of "X" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] @@ -411,7 +410,7 @@ x._replace(y=5) # E: Argument "y" to "_replace" of "X" has incompatible type "i from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) -reveal_type(X._make([5, 'a'])) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.X]" +reveal_type(X._make([5, 'a'])) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.X]" X._make('a b') # E: Argument 1 to "_make" of "X" has incompatible type "str"; expected "Iterable[Any]" -- # FIX: not a proper class method @@ -425,7 +424,7 @@ X._make('a b') # E: Argument 1 to "_make" of "X" has incompatible type "str"; e from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) -reveal_type(X._fields) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(X._fields) # N: Revealed type is "tuple[builtins.str, builtins.str]" [builtins fixtures/tuple.pyi] [case testNamedTupleSource] @@ -451,7 +450,7 @@ from typing import NamedTuple X = NamedTuple('X', [('x', int), ('y', str)]) Y = NamedTuple('Y', [('x', int), ('y', str)]) -reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" +reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" [builtins fixtures/list.pyi] @@ -459,8 +458,8 @@ reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[Tuple[ from typing import NamedTuple, Tuple X = NamedTuple('X', [('x', int), ('y', str)]) -reveal_type([(3, 'b'), X(1, 'a')]) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" -reveal_type([X(1, 'a'), (3, 'b')]) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" +reveal_type([(3, 'b'), X(1, 'a')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" +reveal_type([X(1, 'a'), (3, 'b')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" [builtins fixtures/list.pyi] @@ -520,14 +519,14 @@ a = B('').member() [case testNamedTupleSelfTypeReplace] from typing import NamedTuple, TypeVar A = NamedTuple('A', [('x', str)]) -reveal_type(A('hello')._replace(x='')) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.A]" +reveal_type(A('hello')._replace(x='')) # N: Revealed type is "tuple[builtins.str, fallback=__main__.A]" a: A a = A('hello')._replace(x='') class B(A): pass -reveal_type(B('hello')._replace(x='')) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.B]" +reveal_type(B('hello')._replace(x='')) # N: Revealed type is "tuple[builtins.str, fallback=__main__.B]" b: B b = B('hello')._replace(x='') [builtins fixtures/tuple.pyi] @@ -535,13 +534,13 @@ b = B('hello')._replace(x='') [case testNamedTupleSelfTypeMake] from typing import NamedTuple, TypeVar A = NamedTuple('A', [('x', str)]) -reveal_type(A._make([''])) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.A]" +reveal_type(A._make([''])) # N: Revealed type is "tuple[builtins.str, fallback=__main__.A]" a = A._make(['']) # type: A class B(A): pass -reveal_type(B._make([''])) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.B]" +reveal_type(B._make([''])) # N: Revealed type is "tuple[builtins.str, fallback=__main__.B]" b = B._make(['']) # type: B [builtins fixtures/list.pyi] @@ -549,7 +548,7 @@ b = B._make(['']) # type: B [case testNamedTupleIncompatibleRedefinition] from typing import NamedTuple class Crash(NamedTuple): - count: int # E: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], object], int]") + count: int # E: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[object], int]") [builtins fixtures/tuple.pyi] [case testNamedTupleInClassNamespace] @@ -560,7 +559,7 @@ class C: A = NamedTuple('A', [('x', int)]) def g(self): A = NamedTuple('A', [('y', int)]) -C.A # E: "Type[C]" has no attribute "A" +C.A # E: "type[C]" has no attribute "A" [builtins fixtures/tuple.pyi] [case testNamedTupleInFunction] @@ -604,8 +603,8 @@ def f(x: a.X) -> None: reveal_type(x) [builtins fixtures/tuple.pyi] [out] -tmp/b.py:4: note: Revealed type is "Tuple[Any, fallback=a.X]" -tmp/b.py:6: note: Revealed type is "Tuple[Any, fallback=a.X]" +tmp/b.py:4: note: Revealed type is "tuple[Any, fallback=a.X]" +tmp/b.py:6: note: Revealed type is "tuple[Any, fallback=a.X]" [case testNamedTupleWithImportCycle2] import a @@ -624,8 +623,8 @@ def f(x: a.N) -> None: reveal_type(x) [builtins fixtures/tuple.pyi] [out] -tmp/b.py:4: note: Revealed type is "Tuple[Any, fallback=a.N]" -tmp/b.py:7: note: Revealed type is "Tuple[Any, fallback=a.N]" +tmp/b.py:4: note: Revealed type is "tuple[Any, fallback=a.N]" +tmp/b.py:7: note: Revealed type is "tuple[Any, fallback=a.N]" [case testSimpleSelfReferentialNamedTuple] from typing import NamedTuple @@ -677,7 +676,7 @@ def test() -> None: # N: Recursive types are not allowed at function scope ]) n: Node - reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.Node@4]" + reveal_type(n) # N: Revealed type is "tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.Node@4]" [builtins fixtures/tuple.pyi] [case testSelfRefNT2] @@ -694,7 +693,7 @@ def test() -> None: y: int n: A - reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.A@4]" + reveal_type(n) # N: Revealed type is "tuple[builtins.str, builtins.tuple[Any, ...], fallback=__main__.A@4]" [builtins fixtures/tuple.pyi] [case testSelfRefNT3] @@ -712,10 +711,10 @@ def test() -> None: ]) n: B m: A - reveal_type(n.x) # N: Revealed type is "Tuple[Any, builtins.int]" + reveal_type(n.x) # N: Revealed type is "tuple[Any, builtins.int]" reveal_type(m[0]) # N: Revealed type is "builtins.str" lst = [m, n] - reveal_type(lst[0]) # N: Revealed type is "Tuple[builtins.object, builtins.object]" + reveal_type(lst[0]) # N: Revealed type is "tuple[builtins.object, builtins.object]" [builtins fixtures/tuple.pyi] [case testSelfRefNT4] @@ -740,7 +739,7 @@ from typing import NamedTuple def test() -> None: B = NamedTuple('B', [ - ('x', A), # E: Cannot resolve name "A" (possible cyclic definition) \ + ('x', A), # E: Cannot resolve name "A" (possible cyclic definition) \ # N: Recursive types are not allowed at function scope \ # E: Name "A" is used before definition ('y', int), @@ -751,8 +750,8 @@ def test() -> None: ]) n: A def f(m: B) -> None: pass - reveal_type(n) # N: Revealed type is "Tuple[builtins.str, Tuple[Any, builtins.int, fallback=__main__.B@4], fallback=__main__.A@8]" - reveal_type(f) # N: Revealed type is "def (m: Tuple[Any, builtins.int, fallback=__main__.B@4])" + reveal_type(n) # N: Revealed type is "tuple[builtins.str, tuple[Any, builtins.int, fallback=__main__.B@4], fallback=__main__.A@8]" + reveal_type(f) # N: Revealed type is "def (m: tuple[Any, builtins.int, fallback=__main__.B@4])" [builtins fixtures/tuple.pyi] [case testRecursiveNamedTupleInBases] @@ -766,13 +765,13 @@ def test() -> None: class B(NamedTuple('B', [('val', object)])): pass exp: Exp - reveal_type(exp) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]" + reveal_type(exp) # N: Revealed type is "Union[Any, tuple[builtins.object, fallback=__main__.B@6]]" if isinstance(exp, A): - reveal_type(exp[0][0]) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]" - reveal_type(exp.attr[0]) # N: Revealed type is "Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]" + reveal_type(exp[0][0]) # N: Revealed type is "Union[Any, tuple[builtins.object, fallback=__main__.B@6]]" + reveal_type(exp.attr[0]) # N: Revealed type is "Union[Any, tuple[builtins.object, fallback=__main__.B@6]]" if isinstance(exp, B): reveal_type(exp.val) # N: Revealed type is "builtins.object" - reveal_type(A([B(1), B(2)])) # N: Revealed type is "Tuple[builtins.list[Union[Any, Tuple[builtins.object, fallback=__main__.B@6]]], fallback=__main__.A@5]" + reveal_type(A([B(1), B(2)])) # N: Revealed type is "tuple[builtins.list[Union[Any, tuple[builtins.object, fallback=__main__.B@6]]], fallback=__main__.A@5]" [builtins fixtures/isinstancelist.pyi] [out] @@ -787,7 +786,7 @@ from b import tp x: tp reveal_type(x.x) # N: Revealed type is "builtins.int" -reveal_type(tp) # N: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fallback=b.tp]" +reveal_type(tp) # N: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=b.tp]" tp('x') # E: Argument 1 to "tp" has incompatible type "str"; expected "int" [file b.py] @@ -810,7 +809,7 @@ def test() -> None: pass hc = HelpCommand(subcommands=[]) - reveal_type(hc) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.HelpCommand@7]" + reveal_type(hc) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.HelpCommand@7]" [builtins fixtures/list.pyi] [out] @@ -841,7 +840,7 @@ class D(NamedTuple): def f(cls) -> None: pass d: Type[D] -d.g() # E: "Type[D]" has no attribute "g" +d.g() # E: "type[D]" has no attribute "g" d.f() [builtins fixtures/classmethod.pyi] @@ -903,7 +902,7 @@ class Parent(NamedTuple): class Child(Parent): pass -reveal_type(Child.class_method()) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.Child]" +reveal_type(Child.class_method()) # N: Revealed type is "tuple[builtins.str, fallback=__main__.Child]" [builtins fixtures/classmethod.pyi] [case testNamedTupleAsConditionalStrictOptionalDisabled] @@ -943,10 +942,10 @@ class MyTupleB(NamedTuple): field_2: MyBaseTuple u: MyTupleUnion -reveal_type(u.field_1) # N: Revealed type is "typing.Mapping[Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]" -reveal_type(u.field_2) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]" -reveal_type(u[0]) # N: Revealed type is "typing.Mapping[Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]" -reveal_type(u[1]) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]" +reveal_type(u.field_1) # N: Revealed type is "typing.Mapping[tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]" +reveal_type(u.field_2) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]" +reveal_type(u[0]) # N: Revealed type is "typing.Mapping[tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple], builtins.int]" +reveal_type(u[1]) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.MyBaseTuple]" [builtins fixtures/tuple.pyi] [case testAssignNamedTupleAsAttribute] @@ -966,8 +965,8 @@ from typing import NamedTuple N = NamedTuple('N', []) n: N -reveal_type(N) # N: Revealed type is "def () -> Tuple[(), fallback=__main__.N]" -reveal_type(n) # N: Revealed type is "Tuple[(), fallback=__main__.N]" +reveal_type(N) # N: Revealed type is "def () -> tuple[(), fallback=__main__.N]" +reveal_type(n) # N: Revealed type is "tuple[(), fallback=__main__.N]" [builtins fixtures/tuple.pyi] [case testNamedTupleWrongfile] @@ -1028,11 +1027,11 @@ print_namedtuple(b5) # ok print_namedtuple(b6) # ok print_namedtuple(1) # E: Argument 1 to "print_namedtuple" has incompatible type "int"; expected "NamedTuple" -print_namedtuple(('bar',)) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[str]"; expected "NamedTuple" -print_namedtuple((1, 2)) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple" -print_namedtuple((b1,)) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[Bar]"; expected "NamedTuple" +print_namedtuple(('bar',)) # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[str]"; expected "NamedTuple" +print_namedtuple((1, 2)) # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[int, int]"; expected "NamedTuple" +print_namedtuple((b1,)) # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[Bar]"; expected "NamedTuple" t: Tuple[str, ...] -print_namedtuple(t) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[str, ...]"; expected "NamedTuple" +print_namedtuple(t) # E: Argument 1 to "print_namedtuple" has incompatible type "tuple[str, ...]"; expected "NamedTuple" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1075,9 +1074,9 @@ def good6() -> NamedTuple: def bad1() -> NamedTuple: return 1 # E: Incompatible return value type (got "int", expected "NamedTuple") def bad2() -> NamedTuple: - return () # E: Incompatible return value type (got "Tuple[()]", expected "NamedTuple") + return () # E: Incompatible return value type (got "tuple[()]", expected "NamedTuple") def bad3() -> NamedTuple: - return (1, 2) # E: Incompatible return value type (got "Tuple[int, int]", expected "NamedTuple") + return (1, 2) # E: Incompatible return value type (got "tuple[int, int]", expected "NamedTuple") [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1091,14 +1090,14 @@ C = NamedTuple("C", [("x", Literal[True, False])]) T = Tuple[Literal[True, False]] # Was error here: -# Incompatible types in assignment (expression has type "List[C]", variable has type "List[C]") +# Incompatible types in assignment (expression has type "list[C]", variable has type "list[C]") x: List[C] = [C(True)] t: T # Was error here: -# Incompatible types in assignment (expression has type "List[Tuple[bool]]", -# variable has type "List[Tuple[Union[Literal[True], Literal[False]]]]") +# Incompatible types in assignment (expression has type "list[tuple[bool]]", +# variable has type "list[tuple[Union[Literal[True], Literal[False]]]]") y: List[T] = [t] [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1115,22 +1114,22 @@ class C(NamedTuple): def foo(c: C) -> None: if c: - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]" else: - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]" def bar(c: C) -> None: if not c: - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]" else: - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]" class C1(NamedTuple): x: int def foo1(c: C1) -> None: if c: - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C1]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C1]" else: c # E: Statement is unreachable @@ -1138,7 +1137,7 @@ def bar1(c: C1) -> None: if not c: c # E: Statement is unreachable else: - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C1]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C1]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1163,7 +1162,7 @@ class One(NamedTuple): bar: int baz: str o: One -reveal_type(o.__match_args__) # N: Revealed type is "Tuple[Literal['bar'], Literal['baz']]" +reveal_type(o.__match_args__) # N: Revealed type is "tuple[Literal['bar'], Literal['baz']]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1203,11 +1202,11 @@ class NT(NamedTuple, Generic[T]): value: T nts: NT[str] -reveal_type(nts) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]" +reveal_type(nts) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]" reveal_type(nts.value) # N: Revealed type is "builtins.str" nti = NT(key=0, value=0) -reveal_type(nti) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" +reveal_type(nti) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" reveal_type(nti.value) # N: Revealed type is "builtins.int" NT[str](key=0, value=0) # E: Argument "value" to "NT" has incompatible type "int"; expected "str" @@ -1225,8 +1224,8 @@ class NT(NamedTuple, Generic[T]): Alias = NT[List[T]] an: Alias[str] -reveal_type(an) # N: Revealed type is "Tuple[builtins.int, builtins.list[builtins.str], fallback=__main__.NT[builtins.list[builtins.str]]]" -Alias[str](key=0, value=0) # E: Argument "value" to "NT" has incompatible type "int"; expected "List[str]" +reveal_type(an) # N: Revealed type is "tuple[builtins.int, builtins.list[builtins.str], fallback=__main__.NT[builtins.list[builtins.str]]]" +Alias[str](key=0, value=0) # E: Argument "value" to "NT" has incompatible type "int"; expected "list[str]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1262,7 +1261,7 @@ nts: NT[str] reveal_type(nts.foo()) # N: Revealed type is "builtins.str" nti = NT.from_value(1) -reveal_type(nti) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" +reveal_type(nti) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" NT[str].from_value(1) # E: Argument 1 to "from_value" of "NT" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1280,7 +1279,7 @@ nti: NT[int] def foo(x: Tuple[int, ...]) -> None: ... foo(nti) -foo(nts) # E: Argument 1 to "foo" has incompatible type "NT[str]"; expected "Tuple[int, ...]" +foo(nts) # E: Argument 1 to "foo" has incompatible type "NT[str]"; expected "tuple[int, ...]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1298,10 +1297,10 @@ x: Tuple[int, ...] S = TypeVar("S") def foo(x: S, y: S) -> S: ... -reveal_type(foo(nti, nti)) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" +reveal_type(foo(nti, nti)) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" -reveal_type(foo(nti, nts)) # N: Revealed type is "Tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]" -reveal_type(foo(nts, nti)) # N: Revealed type is "Tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]" +reveal_type(foo(nti, nts)) # N: Revealed type is "tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]" +reveal_type(foo(nts, nti)) # N: Revealed type is "tuple[builtins.int, builtins.object, fallback=__main__.NT[builtins.object]]" reveal_type(foo(nti, x)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" reveal_type(foo(nts, x)) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]" @@ -1315,13 +1314,13 @@ from typing import NamedTuple, TypeVar T = TypeVar("T") NT = NamedTuple("NT", [("key", int), ("value", T)]) -reveal_type(NT) # N: Revealed type is "def [T] (key: builtins.int, value: T`1) -> Tuple[builtins.int, T`1, fallback=__main__.NT[T`1]]" +reveal_type(NT) # N: Revealed type is "def [T] (key: builtins.int, value: T`1) -> tuple[builtins.int, T`1, fallback=__main__.NT[T`1]]" nts: NT[str] -reveal_type(nts) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]" +reveal_type(nts) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.NT[builtins.str]]" nti = NT(key=0, value=0) -reveal_type(nti) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" +reveal_type(nti) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.NT[builtins.int]]" NT[str](key=0, value=0) # E: Argument "value" to "NT" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1363,7 +1362,7 @@ class NT(NamedTuple, Generic[T]): return self._replace() class SNT(NT[int]): ... -reveal_type(SNT("test", 42).meth()) # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.SNT]" +reveal_type(SNT("test", 42).meth()) # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.SNT]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] @@ -1441,3 +1440,118 @@ def bar() -> None: misspelled_var_name # E: Name "misspelled_var_name" is not defined [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + + +[case testNamedTupleFinalAndClassVar] +from typing import NamedTuple, Final, ClassVar + +class My(NamedTuple): + a: Final # E: Final[...] can't be used inside a NamedTuple + b: Final[int] # E: Final[...] can't be used inside a NamedTuple + c: ClassVar # E: ClassVar[...] can't be used inside a NamedTuple + d: ClassVar[int] # E: ClassVar[...] can't be used inside a NamedTuple + +Func = NamedTuple('Func', [ + ('a', Final), # E: Final[...] can't be used inside a NamedTuple + ('b', Final[int]), # E: Final[...] can't be used inside a NamedTuple + ('c', ClassVar), # E: ClassVar[...] can't be used inside a NamedTuple + ('d', ClassVar[int]), # E: ClassVar[...] can't be used inside a NamedTuple +]) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] + +[case testGenericNamedTupleRecursiveBound] +from typing import Generic, NamedTuple, TypeVar +T = TypeVar("T", bound="NT") +class NT(NamedTuple, Generic[T]): + parent: T + item: int + +def main(n: NT[T]) -> None: + reveal_type(n.parent) # N: Revealed type is "T`-1" + reveal_type(n.item) # N: Revealed type is "builtins.int" + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] + +[case testNamedTupleOverlappingCheck] +from typing import overload, NamedTuple, Union + +class AKey(NamedTuple): + k: str + +class A(NamedTuple): + key: AKey + + +class BKey(NamedTuple): + k: str + +class B(NamedTuple): + key: BKey + +@overload +def f(arg: A) -> A: ... +@overload +def f(arg: B) -> B: ... +def f(arg: Union[A, B]) -> Union[A, B]: ... + +def g(x: Union[A, B, str]) -> Union[A, B, str]: + if isinstance(x, str): + return x + else: + reveal_type(x) # N: Revealed type is "Union[tuple[tuple[builtins.str, fallback=__main__.AKey], fallback=__main__.A], tuple[tuple[builtins.str, fallback=__main__.BKey], fallback=__main__.B]]" + return x._replace() + +# no errors should be raised above. +[builtins fixtures/tuple.pyi] + +[case testNamedTupleUnionAnyMethodCall] +from collections import namedtuple +from typing import Any, Union + +T = namedtuple("T", ["x"]) + +class C(T): + def f(self) -> bool: + return True + +c: Union[C, Any] +reveal_type(c.f()) # N: Revealed type is "Union[builtins.bool, Any]" +[builtins fixtures/tuple.pyi] + +[case testNamedTupleAsClassMemberNoCrash] +# https://github.com/python/mypy/issues/18736 +from collections import namedtuple + +class Base: + def __init__(self, namespace: tuple[str, ...]) -> None: + # Not a bug: trigger defer + names = [name for name in namespace if fail] # E: Name "fail" is not defined + self.n = namedtuple("n", names) # E: NamedTuple type as an attribute is not supported +[builtins fixtures/tuple.pyi] + +[case testNamedTupleDefaultValueDefer] +# flags: --debug-serialize +from typing import NamedTuple + +class NT(NamedTuple): + foo: int = UNDEFINED # E: Name "UNDEFINED" is not defined +[builtins fixtures/tuple.pyi] + +[case testNamedTupleDefaultValueDefer2] +# flags: --debug-serialize +from typing import NamedTuple + +class NT(NamedTuple): + foo: int = DEFERRED_INT + +class NT2(NamedTuple): + foo: int = DEFERRED_STR # E: Incompatible types in assignment (expression has type "str", variable has type "int") + +from foo import DEFERRED_INT, DEFERRED_STR + +[file foo.py] +DEFERRED_INT = 1 +DEFERRED_STR = "a" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 60fc39dd817be..00d33c86414fe 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1,7 +1,6 @@ [case testNarrowingParentWithStrsBasic] from dataclasses import dataclass -from typing import NamedTuple, Tuple, Union -from typing_extensions import Literal, TypedDict +from typing import Literal, NamedTuple, Tuple, TypedDict, Union class Object1: key: Literal["A"] @@ -54,24 +53,24 @@ else: x3: Union[NamedTuple1, NamedTuple2] if x3.key == "A": - reveal_type(x3) # N: Revealed type is "Tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]" + reveal_type(x3) # N: Revealed type is "tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]" reveal_type(x3.key) # N: Revealed type is "Literal['A']" else: - reveal_type(x3) # N: Revealed type is "Tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]" + reveal_type(x3) # N: Revealed type is "tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]" reveal_type(x3.key) # N: Revealed type is "Literal['B']" if x3[0] == "A": - reveal_type(x3) # N: Revealed type is "Tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]" + reveal_type(x3) # N: Revealed type is "tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]" reveal_type(x3[0]) # N: Revealed type is "Literal['A']" else: - reveal_type(x3) # N: Revealed type is "Tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]" + reveal_type(x3) # N: Revealed type is "tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]" reveal_type(x3[0]) # N: Revealed type is "Literal['B']" x4: Union[Tuple1, Tuple2] if x4[0] == "A": - reveal_type(x4) # N: Revealed type is "Tuple[Literal['A'], builtins.int]" + reveal_type(x4) # N: Revealed type is "tuple[Literal['A'], builtins.int]" reveal_type(x4[0]) # N: Revealed type is "Literal['A']" else: - reveal_type(x4) # N: Revealed type is "Tuple[Literal['B'], builtins.str]" + reveal_type(x4) # N: Revealed type is "tuple[Literal['B'], builtins.str]" reveal_type(x4[0]) # N: Revealed type is "Literal['B']" x5: Union[TypedDict1, TypedDict2] @@ -80,12 +79,12 @@ if x5["key"] == "A": else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal['B'], 'foo': builtins.str})" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentWithEnumsBasic] from enum import Enum from dataclasses import dataclass -from typing import NamedTuple, Tuple, Union -from typing_extensions import Literal, TypedDict +from typing import Literal, NamedTuple, Tuple, TypedDict, Union class Key(Enum): A = 1 @@ -143,24 +142,24 @@ else: x3: Union[NamedTuple1, NamedTuple2] if x3.key is Key.A: - reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]" + reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]" reveal_type(x3.key) # N: Revealed type is "Literal[__main__.Key.A]" else: - reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]" + reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]" reveal_type(x3.key) # N: Revealed type is "Literal[__main__.Key.B]" if x3[0] is Key.A: - reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]" + reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]" reveal_type(x3[0]) # N: Revealed type is "Literal[__main__.Key.A]" else: - reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]" + reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]" reveal_type(x3[0]) # N: Revealed type is "Literal[__main__.Key.B]" x4: Union[Tuple1, Tuple2] if x4[0] is Key.A: - reveal_type(x4) # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int]" + reveal_type(x4) # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int]" reveal_type(x4[0]) # N: Revealed type is "Literal[__main__.Key.A]" else: - reveal_type(x4) # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str]" + reveal_type(x4) # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str]" reveal_type(x4[0]) # N: Revealed type is "Literal[__main__.Key.B]" x5: Union[TypedDict1, TypedDict2] @@ -168,12 +167,12 @@ if x5["key"] is Key.A: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal[__main__.Key.A], 'foo': builtins.int})" else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal[__main__.Key.B], 'foo': builtins.str})" -[builtins fixtures/narrowing.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentWithIsInstanceBasic] from dataclasses import dataclass -from typing import NamedTuple, Tuple, Union -from typing_extensions import TypedDict +from typing import NamedTuple, Tuple, TypedDict, Union class Object1: key: int @@ -214,32 +213,32 @@ else: x3: Union[NamedTuple1, NamedTuple2] if isinstance(x3.key, int): - reveal_type(x3) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NamedTuple1]" + reveal_type(x3) # N: Revealed type is "tuple[builtins.int, fallback=__main__.NamedTuple1]" else: - reveal_type(x3) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NamedTuple2]" + reveal_type(x3) # N: Revealed type is "tuple[builtins.str, fallback=__main__.NamedTuple2]" if isinstance(x3[0], int): - reveal_type(x3) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NamedTuple1]" + reveal_type(x3) # N: Revealed type is "tuple[builtins.int, fallback=__main__.NamedTuple1]" else: - reveal_type(x3) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NamedTuple2]" + reveal_type(x3) # N: Revealed type is "tuple[builtins.str, fallback=__main__.NamedTuple2]" x4: Union[Tuple1, Tuple2] if isinstance(x4[0], int): - reveal_type(x4) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(x4) # N: Revealed type is "tuple[builtins.int]" else: - reveal_type(x4) # N: Revealed type is "Tuple[builtins.str]" + reveal_type(x4) # N: Revealed type is "tuple[builtins.str]" x5: Union[TypedDict1, TypedDict2] if isinstance(x5["key"], int): reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': builtins.int})" else: reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': builtins.str})" -[builtins fixtures/narrowing.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentMultipleKeys] # flags: --warn-unreachable from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Key(Enum): A = 1 @@ -271,8 +270,7 @@ else: [case testNarrowingTypedDictParentMultipleKeys] # flags: --warn-unreachable -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, TypedDict, Union class TypedDict1(TypedDict): key: Literal['A', 'C'] @@ -295,11 +293,11 @@ if x['key'] == 'D': else: reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key': Union[Literal['B'], Literal['C']]})]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingPartialTypedDictParentMultipleKeys] # flags: --warn-unreachable -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, TypedDict, Union class TypedDict1(TypedDict, total=False): key: Literal['A', 'C'] @@ -322,10 +320,10 @@ if x['key'] == 'D': else: reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key'?: Union[Literal['B'], Literal['C']]})]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingNestedTypedDicts] -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, TypedDict, Union class A(TypedDict): key: Literal['A'] @@ -350,11 +348,11 @@ if unknown['inner']['key'] == 'C': reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.Y', {'inner': Union[TypedDict('__main__.B', {'key': Literal['B']}), TypedDict('__main__.C', {'key': Literal['C']})]})" reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.C', {'key': Literal['C']})" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentWithMultipleParents] from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Key(Enum): A = 1 @@ -398,8 +396,7 @@ else: [case testNarrowingParentWithParentMixtures] from enum import Enum -from typing import Union, NamedTuple -from typing_extensions import Literal, TypedDict +from typing import Literal, Union, NamedTuple, TypedDict class Key(Enum): A = 1 @@ -417,7 +414,7 @@ ok_mixture: Union[KeyedObject, KeyedNamedTuple] if ok_mixture.key is Key.A: reveal_type(ok_mixture) # N: Revealed type is "__main__.KeyedObject" else: - reveal_type(ok_mixture) # N: Revealed type is "Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" + reveal_type(ok_mixture) # N: Revealed type is "tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" impossible_mixture: Union[KeyedObject, KeyedTypedDict] if impossible_mixture.key is Key.A: # E: Item "KeyedTypedDict" of "Union[KeyedObject, KeyedTypedDict]" has no attribute "key" @@ -434,22 +431,21 @@ weird_mixture: Union[KeyedTypedDict, KeyedNamedTuple] if weird_mixture["key"] is Key.B: # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> Literal[Key.C] \ - # N: def __getitem__(self, slice, /) -> Tuple[Literal[Key.C], ...] - reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" + # N: def __getitem__(self, slice, /) -> tuple[Literal[Key.C], ...] + reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" else: - reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" + reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" if weird_mixture[0] is Key.B: # E: TypedDict key must be a string literal; expected one of ("key") - reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" + reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" else: - reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" + reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] [case testNarrowingParentWithProperties] from enum import Enum -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Key(Enum): A = 1 @@ -476,8 +472,7 @@ else: [case testNarrowingParentWithAny] from enum import Enum -from typing import Union, Any -from typing_extensions import Literal +from typing import Literal, Union, Any class Key(Enum): A = 1 @@ -501,8 +496,7 @@ else: [builtins fixtures/tuple.pyi] [case testNarrowingParentsHierarchy] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union from enum import Enum class Key(Enum): @@ -580,8 +574,7 @@ else: [case testNarrowingParentsHierarchyTypedDict] # flags: --warn-unreachable -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, TypedDict, Union from enum import Enum class Key(Enum): @@ -618,12 +611,12 @@ if y["model"]["key"] is Key.C: else: reveal_type(y) # N: Revealed type is "Union[TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), 'foo': builtins.int}), TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]}), 'bar': builtins.str})]" reveal_type(y["model"]) # N: Revealed type is "Union[TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]})]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingParentsHierarchyTypedDictWithStr] # flags: --warn-unreachable -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, TypedDict, Union class Parent1(TypedDict): model: Model1 @@ -655,10 +648,10 @@ else: reveal_type(y) # N: Revealed type is "Union[TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal['A']}), 'foo': builtins.int}), TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal['B']}), 'bar': builtins.str})]" reveal_type(y["model"]) # N: Revealed type is "Union[TypedDict('__main__.Model1', {'key': Literal['A']}), TypedDict('__main__.Model2', {'key': Literal['B']})]" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingExprPropagation] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class A: tag: Literal['A'] @@ -679,7 +672,7 @@ if not (abo is None or abo.tag != "B"): [case testNarrowingEqualityFlipFlop] # flags: --warn-unreachable --strict-equality -from typing_extensions import Literal, Final +from typing import Final, Literal from enum import Enum class State(Enum): @@ -744,7 +737,7 @@ def test3(switch: FlipFlopEnum) -> None: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitStrLiteral] -from typing_extensions import Literal, Final +from typing import Final, Literal A_final: Final = "A" A_literal: Literal["A"] @@ -790,8 +783,7 @@ reveal_type(x_union) # N: Revealed type is "Union[Literal['A'], Literal['B' [builtins fixtures/primitives.pyi] [case testNarrowingEqualityRequiresExplicitEnumLiteral] -from typing import Union -from typing_extensions import Literal, Final +from typing import Final, Literal, Union from enum import Enum class Foo(Enum): @@ -832,8 +824,7 @@ def bar(x: Union[SingletonFoo, Foo], y: SingletonFoo) -> None: [builtins fixtures/primitives.pyi] [case testNarrowingEqualityDisabledForCustomEquality] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union from enum import Enum class Custom: @@ -875,8 +866,7 @@ else: [case testNarrowingEqualityDisabledForCustomEqualityChain] # flags: --strict-equality --warn-unreachable -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union class Custom: def __eq__(self, other: object) -> bool: return True @@ -912,8 +902,7 @@ else: [case testNarrowingUnreachableCases] # flags: --strict-equality --warn-unreachable -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union a: Literal[1] b: Literal[1, 2] @@ -960,8 +949,7 @@ else: [case testNarrowingUnreachableCases2] # flags: --strict-equality --warn-unreachable -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union a: Literal[1, 2, 3, 4] b: Literal[1, 2, 3, 4] @@ -999,8 +987,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingLiteralTruthiness] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union str_or_false: Union[Literal[False], str] @@ -1119,7 +1106,7 @@ T = TypeVar("T", A, B) def f(cls: Type[T]) -> T: if issubclass(cls, A): - reveal_type(cls) # N: Revealed type is "Type[__main__.A]" + reveal_type(cls) # N: Revealed type is "type[__main__.A]" x: bool if x: return A() @@ -1133,8 +1120,7 @@ reveal_type(f(B)) # N: Revealed type is "__main__.B" [case testNarrowingLiteralIdentityCheck] -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union str_or_false: Union[Literal[False], str] @@ -1174,8 +1160,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingBooleanIdentityCheck] -from typing import Optional -from typing_extensions import Literal +from typing import Literal, Optional bool_val: bool @@ -1196,8 +1181,7 @@ else: [builtins fixtures/primitives.pyi] [case testNarrowingBooleanTruthiness] -from typing import Optional -from typing_extensions import Literal +from typing import Literal, Optional bool_val: bool @@ -1217,8 +1201,7 @@ reveal_type(opt_bool_val) # N: Revealed type is "Union[builtins.bool, None]" [builtins fixtures/primitives.pyi] [case testNarrowingBooleanBoolOp] -from typing import Optional -from typing_extensions import Literal +from typing import Literal, Optional bool_a: bool bool_b: bool @@ -1245,8 +1228,7 @@ reveal_type(x) # N: Revealed type is "builtins.bool" [builtins fixtures/primitives.pyi] [case testNarrowingTypedDictUsingEnumLiteral] -from typing import Union -from typing_extensions import TypedDict, Literal +from typing import Literal, TypedDict, Union from enum import Enum class E(Enum): @@ -1266,6 +1248,7 @@ def f(d: Union[Foo, Bar]) -> None: d['x'] reveal_type(d) # N: Revealed type is "TypedDict('__main__.Foo', {'tag': Literal[__main__.E.FOO], 'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNarrowingUsingMetaclass] from typing import Type @@ -1277,14 +1260,14 @@ class C: pass def f(t: Type[C]) -> None: if type(t) is M: - reveal_type(t) # N: Revealed type is "Type[__main__.C]" + reveal_type(t) # N: Revealed type is "type[__main__.C]" else: - reveal_type(t) # N: Revealed type is "Type[__main__.C]" + reveal_type(t) # N: Revealed type is "type[__main__.C]" if type(t) is not M: - reveal_type(t) # N: Revealed type is "Type[__main__.C]" + reveal_type(t) # N: Revealed type is "type[__main__.C]" else: - reveal_type(t) # N: Revealed type is "Type[__main__.C]" - reveal_type(t) # N: Revealed type is "Type[__main__.C]" + reveal_type(t) # N: Revealed type is "type[__main__.C]" + reveal_type(t) # N: Revealed type is "type[__main__.C]" [case testNarrowingUsingTypeVar] from typing import Type, TypeVar @@ -1301,13 +1284,12 @@ def f(t: Type[T], a: A, b: B) -> None: reveal_type(a) # N: Revealed type is "__main__.A" if type(b) is t: - reveal_type(b) # N: Revealed type is "Never" + reveal_type(b) # N: Revealed type is "T`-1" else: reveal_type(b) # N: Revealed type is "__main__.B" [case testNarrowingNestedUnionOfTypedDicts] -from typing import Union -from typing_extensions import Literal, TypedDict +from typing import Literal, TypedDict, Union class A(TypedDict): tag: Literal["A"] @@ -1331,9 +1313,8 @@ elif abc["tag"] == "C": reveal_type(abc) # N: Revealed type is "TypedDict('__main__.C', {'tag': Literal['C'], 'c': builtins.int})" else: reveal_type(abc) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['B'], 'b': builtins.int})" - [builtins fixtures/primitives.pyi] - +[typing fixtures/typing-typeddict.pyi] [case testNarrowingRuntimeCover] from typing import Dict, List, Union @@ -1385,9 +1366,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]" @@ -1521,14 +1502,14 @@ from typing import Tuple x: Tuple[int, ...] if len(x) == 3: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" else: reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" if len(x) != 3: reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" [builtins fixtures/len.pyi] [case testNarrowingLenTypeUnaffected] @@ -1560,20 +1541,19 @@ VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] x: VarTuple y: VarTuple if len(x) == len(y) == 3: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" - reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" [builtins fixtures/len.pyi] [case testNarrowingLenFinal] -from typing import Tuple, Union -from typing_extensions import Final +from typing import Final, Tuple, Union VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] x: VarTuple fin: Final = 3 if len(x) == fin: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" [builtins fixtures/len.pyi] [case testNarrowingLenGreaterThan] @@ -1583,24 +1563,24 @@ VarTuple = Union[Tuple[int], Tuple[int, int], Tuple[int, int, int]] x: VarTuple if len(x) > 1: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int]" if len(x) < 2: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" if len(x) >= 2: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int]" if len(x) <= 2: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" [builtins fixtures/len.pyi] [case testNarrowingLenBothSidesUnionTuples] @@ -1615,9 +1595,9 @@ VarTuple = Union[ x: VarTuple if 2 <= len(x) <= 3: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int, builtins.int, builtins.int]]" [builtins fixtures/len.pyi] [case testNarrowingLenGreaterThanHomogeneousTupleShort] @@ -1628,9 +1608,9 @@ VarTuple = Tuple[int, ...] x: VarTuple if len(x) < 3: - reveal_type(x) # N: Revealed type is "Union[Tuple[()], Tuple[builtins.int], Tuple[builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[()], tuple[builtins.int], tuple[builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/len.pyi] @@ -1653,9 +1633,9 @@ from typing import Tuple x: Tuple[int, ...] if 1 < len(x) < 4: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[()], Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]]" + reveal_type(x) # N: Revealed type is "Union[tuple[()], tuple[builtins.int], tuple[builtins.int, builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]]" reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/len.pyi] @@ -1667,12 +1647,12 @@ x: Union[Tuple[int, int], Tuple[int, int, int]] if len(x) >= 4: reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" if len(x) < 2: reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" [builtins fixtures/len.pyi] [case testNarrowingLenMixedTypes] @@ -1681,17 +1661,17 @@ from typing import Tuple, List, Union x: Union[Tuple[int, int], Tuple[int, int, int], List[int]] a = b = c = 0 if len(x) == 3: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]" a, b, c = x else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.list[builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.list[builtins.int]]" a, b = x if len(x) != 3: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.list[builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.list[builtins.int]]" a, b = x else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int, builtins.int], builtins.list[builtins.int]]" a, b, c = x [builtins fixtures/len.pyi] @@ -1702,14 +1682,14 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") def foo(x: Tuple[int, Unpack[Ts], str]) -> None: if len(x) == 5: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" if len(x) != 5: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" [builtins fixtures/len.pyi] [case testNarrowingLenTypeVarTupleGreaterThan] @@ -1719,17 +1699,17 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") def foo(x: Tuple[int, Unpack[Ts], str]) -> None: if len(x) > 5: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" reveal_type(x[5]) # N: Revealed type is "builtins.object" reveal_type(x[-6]) # N: Revealed type is "builtins.object" reveal_type(x[-1]) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" if len(x) < 5: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" x[5] # E: Tuple index out of range \ # N: Variadic tuple can have length 5 x[-6] # E: Tuple index out of range \ @@ -1750,23 +1730,23 @@ def foo(x: Tuple[int, Unpack[Ts], str]) -> None: if len(x) == 1: reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" if len(x) != 1: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" else: reveal_type(x) # E: Statement is unreachable def bar(x: Tuple[int, Unpack[Ts], str]) -> None: if len(x) >= 2: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" else: reveal_type(x) # E: Statement is unreachable if len(x) < 2: reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" [builtins fixtures/len.pyi] [case testNarrowingLenVariadicTupleEquals] @@ -1775,14 +1755,14 @@ from typing_extensions import Unpack def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: if len(x) == 4: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, builtins.str]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" if len(x) != 4: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, builtins.str]" [builtins fixtures/len.pyi] [case testNarrowingLenVariadicTupleGreaterThan] @@ -1791,16 +1771,16 @@ from typing_extensions import Unpack def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: if len(x) > 3: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], Tuple[builtins.int, builtins.float, builtins.str]]" - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.str], tuple[builtins.int, builtins.float, builtins.str]]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" if len(x) < 3: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" [builtins fixtures/len.pyi] [case testNarrowingLenVariadicTupleUnreachable] @@ -1812,23 +1792,23 @@ def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: if len(x) == 1: reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" if len(x) != 1: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" else: reveal_type(x) # E: Statement is unreachable def bar(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: if len(x) >= 2: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" else: reveal_type(x) # E: Statement is unreachable if len(x) < 2: reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" [builtins fixtures/len.pyi] [case testNarrowingLenBareExpressionPrecise] @@ -1837,7 +1817,7 @@ from typing import Tuple x: Tuple[int, ...] assert x -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" [builtins fixtures/len.pyi] [case testNarrowingLenBareExpressionTypeVarTuple] @@ -1856,9 +1836,9 @@ from typing import Tuple, Optional x: Optional[Tuple[int, ...]] if x: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[()], None]" + reveal_type(x) # N: Revealed type is "Union[tuple[()], None]" [builtins fixtures/len.pyi] [case testNarrowingLenBareExpressionWithNoneImprecise] @@ -1877,14 +1857,14 @@ from typing import Any x: Any if isinstance(x, (list, tuple)) and len(x) == 0: - reveal_type(x) # N: Revealed type is "Union[Tuple[()], builtins.list[Any]]" + reveal_type(x) # N: Revealed type is "Union[tuple[()], builtins.list[Any]]" else: reveal_type(x) # N: Revealed type is "Any" reveal_type(x) # N: Revealed type is "Any" x1: Any if isinstance(x1, (list, tuple)) and len(x1) > 1: - reveal_type(x1) # N: Revealed type is "Union[Tuple[Any, Any, Unpack[builtins.tuple[Any, ...]]], builtins.list[Any]]" + reveal_type(x1) # N: Revealed type is "Union[tuple[Any, Any, Unpack[builtins.tuple[Any, ...]]], builtins.list[Any]]" else: reveal_type(x1) # N: Revealed type is "Any" reveal_type(x1) # N: Revealed type is "Any" @@ -1895,7 +1875,7 @@ from typing import Any x: Any if isinstance(x, (list, tuple)) and len(x) == 0: - reveal_type(x) # N: Revealed type is "Union[Tuple[()], builtins.list[Any]]" + reveal_type(x) # N: Revealed type is "Union[tuple[()], builtins.list[Any]]" else: reveal_type(x) # N: Revealed type is "Any" reveal_type(x) # N: Revealed type is "Any" @@ -1909,8 +1889,7 @@ reveal_type(x1) # N: Revealed type is "Any" [builtins fixtures/len.pyi] [case testNarrowingLenExplicitLiteralTypes] -from typing import Tuple, Union -from typing_extensions import Literal +from typing import Literal, Tuple, Union VarTuple = Union[ Tuple[int], @@ -1921,15 +1900,15 @@ x: VarTuple supported: Literal[2] if len(x) == supported: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" not_supported_yet: Literal[2, 3] if len(x) == not_supported_yet: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int], Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int], tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" [builtins fixtures/len.pyi] [case testNarrowingLenUnionOfVariadicTuples] @@ -1937,7 +1916,7 @@ from typing import Tuple, Union x: Union[Tuple[int, ...], Tuple[str, ...]] if len(x) == 2: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.str, builtins.str]]" else: reveal_type(x) # N: Revealed type is "Union[builtins.tuple[builtins.int, ...], builtins.tuple[builtins.str, ...]]" [builtins fixtures/len.pyi] @@ -1955,9 +1934,9 @@ class Point3D(NamedTuple): x: Union[Point2D, Point3D] if len(x) == 2: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.Point2D]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.Point2D]" else: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.int, fallback=__main__.Point3D]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, fallback=__main__.Point3D]" [builtins fixtures/len.pyi] [case testNarrowingLenTupleSubclass] @@ -1968,7 +1947,7 @@ class Ints(Tuple[int, ...]): x: Ints if len(x) == 2: - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.Ints]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.Ints]" reveal_type(x.size) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "__main__.Ints" @@ -2012,15 +1991,15 @@ x: Union[Tuple[int, int], Tuple[int, int, int]] n: int if len(x) == n: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" a: Any if len(x) == a: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]" + reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.int, builtins.int, builtins.int]]" [builtins fixtures/len.pyi] [case testNarrowingLenUnionWithUnreachable] @@ -2033,7 +2012,7 @@ def f(x: Union[int, Sequence[int]]) -> None: and isinstance(x[0], int) and isinstance(x[1], int) ): - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/len.pyi] [case testNarrowingIsSubclassNoneType1] @@ -2041,9 +2020,9 @@ from typing import Type, Union def f(cls: Type[Union[None, int]]) -> None: if issubclass(cls, int): - reveal_type(cls) # N: Revealed type is "Type[builtins.int]" + reveal_type(cls) # N: Revealed type is "type[builtins.int]" else: - reveal_type(cls) # N: Revealed type is "Type[None]" + reveal_type(cls) # N: Revealed type is "type[None]" [builtins fixtures/isinstance.pyi] [case testNarrowingIsSubclassNoneType2] @@ -2051,9 +2030,9 @@ from typing import Type, Union def f(cls: Type[Union[None, int]]) -> None: if issubclass(cls, type(None)): - reveal_type(cls) # N: Revealed type is "Type[None]" + reveal_type(cls) # N: Revealed type is "type[None]" else: - reveal_type(cls) # N: Revealed type is "Type[builtins.int]" + reveal_type(cls) # N: Revealed type is "type[builtins.int]" [builtins fixtures/isinstance.pyi] [case testNarrowingIsSubclassNoneType3] @@ -2063,9 +2042,9 @@ NoneType_ = type(None) def f(cls: Type[Union[None, int]]) -> None: if issubclass(cls, NoneType_): - reveal_type(cls) # N: Revealed type is "Type[None]" + reveal_type(cls) # N: Revealed type is "type[None]" else: - reveal_type(cls) # N: Revealed type is "Type[builtins.int]" + reveal_type(cls) # N: Revealed type is "type[builtins.int]" [builtins fixtures/isinstance.pyi] [case testNarrowingIsSubclassNoneType4] @@ -2076,9 +2055,9 @@ from typing import Type, Union def f(cls: Type[Union[None, int]]) -> None: if issubclass(cls, NoneType): - reveal_type(cls) # N: Revealed type is "Type[None]" + reveal_type(cls) # N: Revealed type is "type[None]" else: - reveal_type(cls) # N: Revealed type is "Type[builtins.int]" + reveal_type(cls) # N: Revealed type is "type[builtins.int]" [builtins fixtures/isinstance.pyi] [case testNarrowingIsInstanceNoIntersectionWithFinalTypeAndNoneType] @@ -2095,11 +2074,11 @@ class Z: ... x: X if isinstance(x, (Y, Z)): - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, (Y, NoneType)): - reveal_type(x) # N: Revealed type is "__main__.1" + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, (Y, Z, NoneType)): - reveal_type(x) # N: Revealed type is "__main__.2" + reveal_type(x) # N: Revealed type is "__main__." if isinstance(x, (Z, NoneType)): # E: Subclass of "X" and "Z" cannot exist: "Z" is final \ # E: Subclass of "X" and "NoneType" cannot exist: "NoneType" is final reveal_type(x) # E: Statement is unreachable @@ -2291,3 +2270,397 @@ def f4(x: SE) -> None: 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] + +[case testRefinePartialTypeWithinLoop] +# flags: --no-local-partial-types + +x = None +for _ in range(2): + if x is not None: + reveal_type(x) # N: Revealed type is "builtins.int" + x = 1 +reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +def f() -> bool: ... + +y = None +while f(): + reveal_type(y) # N: Revealed type is "Union[None, builtins.int]" + y = 1 +reveal_type(y) # N: Revealed type is "Union[builtins.int, None]" + +z = [] # E: Need type annotation for "z" (hint: "z: list[] = ...") +def g() -> None: + for i in range(2): + while f(): + if z: + z[0] + "v" # E: Unsupported operand types for + ("int" and "str") + z.append(1) + +class A: + def g(self) -> None: + z = [] # E: Need type annotation for "z" (hint: "z: list[] = ...") + for i in range(2): + while f(): + if z: + z[0] + "v" # E: Unsupported operand types for + ("int" and "str") + z.append(1) +[builtins fixtures/primitives.pyi] + +[case testPersistentUnreachableLinesNestedInInpersistentUnreachableLines] +# flags: --warn-unreachable --python-version 3.11 + +x = None +y = None +while True: + if x is not None: + if y is not None: + reveal_type(y) # E: Statement is unreachable + x = 1 +[builtins fixtures/bool.pyi] + +[case testAvoidFalseRedundantCastInLoops] +# flags: --warn-redundant-casts + +from typing import Callable, cast, Union + +ProcessorReturnValue = Union[str, int] +Processor = Callable[[str], ProcessorReturnValue] + +def main_cast(p: Processor) -> None: + ed: ProcessorReturnValue + ed = cast(str, ...) + while True: + ed = p(cast(str, ed)) + +def main_no_cast(p: Processor) -> None: + ed: ProcessorReturnValue + ed = cast(str, ...) + while True: + ed = p(ed) # E: Argument 1 has incompatible type "Union[str, int]"; expected "str" +[builtins fixtures/bool.pyi] + +[case testAvoidFalseUnreachableInLoop1] +# flags: --warn-unreachable --python-version 3.11 + +def f() -> int | None: ... +def b() -> bool: ... + +x: int | None +x = 1 +while x is not None or b(): + x = f() +[builtins fixtures/bool.pyi] + +[case testAvoidFalseUnreachableInLoop2] +# flags: --warn-unreachable --python-version 3.11 + +y = None +while y is None: + if y is None: + y = [] + y.append(1) +[builtins fixtures/list.pyi] + +[case testAvoidFalseUnreachableInLoop3] +# flags: --warn-unreachable --python-version 3.11 + +xs: list[int | None] +y = None +for x in xs: + if x is not None: + if y is None: + y = {} # E: Need type annotation for "y" (hint: "y: dict[, ] = ...") +[builtins fixtures/list.pyi] + +[case testAvoidFalseRedundantExprInLoop] +# flags: --enable-error-code redundant-expr --python-version 3.11 + +def f() -> int | None: ... +def b() -> bool: ... + +x: int | None +x = 1 +while x is not None and b(): + x = f() +[builtins fixtures/primitives.pyi] + +[case testNarrowPromotionsInsideUnions1] + +from typing import Union + +x: Union[str, float, None] +y: Union[int, str] +x = y +reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" +z: Union[complex, str] +z = x +reveal_type(z) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[builtins fixtures/primitives.pyi] + +[case testNarrowPromotionsInsideUnions2] +# flags: --warn-unreachable + +from typing import Optional + +def b() -> bool: ... +def i() -> int: ... +x: Optional[float] + +while b(): + x = None + while b(): + reveal_type(x) # N: Revealed type is "Union[None, builtins.int]" + if x is None or b(): + x = i() + reveal_type(x) # N: Revealed type is "builtins.int" + +[builtins fixtures/bool.pyi] + +[case testAvoidFalseUnreachableInFinally] +# flags: --allow-redefinition-new --local-partial-types --warn-unreachable +def f() -> None: + try: + x = 1 + if int(): + x = "" + return + if int(): + x = None + return + finally: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" + if isinstance(x, str): + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" + +[builtins fixtures/isinstancelist.pyi] + +[case testNarrowingTypeVarMultiple] +from typing import TypeVar + +class A: ... +class B: ... + +T = TypeVar("T") +def foo(x: T) -> T: + if isinstance(x, A): + pass + elif isinstance(x, B): + pass + else: + raise + reveal_type(x) # N: Revealed type is "T`-1" + return x +[builtins fixtures/isinstance.pyi] + +[case testDoNotNarrowToNever] +def any(): + return 1 + +def f() -> None: + x = "a" + x = any() + assert isinstance(x, int) + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/isinstance.pyi] + +[case testNarrowTypeVarBoundType] +from typing import Type, TypeVar + +class A: ... +class B(A): + other: int + +T = TypeVar("T", bound=A) +def test(cls: Type[T]) -> T: + if issubclass(cls, B): + reveal_type(cls) # N: Revealed type is "type[T`-1]" + reveal_type(cls().other) # N: Revealed type is "builtins.int" + return cls() + return cls() +[builtins fixtures/isinstance.pyi] + +[case testNarrowTypeVarBoundUnion] +from typing import TypeVar + +class A: + x: int +class B: + x: str + +T = TypeVar("T") +def test(x: T) -> T: + if not isinstance(x, (A, B)): + return x + reveal_type(x) # N: Revealed type is "T`-1" + reveal_type(x.x) # N: Revealed type is "Union[builtins.int, builtins.str]" + if isinstance(x, A): + reveal_type(x) # N: Revealed type is "T`-1" + reveal_type(x.x) # N: Revealed type is "builtins.int" + return x + reveal_type(x) # N: Revealed type is "T`-1" + reveal_type(x.x) # N: Revealed type is "builtins.str" + return x +[builtins fixtures/isinstance.pyi] + +[case testIsinstanceNarrowingWithSelfTypes] +from typing import Generic, TypeVar, overload + +T = TypeVar("T") + +class A(Generic[T]): + def __init__(self: A[int]) -> None: + pass + +def check_a(obj: "A[T] | str") -> None: + reveal_type(obj) # N: Revealed type is "Union[__main__.A[T`-1], builtins.str]" + if isinstance(obj, A): + reveal_type(obj) # N: Revealed type is "__main__.A[T`-1]" + else: + reveal_type(obj) # N: Revealed type is "builtins.str" + + +class B(Generic[T]): + @overload + def __init__(self, x: T) -> None: ... + @overload + def __init__(self: B[int]) -> None: ... + def __init__(self, x: "T | None" = None) -> None: + pass + +def check_b(obj: "B[T] | str") -> None: + reveal_type(obj) # N: Revealed type is "Union[__main__.B[T`-1], builtins.str]" + if isinstance(obj, B): + reveal_type(obj) # N: Revealed type is "__main__.B[T`-1]" + else: + reveal_type(obj) # N: Revealed type is "builtins.str" + + +class C(Generic[T]): + @overload + def __init__(self: C[int]) -> None: ... + @overload + def __init__(self, x: T) -> None: ... + def __init__(self, x: "T | None" = None) -> None: + pass + +def check_c(obj: "C[T] | str") -> None: + reveal_type(obj) # N: Revealed type is "Union[__main__.C[T`-1], builtins.str]" + if isinstance(obj, C): + reveal_type(obj) # N: Revealed type is "__main__.C[T`-1]" + else: + reveal_type(obj) # N: Revealed type is "builtins.str" + + +class D(tuple[T], Generic[T]): ... + +def check_d(arg: D[T]) -> None: + if not isinstance(arg, D): + return + reveal_type(arg) # N: Revealed type is "tuple[T`-1, fallback=__main__.D[Any]]" +[builtins fixtures/tuple.pyi] + + +[case testNarrowingUnionMixins] +class Base: ... + +class FooMixin: + def foo(self) -> None: ... + +class BarMixin: + def bar(self) -> None: ... + +def baz(item: Base) -> None: + if not isinstance(item, (FooMixin, BarMixin)): + raise + + reveal_type(item) # N: Revealed type is "Union[__main__., __main__.]" + if isinstance(item, FooMixin): + reveal_type(item) # N: Revealed type is "__main__." + item.foo() + else: + reveal_type(item) # N: Revealed type is "__main__." + item.bar() +[builtins fixtures/isinstance.pyi] + +[case testCustomSetterNarrowingReWidened] +class B: ... +class C(B): ... +class C1(B): ... +class D(C): ... + +class Test: + @property + def foo(self) -> C: ... + @foo.setter + def foo(self, val: B) -> None: ... + +t: Test +t.foo = D() +reveal_type(t.foo) # N: Revealed type is "__main__.D" +t.foo = C1() +reveal_type(t.foo) # N: Revealed type is "__main__.C" +[builtins fixtures/property.pyi] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 511c7b003015f..61bf080187226 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -229,7 +229,7 @@ class C(B): [targets b, a, b, a, __main__] [case testNewAnalyzerTypedDictClass] -from mypy_extensions import TypedDict +from typing import TypedDict import a class T1(TypedDict): x: A @@ -237,7 +237,7 @@ class A: pass reveal_type(T1(x=A())) # E [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict from b import TD1 as TD2, TD3 class T2(TD3): x: int @@ -246,7 +246,8 @@ reveal_type(T2(x=2)) # E [file b.py] from a import TypedDict as TD1 from a import TD2 as TD3 -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] tmp/a.py:5: note: Revealed type is "TypedDict('a.T2', {'x': builtins.int})" @@ -254,7 +255,7 @@ main:6: note: Revealed type is "TypedDict('__main__.T1', {'x': __main__.A})" [case testNewAnalyzerTypedDictClassInheritance] -from mypy_extensions import TypedDict +from typing import TypedDict class T2(T1): y: int @@ -275,7 +276,8 @@ x: T2 reveal_type(x) # N: Revealed type is "TypedDict('__main__.T2', {'x': builtins.str, 'y': builtins.int})" y: T4 reveal_type(y) # N: Revealed type is "TypedDict('__main__.T4', {'x': builtins.str, 'y': __main__.A})" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNewAnalyzerRedefinitionAndDeferral1a] import a @@ -861,8 +863,8 @@ In = NamedTuple('In', [('s', str), ('t', Other)]) Out = NamedTuple('Out', [('x', In), ('y', Other)]) o: Out i: In -reveal_type(o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" -reveal_type(o.x) # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]" +reveal_type(o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" +reveal_type(o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other, fallback=__main__.In]" reveal_type(o.y) # N: Revealed type is "__main__.Other" reveal_type(o.x.t) # N: Revealed type is "__main__.Other" reveal_type(i.t) # N: Revealed type is "__main__.Other" @@ -878,8 +880,8 @@ class Out(NamedTuple): x: In y: Other -reveal_type(o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" -reveal_type(o.x) # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]" +reveal_type(o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" +reveal_type(o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other, fallback=__main__.In]" reveal_type(o.y) # N: Revealed type is "__main__.Other" reveal_type(o.x.t) # N: Revealed type is "__main__.Other" reveal_type(i.t) # N: Revealed type is "__main__.Other" @@ -896,8 +898,8 @@ from typing import NamedTuple o: C.Out i: C.In -reveal_type(o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]" -reveal_type(o.x) # N: Revealed type is "Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]" +reveal_type(o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]" +reveal_type(o.x) # N: Revealed type is "tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]" reveal_type(o.y) # N: Revealed type is "__main__.C.Other" reveal_type(o.x.t) # N: Revealed type is "__main__.C.Other" reveal_type(i.t) # N: Revealed type is "__main__.C.Other" @@ -915,8 +917,8 @@ from typing import NamedTuple o: C.Out i: C.In -reveal_type(o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]" -reveal_type(o.x) # N: Revealed type is "Tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]" +reveal_type(o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In], __main__.C.Other, fallback=__main__.C.Out]" +reveal_type(o.x) # N: Revealed type is "tuple[builtins.str, __main__.C.Other, fallback=__main__.C.In]" reveal_type(o.y) # N: Revealed type is "__main__.C.Other" reveal_type(o.x.t) # N: Revealed type is "__main__.C.Other" reveal_type(i.t) # N: Revealed type is "__main__.C.Other" @@ -942,8 +944,8 @@ class C: self.o: Out c = C() -reveal_type(c.o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6], __main__.Other@7, fallback=__main__.C.Out@5]" -reveal_type(c.o.x) # N: Revealed type is "Tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6]" +reveal_type(c.o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6], __main__.Other@7, fallback=__main__.C.Out@5]" +reveal_type(c.o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerNamedTupleClassNestedMethod] @@ -962,16 +964,16 @@ class C: self.o: Out c = C() -reveal_type(c.o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9], __main__.Other@12, fallback=__main__.C.Out@5]" -reveal_type(c.o.x) # N: Revealed type is "Tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]" -reveal_type(c.o.method()) # N: Revealed type is "Tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]" +reveal_type(c.o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9], __main__.Other@12, fallback=__main__.C.Out@5]" +reveal_type(c.o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]" +reveal_type(c.o.method()) # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerNamedTupleClassForwardMethod] from typing import NamedTuple n: NT -reveal_type(n.get_other()) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.Other]" +reveal_type(n.get_other()) # N: Revealed type is "tuple[builtins.str, fallback=__main__.Other]" reveal_type(n.get_other().s) # N: Revealed type is "builtins.str" class NT(NamedTuple): @@ -993,8 +995,8 @@ class SubO(Out): pass o: SubO -reveal_type(SubO._make) # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" -reveal_type(o._replace(y=Other())) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" +reveal_type(SubO._make) # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" +reveal_type(o._replace(y=Other())) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.SubO]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerNamedTupleBaseClass] @@ -1007,10 +1009,10 @@ class Out(NamedTuple('Out', [('x', In), ('y', Other)])): pass o: Out -reveal_type(o) # N: Revealed type is "Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" -reveal_type(o.x) # N: Revealed type is "Tuple[builtins.str, __main__.Other, fallback=__main__.In]" +reveal_type(o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" +reveal_type(o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other, fallback=__main__.In]" reveal_type(o.x.t) # N: Revealed type is "__main__.Other" -reveal_type(Out._make) # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> Tuple[Tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" +reveal_type(Out._make) # N: Revealed type is "def (iterable: typing.Iterable[Any]) -> tuple[tuple[builtins.str, __main__.Other, fallback=__main__.In], __main__.Other, fallback=__main__.Out]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerIncompleteRefShadowsBuiltin1] @@ -1076,7 +1078,7 @@ from b import C import a [file a.py] C = 1 -from b import C # E: Incompatible import of "C" (imported name has type "Type[C]", local name has type "int") +from b import C # E: Incompatible import of "C" (imported name has type "type[C]", local name has type "int") [file b.py] import a @@ -1090,7 +1092,7 @@ import a C = 1 MYPY = False if MYPY: # Tweak processing order - from b import * # E: Incompatible import of "C" (imported name has type "Type[C]", local name has type "int") + from b import * # E: Incompatible import of "C" (imported name has type "type[C]", local name has type "int") [file b.py] import a @@ -1102,7 +1104,7 @@ class B: ... import a [file a.py] C = 1 -from b import * # E: Incompatible import of "C" (imported name has type "Type[C]", local name has type "int") +from b import * # E: Incompatible import of "C" (imported name has type "type[C]", local name has type "int") [file b.py] MYPY = False @@ -1430,7 +1432,7 @@ from a import x class B(List[B], Generic[T]): pass T = TypeVar('T') -reveal_type(x) # N: Revealed type is "b.B[Tuple[builtins.int, builtins.int]]" +reveal_type(x) # N: Revealed type is "b.B[tuple[builtins.int, builtins.int]]" [builtins fixtures/list.pyi] [case testNewAnalyzerAliasToNotReadyClassInGeneric] @@ -1447,7 +1449,7 @@ from a import x class B(List[B]): pass -reveal_type(x) # N: Revealed type is "Tuple[b.B, b.B]" +reveal_type(x) # N: Revealed type is "tuple[b.B, b.B]" [builtins fixtures/list.pyi] [case testNewAnalyzerAliasToNotReadyClassDoubleGeneric] @@ -1568,7 +1570,7 @@ import a [file a.py] from b import B def func() -> B: ... -reveal_type(func()) # N: Revealed type is "builtins.list[Tuple[b.C, b.C]]" +reveal_type(func()) # N: Revealed type is "builtins.list[tuple[b.C, b.C]]" [file b.py] from typing import List, Tuple @@ -1595,7 +1597,7 @@ abl: List[Tuple[A, B]] abd = {a: b for a, b in abl} x: Dict[B, A] = {a: b for a, b in abl} # E: Key expression in dictionary comprehension has incompatible type "A"; expected type "B" \ # E: Value expression in dictionary comprehension has incompatible type "B"; expected type "A" -y: A = {a: b for a, b in abl} # E: Incompatible types in assignment (expression has type "Dict[A, B]", variable has type "A") +y: A = {a: b for a, b in abl} # E: Incompatible types in assignment (expression has type "dict[A, B]", variable has type "A") class A: pass class B: pass [builtins fixtures/dict.pyi] @@ -1659,15 +1661,14 @@ tmp/a.py:10: error: Type argument "str" of "C" must be a subtype of "int" tmp/a.py:11: error: Type argument "str" of "C" must be a subtype of "int" [case testNewAnalyzerTypeArgBoundCheckDifferentNodes] -from typing import TypeVar, Generic, NamedTuple, NewType, Union, Any, cast, overload -from mypy_extensions import TypedDict +from typing import TypeVar, TypedDict, Generic, NamedTuple, NewType, Union, Any, cast, overload T = TypeVar('T', bound=int) class C(Generic[T]): pass class C2(Generic[T]): pass -A = C[str] # E: Type argument "str" of "C" must be a subtype of "int" \ - # E: Value of type variable "T" of "C" cannot be "str" +A = C[str] # E: Value of type variable "T" of "C" cannot be "str" \ + # E: Type argument "str" of "C" must be a subtype of "int" B = Union[C[str], int] # E: Type argument "str" of "C" must be a subtype of "int" S = TypeVar('S', bound=C[str]) # E: Type argument "str" of "C" must be a subtype of "int" U = TypeVar('U', C[str], str) # E: Type argument "str" of "C" must be a subtype of "int" @@ -1706,7 +1707,8 @@ def g(x: int) -> int: ... def g(x: Union[C[str], int]) -> int: # E: Type argument "str" of "C" must be a subtype of "int" y: C[object] # E: Type argument "object" of "C" must be a subtype of "int" return 0 -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNewAnalyzerTypeArgBoundCheckWithStrictOptional] # flags: --config-file tmp/mypy.ini @@ -1838,7 +1840,7 @@ x.extend(y) import b [file a.py] from b import x -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" [file b.py] import a x = (1, 2) @@ -1848,7 +1850,7 @@ x = (1, 2) import a [file a.py] from b import x -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" [file b.py] import a x = (1, 2) @@ -1908,9 +1910,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 @@ -1972,7 +1974,7 @@ S = TypeVar('S', bound='Tuple[G[A], ...]') class GG(Generic[S]): pass -g: GG[Tuple[G[B], G[C]]] # E: Type argument "Tuple[G[B], G[C]]" of "GG" must be a subtype of "Tuple[G[A], ...]" \ +g: GG[Tuple[G[B], G[C]]] # E: Type argument "tuple[G[B], G[C]]" of "GG" must be a subtype of "tuple[G[A], ...]" \ # E: Type argument "B" of "G" must be a subtype of "A" \ # E: Type argument "C" of "G" must be a subtype of "A" @@ -2069,6 +2071,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 @@ -2173,7 +2176,7 @@ def test() -> None: reveal_type(y.x) # N: Revealed type is "builtins.int" reveal_type(y[0]) # N: Revealed type is "builtins.int" x: A - reveal_type(x) # N: Revealed type is "__main__.G@7[Tuple[builtins.int, fallback=__main__.C@5]]" + reveal_type(x) # N: Revealed type is "__main__.G@7[tuple[builtins.int, fallback=__main__.C@5]]" [builtins fixtures/list.pyi] [case testNewAnalyzerDuplicateTypeVar] @@ -2311,7 +2314,7 @@ from typing import cast, NamedTuple x = cast('C', None) -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.C]" reveal_type(x.x) # N: Revealed type is "builtins.int" C = NamedTuple('C', [('x', int)]) @@ -2525,8 +2528,7 @@ tmp/unittest/suite.pyi:6: error: Name "Iterable" is not defined tmp/unittest/suite.pyi:6: note: Did you forget to import it from "typing"? (Suggestion: "from typing import Iterable") [case testNewAnalyzerNewTypeSpecialCase] -from typing import NewType -from typing_extensions import Final, Literal +from typing import Final, Literal, NewType X = NewType('X', int) @@ -2742,13 +2744,11 @@ T = TypeVar('T') class C(Generic[T]): pass -# TODO: Error message is confusing + C = C[int] # E: Cannot assign to a type \ - # E: Incompatible types in assignment (expression has type "Type[C[Any]]", variable has type "Type[C[Any]]") + # E: Incompatible types in assignment (expression has type "type[C[int]]", variable has type "type[C[T]]") x: C reveal_type(x) # N: Revealed type is "__main__.C[Any]" -[out] -[out2] [case testNewAnalyzerClassVariableOrdering] def foo(x: str) -> None: pass @@ -2776,7 +2776,7 @@ class C: reveal_type(C.A) # N: Revealed type is "def () -> a.A" [case testNewAnalyzerFinalLiteralInferredAsLiteralWithDeferral] -from typing_extensions import Final, Literal +from typing import Final, Literal defer: Yes @@ -2805,6 +2805,7 @@ def get() -> int: ... import typing t = typing.typevar('t') # E: Module has no attribute "typevar" [builtins fixtures/module.pyi] +[typing fixtures/typing-full.pyi] [case testNewAnalyzerImportFromTopLevelFunction] import a.b # This works at runtime @@ -3139,6 +3140,22 @@ from typing import Final x: Final = 0 x = x # E: Cannot assign to final name "x" +[case testNewAnalyzerIdentityAssignmentClassImplicit] +class C: ... +class A: + C = C[str] # E: "C" expects no type arguments, but 1 given +[builtins fixtures/tuple.pyi] + +[case testNewAnalyzerIdentityAssignmentClassExplicit] +from typing_extensions import TypeAlias + +class A: + C: TypeAlias = C +class C: ... +c: A.C +reveal_type(c) # N: Revealed type is "__main__.C" +[builtins fixtures/tuple.pyi] + [case testNewAnalyzerClassPropertiesInAllScopes] from abc import abstractmethod, ABCMeta @@ -3215,15 +3232,15 @@ class User: self.name = name # E: Cannot assign to a method [case testNewAnalyzerMemberNameMatchesTypedDict] -from typing import Union, Any -from typing_extensions import TypedDict +from typing import TypedDict, Union, Any class T(TypedDict): b: b.T class b: T: Union[Any] -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNewAnalyzerMemberNameMatchesNamedTuple] from typing import Union, Any, NamedTuple @@ -3240,3 +3257,21 @@ class b: x = x[1] # E: Cannot resolve name "x" (possible cyclic definition) y = 1[y] # E: Value of type "int" is not indexable \ # E: Cannot determine type of "y" + +[case testForwardBaseDeferAttr] +from typing import Optional, Callable, TypeVar + +class C(B): + def a(self) -> None: + reveal_type(self._foo) # N: Revealed type is "Union[builtins.int, None]" + self._foo = defer() + +class B: + def __init__(self) -> None: + self._foo: Optional[int] = None + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], T]: ... + +@deco +def defer() -> int: ... diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test index 3ed4c6d3d8e2d..df36a1ce4dd22 100644 --- a/test-data/unit/check-newsyntax.test +++ b/test-data/unit/check-newsyntax.test @@ -1,5 +1,5 @@ [case testNewSyntaxSyntaxError] -x: int: int # E: invalid syntax +x: int: int # E: Invalid syntax [out] [case testNewSyntaxBasics] @@ -21,7 +21,7 @@ from typing import Dict, Any d: Dict[int, str] = {} d[42] = 'ab' d[42] = 42 # E: Incompatible types in assignment (expression has type "int", target has type "str") -d['ab'] = 'ab' # E: Invalid index type "str" for "Dict[int, str]"; expected type "int" +d['ab'] = 'ab' # E: Invalid index type "str" for "dict[int, str]"; expected type "int" [builtins fixtures/dict.pyi] [out] @@ -126,4 +126,4 @@ reveal_type(f'{1}') # N: Revealed type is "builtins.str" # flags: --python-version 3.99 x *** x this is what future python looks like public static void main String[] args await goto exit [out] -main:2: error: invalid syntax; you likely need to run mypy using Python 3.99 or newer +main:2: error: Invalid syntax; you likely need to run mypy using Python 3.99 or newer diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test index a0a30079f0625..f7219e7212224 100644 --- a/test-data/unit/check-newtype.test +++ b/test-data/unit/check-newtype.test @@ -44,7 +44,7 @@ main:12: error: Argument 1 to "TcpPacketId" has incompatible type "int"; expecte from typing import NewType, Tuple TwoTuple = NewType('TwoTuple', Tuple[int, str]) a = TwoTuple((3, "a")) -b = TwoTuple(("a", 3)) # E: Argument 1 to "TwoTuple" has incompatible type "Tuple[str, int]"; expected "Tuple[int, str]" +b = TwoTuple(("a", 3)) # E: Argument 1 to "TwoTuple" has incompatible type "tuple[str, int]"; expected "tuple[int, str]" reveal_type(a[0]) # N: Revealed type is "builtins.int" reveal_type(a[1]) # N: Revealed type is "builtins.str" @@ -291,7 +291,7 @@ Foo = NewType('Foo', Union[int, float]) # E: Argument 2 to NewType(...) must be [case testNewTypeWithTypeTypeFails] from typing import NewType, Type -Foo = NewType('Foo', Type[int]) # E: Argument 2 to NewType(...) must be subclassable (got "Type[int]") +Foo = NewType('Foo', Type[int]) # E: Argument 2 to NewType(...) must be subclassable (got "type[int]") a = Foo(type(3)) [builtins fixtures/args.pyi] [out] diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 683ce04469152..679906b0e00ed 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -201,7 +201,7 @@ x.append(1) # E: Argument 1 to "append" of "list" has incompatible type "int"; [case testInferNonOptionalListType] x = [] x.append(1) -x() # E: "List[int]" not callable +x() # E: "list[int]" not callable [builtins fixtures/list.pyi] [case testInferOptionalDictKeyValueTypes] @@ -209,13 +209,13 @@ x = {None: None} x["bar"] = 1 [builtins fixtures/dict.pyi] [out] -main:2: error: Invalid index type "str" for "Dict[None, None]"; expected type "None" +main:2: error: Invalid index type "str" for "dict[None, None]"; expected type "None" main:2: error: Incompatible types in assignment (expression has type "int", target has type "None") [case testInferNonOptionalDictType] x = {} x["bar"] = 1 -x() # E: "Dict[str, int]" not callable +x() # E: "dict[str, int]" not callable [builtins fixtures/dict.pyi] [case testNoneClassVariable] @@ -321,10 +321,13 @@ def f() -> Generator[None, None, None]: [out] [case testNoneAndStringIsNone] -a = None +a: None = None b = "foo" reveal_type(a and b) # N: Revealed type is "None" +c = None +reveal_type(c and b) # N: Revealed type is "None" + [case testNoneMatchesObjectInOverload] import a a.f(None) @@ -581,7 +584,7 @@ x is not None and x + '42' # E: Unsupported operand types for + ("int" and "str [case testInvalidBooleanBranchIgnored] from typing import Optional -x = None +x: None = None x is not None and x + 42 [builtins fixtures/isinstance.pyi] @@ -725,7 +728,6 @@ def g(x: Optional[int]) -> int: reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" return x - [builtins fixtures/bool.pyi] [case testOptionalAssignAny2] @@ -738,12 +740,11 @@ def g(x: Optional[int]) -> int: reveal_type(x) # N: Revealed type is "None" x = 1 reveal_type(x) # N: Revealed type is "builtins.int" - # Since we've assigned to x, the special case None behavior shouldn't happen + # Same as above, even after we've assigned to x x = f() - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" - return x # E: Incompatible return value type (got "Optional[int]", expected "int") - + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + return x [builtins fixtures/bool.pyi] [case testOptionalAssignAny3] @@ -755,11 +756,9 @@ def g(x: Optional[int]) -> int: if x is not None: return x reveal_type(x) # N: Revealed type is "None" - if 1: - x = f() - reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" - return x - + x = f() + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + return x [builtins fixtures/bool.pyi] [case testStrictOptionalCovarianceCrossModule] @@ -782,8 +781,8 @@ asdf(x) \[mypy-a] strict_optional = False [out] -main:4: error: Argument 1 to "asdf" has incompatible type "List[str]"; expected "List[Optional[str]]" -main:4: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +main:4: error: Argument 1 to "asdf" has incompatible type "list[str]"; expected "list[Optional[str]]" +main:4: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance main:4: note: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] @@ -979,7 +978,7 @@ def f23(b: bool) -> None: def f1(b: bool) -> None: if b: - x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + x = [] # E: Need type annotation for "x" (hint: "x: list[] = ...") else: x = None diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index e414c1c9b0b6f..1830a0c5ce3c0 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -231,9 +231,51 @@ def f(x: 'A') -> Any: # E: Overloaded function implementation does not accept al reveal_type(f(A())) # N: Revealed type is "__main__.B" reveal_type(f(B())) # N: Revealed type is "__main__.A" - [builtins fixtures/isinstance.pyi] +[case testTypeCheckOverloadImplOverlapVarArgsAndKwargs] +from __future__ import annotations +from typing import overload + +@overload +def foo(x: int) -> None: ... +@overload +def foo(a: str, /) -> None: ... + +def foo(*args: int | str, **kw: int) -> None: + pass +[builtins fixtures/tuple.pyi] + +[case testTypeCheckOverloadImplOverlapVarArgsAndKwargsUnion] +from __future__ import annotations +from typing import overload + +class Foo: ... + +@overload +def foo(x: int) -> None: ... +@overload +def foo(*, x: Foo) -> None: ... +@overload +def foo(a: str, /) -> None: ... + +def foo(*args: int | str, **kw: int | Foo) -> None: + pass +[builtins fixtures/tuple.pyi] + + +[case testTypeCheckOverloadImplOverlapVarArgsAndKwargsNever] +from __future__ import annotations +from typing import overload + +@overload # E: Single overload definition, multiple required +def foo(x: int) -> None: ... + +def foo(*args: int, **kw: str) -> None: # E: Overloaded function implementation does not accept all possible arguments of signature 1 + pass +[builtins fixtures/tuple.pyi] + + [case testTypeCheckOverloadWithImplTooSpecificRetType] from typing import overload, Any @@ -620,7 +662,7 @@ t: type a: A if int(): - a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") + a = A # E: Incompatible types in assignment (expression has type "type[A]", variable has type "A") t = A class A: @@ -811,7 +853,7 @@ n = 1 m = 1 n = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") m = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -f(list_object) # E: Argument 1 to "f" has incompatible type "List[object]"; expected "List[int]" +f(list_object) # E: Argument 1 to "f" has incompatible type "list[object]"; expected "list[int]" [builtins fixtures/list.pyi] [case testOverlappingOverloadSignatures] @@ -1147,7 +1189,7 @@ def f(x: str) -> None: pass f(1.1) f('') f(1) -f(()) # E: No overload variant of "f" matches argument type "Tuple[()]" \ +f(()) # E: No overload variant of "f" matches argument type "tuple[()]" \ # N: Possible overload variants: \ # N: def f(x: float) -> None \ # N: def f(x: str) -> None @@ -1216,13 +1258,13 @@ from typing import overload def f(x: int, y: str) -> int: pass @overload def f(*x: str) -> str: pass -f(*(1,))() # E: No overload variant of "f" matches argument type "Tuple[int]" \ +f(*(1,))() # E: No overload variant of "f" matches argument type "tuple[int]" \ # N: Possible overload variants: \ # N: def f(x: int, y: str) -> int \ # N: def f(*x: str) -> str f(*('',))() # E: "str" not callable f(*(1, ''))() # E: "int" not callable -f(*(1, '', 1))() # E: No overload variant of "f" matches argument type "Tuple[int, str, int]" \ +f(*(1, '', 1))() # E: No overload variant of "f" matches argument type "tuple[int, str, int]" \ # N: Possible overload variants: \ # N: def f(x: int, y: str) -> int \ # N: def f(*x: str) -> str @@ -1239,7 +1281,7 @@ def f(x: int, y: List[int] = None) -> int: pass def f(x: int, y: List[str] = None) -> int: pass f(y=[1], x=0)() # E: "int" not callable f(y=[''], x=0)() # E: "int" not callable -a = f(y=[['']], x=0) # E: List item 0 has incompatible type "List[str]"; expected "int" +a = f(y=[['']], x=0) # E: List item 0 has incompatible type "list[str]"; expected "int" reveal_type(a) # N: Revealed type is "builtins.int" [builtins fixtures/list.pyi] @@ -1299,7 +1341,7 @@ def g(x: U, y: V) -> None: f(y) # E: No overload variant of "f" matches argument type "V" \ # N: Possible overload variants: \ # N: def [T: str] f(x: T) -> T \ - # N: def [T: str] f(x: List[T]) -> None + # N: def [T: str] f(x: list[T]) -> None a = f([x]) reveal_type(a) # N: Revealed type is "None" f([y]) # E: Value of type variable "T" of "f" cannot be "V" @@ -1414,11 +1456,11 @@ main:17: note: Revealed type is "builtins.int" main:18: note: Revealed type is "builtins.str" main:19: note: Revealed type is "Any" main:20: note: Revealed type is "Union[builtins.int, builtins.str]" -main:21: error: Argument 1 to "foo" has incompatible type "List[bool]"; expected "List[int]" -main:21: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +main:21: error: Argument 1 to "foo" has incompatible type "list[bool]"; expected "list[int]" +main:21: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance main:21: note: Consider using "Sequence" instead, which is covariant -main:22: error: Argument 1 to "foo" has incompatible type "List[object]"; expected "List[int]" -main:23: error: Argument 1 to "foo" has incompatible type "List[Union[int, str]]"; expected "List[int]" +main:22: error: Argument 1 to "foo" has incompatible type "list[object]"; expected "list[int]" +main:23: error: Argument 1 to "foo" has incompatible type "list[Union[int, str]]"; expected "list[int]" [case testOverloadAgainstEmptyCollections] from typing import overload, List @@ -1482,7 +1524,7 @@ class A(Generic[T]): b = A() # type: A[Tuple[int, int]] b.f((0, 0)) -b.f((0, '')) # E: Argument 1 to "f" of "A" has incompatible type "Tuple[int, str]"; expected "Tuple[int, int]" +b.f((0, '')) # E: Argument 1 to "f" of "A" has incompatible type "tuple[int, str]"; expected "tuple[int, int]" [builtins fixtures/tuple.pyi] [case testSingleOverloadStub] @@ -1554,14 +1596,14 @@ def f(x: int, y: Tuple[str, ...]) -> None: pass @overload def f(x: int, y: str) -> None: pass f(1, ('2', '3')) -f(1, (2, '3')) # E: Argument 2 to "f" has incompatible type "Tuple[int, str]"; expected "Tuple[str, ...]" +f(1, (2, '3')) # E: Argument 2 to "f" has incompatible type "tuple[int, str]"; expected "tuple[str, ...]" f(1, ('2',)) f(1, '2') -f(1, (2, 3)) # E: Argument 2 to "f" has incompatible type "Tuple[int, int]"; expected "Tuple[str, ...]" +f(1, (2, 3)) # E: Argument 2 to "f" has incompatible type "tuple[int, int]"; expected "tuple[str, ...]" x = ('2', '3') # type: Tuple[str, ...] f(1, x) y = (2, 3) # type: Tuple[int, ...] -f(1, y) # E: Argument 2 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[str, ...]" +f(1, y) # E: Argument 2 to "f" has incompatible type "tuple[int, ...]"; expected "tuple[str, ...]" [builtins fixtures/tuple.pyi] [case testCallableSpecificOverload] @@ -2539,7 +2581,7 @@ x: List[int] reveal_type(foo(*x)) # N: Revealed type is "__main__.C" y: List[str] -foo(*y) # E: No overload variant of "foo" matches argument type "List[str]" \ +foo(*y) # E: No overload variant of "foo" matches argument type "list[str]" \ # N: Possible overload variants: \ # N: def foo(x: int) -> A \ # N: def foo(x: int, y: int) -> B \ @@ -2626,8 +2668,8 @@ def f(*xs: int) -> Tuple[int, ...]: ... def f(*args): pass i: int -reveal_type(f(i)) # N: Revealed type is "Tuple[builtins.int]" -reveal_type(f(i, i)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(i)) # N: Revealed type is "tuple[builtins.int]" +reveal_type(f(i, i)) # N: Revealed type is "tuple[builtins.int, builtins.int]" reveal_type(f(i, i, i)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" reveal_type(f(*[])) # N: Revealed type is "builtins.tuple[builtins.int, ...]" @@ -2648,8 +2690,8 @@ def f(*args): pass i: int reveal_type(f(*())) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(f(*(i,))) # N: Revealed type is "Tuple[builtins.int]" -reveal_type(f(*(i, i))) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(*(i,))) # N: Revealed type is "tuple[builtins.int]" +reveal_type(f(*(i, i))) # N: Revealed type is "tuple[builtins.int, builtins.int]" reveal_type(f(*(i, i, i))) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] @@ -2668,8 +2710,8 @@ C = NamedTuple('C', [('a', int), ('b', int), ('c', int)]) a: A b: B c: C -reveal_type(f(*a)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" -reveal_type(f(*b)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(*a)) # N: Revealed type is "tuple[builtins.int, builtins.int]" +reveal_type(f(*b)) # N: Revealed type is "tuple[builtins.int, builtins.int]" reveal_type(f(*c)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] @@ -2691,8 +2733,7 @@ reveal_type(f(**{'a': 4, 'b': 4, 'c': 4})) # N: Revealed type is "builtins.tup [builtins fixtures/dict.pyi] [case testOverloadKwargsSelectionWithTypedDict] -from typing import overload, Tuple -from typing_extensions import TypedDict +from typing import overload, Tuple, TypedDict @overload def f(*, x: int) -> Tuple[int]: ... @overload @@ -2709,10 +2750,11 @@ a: A b: B c: C -reveal_type(f(**a)) # N: Revealed type is "Tuple[builtins.int]" -reveal_type(f(**b)) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(f(**a)) # N: Revealed type is "tuple[builtins.int]" +reveal_type(f(**b)) # N: Revealed type is "tuple[builtins.int, builtins.int]" reveal_type(f(**c)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadVarargsAndKwargsSelection] from typing import overload, Any, Tuple, Dict @@ -2913,8 +2955,7 @@ class Wrapper(Generic[T]): [builtins fixtures/list.pyi] [case testOverloadTypedDictDifferentRequiredKeysMeansDictsAreDisjoint] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int, 'y': int}) B = TypedDict('B', {'x': int, 'y': str}) @@ -2925,10 +2966,10 @@ def f(x: A) -> int: ... def f(x: B) -> str: ... def f(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTypedDictPartiallyOverlappingRequiredKeys] -from typing import overload, Union -from mypy_extensions import TypedDict +from typing import overload, TypedDict, Union A = TypedDict('A', {'x': int, 'y': Union[int, str]}) B = TypedDict('B', {'x': int, 'y': Union[str, float]}) @@ -2945,10 +2986,10 @@ def g(x: A) -> int: ... def g(x: B) -> object: ... def g(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTypedDictFullyNonTotalDictsAreAlwaysPartiallyOverlapping] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int, 'y': str}, total=False) B = TypedDict('B', {'a': bool}, total=False) @@ -2966,10 +3007,10 @@ def g(x: A) -> int: ... # E: Overloaded function signatures 1 and 2 overlap wit def g(x: C) -> str: ... def g(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTotalAndNonTotalTypedDictsCanPartiallyOverlap] -from typing import overload, Union -from mypy_extensions import TypedDict +from typing import overload, TypedDict, Union A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'x': Union[int, str], 'y': str, 'z': int}, total=False) @@ -2987,10 +3028,10 @@ def f2(x: A) -> str: ... def f2(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedTypedDictsWithSomeOptionalKeysArePartiallyOverlapping] -from typing import overload, Union -from mypy_extensions import TypedDict +from typing import overload, TypedDict, Union class A(TypedDict): x: int @@ -3009,6 +3050,7 @@ def f(x: C) -> str: ... def f(x): pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testOverloadedPartiallyOverlappingInheritedTypes1] from typing import overload, List, Union, TypeVar, Generic @@ -3370,10 +3412,10 @@ def wrapper() -> None: obj2: Union[W1[A], W2[B]] reveal_type(foo(obj2)) # N: Revealed type is "Union[__main__.A, __main__.B]" - bar(obj2) # E: Cannot infer type argument 1 of "bar" + bar(obj2) # E: Cannot infer value of type parameter "T" of "bar" b1_overload: A = foo(obj2) # E: Incompatible types in assignment (expression has type "Union[A, B]", variable has type "A") - b1_union: A = bar(obj2) # E: Cannot infer type argument 1 of "bar" + b1_union: A = bar(obj2) # E: Cannot infer value of type parameter "T" of "bar" [case testOverloadingInferUnionReturnWithObjectTypevarReturn] from typing import overload, Union, TypeVar, Generic @@ -3496,13 +3538,13 @@ def t_is_same_bound(arg1: T1, arg2: S) -> Tuple[T1, S]: # The arguments in the tuple are swapped x3: Union[List[S], List[Tuple[S, T1]]] y3: S - Dummy[T1]().foo(x3, y3) # E: Cannot infer type argument 1 of "foo" of "Dummy" \ - # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[List[S], List[Tuple[S, T1]]]"; expected "List[Tuple[T1, Any]]" + Dummy[T1]().foo(x3, y3) # E: Cannot infer value of type parameter "S" of "foo" of "Dummy" \ + # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[list[S], list[tuple[S, T1]]]"; expected "list[tuple[T1, Any]]" x4: Union[List[int], List[Tuple[C, int]]] y4: int reveal_type(Dummy[C]().foo(x4, y4)) # N: Revealed type is "Union[builtins.int, __main__.C]" - Dummy[A]().foo(x4, y4) # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[List[int], List[Tuple[C, int]]]"; expected "List[Tuple[A, int]]" + Dummy[A]().foo(x4, y4) # E: Argument 1 to "foo" of "Dummy" has incompatible type "Union[list[int], list[tuple[C, int]]]"; expected "list[tuple[A, int]]" return arg1, arg2 @@ -4264,7 +4306,7 @@ class Wrapper: @classmethod # E: Overloaded function implementation cannot produce return type of signature 1 def foo(cls, x: Union[int, str]) -> str: - reveal_type(cls) # N: Revealed type is "Type[__main__.Wrapper]" + reveal_type(cls) # N: Revealed type is "type[__main__.Wrapper]" reveal_type(cls.other()) # N: Revealed type is "builtins.str" return "..." @@ -4589,10 +4631,10 @@ class Child(Parent): def child_only(self) -> int: pass x: Union[int, str] -reveal_type(Parent.foo(3)) # N: Revealed type is "Type[__main__.Parent]" -reveal_type(Child.foo(3)) # N: Revealed type is "Type[__main__.Child]" +reveal_type(Parent.foo(3)) # N: Revealed type is "type[__main__.Parent]" +reveal_type(Child.foo(3)) # N: Revealed type is "type[__main__.Child]" reveal_type(Child.foo("...")) # N: Revealed type is "builtins.str" -reveal_type(Child.foo(x)) # N: Revealed type is "Union[Type[__main__.Child], builtins.str]" +reveal_type(Child.foo(x)) # N: Revealed type is "Union[type[__main__.Child], builtins.str]" reveal_type(Child.foo(3)().child_only()) # N: Revealed type is "builtins.int" [builtins fixtures/classmethod.pyi] @@ -5079,7 +5121,7 @@ a = multiple_plausible(Other()) # E: No overload variant of "multiple_plausible # N: def multiple_plausible(x: str) -> str reveal_type(a) # N: Revealed type is "Any" -b = single_plausible(Other) # E: Argument 1 to "single_plausible" has incompatible type "Type[Other]"; expected "Type[int]" +b = single_plausible(Other) # E: Argument 1 to "single_plausible" has incompatible type "type[Other]"; expected "type[int]" reveal_type(b) # N: Revealed type is "builtins.int" c = single_plausible([Other()]) # E: List item 0 has incompatible type "Other"; expected "str" @@ -5267,8 +5309,7 @@ tmp/lib.pyi:3: error: Name "overload" is not defined main:3: note: Revealed type is "Any" [case testLiteralSubtypeOverlap] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload class MyInt(int): ... @@ -6311,6 +6352,40 @@ reveal_type(f12(A())) # N: Revealed type is "__main__.A" [typing fixtures/typing-medium.pyi] +[case testAdjacentConditionalOverloads] +# flags: --always-true true_alias +from typing import overload + +true_alias = True + +if true_alias: + @overload + def ham(v: str) -> list[str]: ... + + @overload + def ham(v: int) -> list[int]: ... + +def ham(v: "int | str") -> "list[str] | list[int]": + return [] + +if true_alias: + @overload + def spam(v: str) -> str: ... + + @overload + def spam(v: int) -> int: ... + +def spam(v: "int | str") -> "str | int": + return "" + +reveal_type(ham) # N: Revealed type is "Overload(def (v: builtins.str) -> builtins.list[builtins.str], def (v: builtins.int) -> builtins.list[builtins.int])" +reveal_type(spam) # N: Revealed type is "Overload(def (v: builtins.str) -> builtins.str, def (v: builtins.int) -> builtins.int)" + +reveal_type(ham("")) # N: Revealed type is "builtins.list[builtins.str]" +reveal_type(ham(0)) # N: Revealed type is "builtins.list[builtins.int]" +reveal_type(spam("")) # N: Revealed type is "builtins.str" +reveal_type(spam(0)) # N: Revealed type is "builtins.int" + [case testOverloadIfUnconditionalFuncDef] # flags: --always-true True --always-false False from typing import overload @@ -6463,7 +6538,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 +6555,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 \ @@ -6604,7 +6679,6 @@ reveal_type(Snafu.snafu('123')) # N: Revealed type is "builtins.str" [builtins fixtures/staticmethod.pyi] [case testOverloadedWithInternalTypeVars] -# flags: --new-type-inference import m [file m.pyi] @@ -6768,3 +6842,26 @@ class D(Generic[T]): 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" + +[case testMultiAssignFromUnionInOverloadCached] +from typing import Iterable, overload, Union, Optional + +@overload +def always_bytes(str_or_bytes: None) -> None: ... +@overload +def always_bytes(str_or_bytes: Union[str, bytes]) -> bytes: ... +def always_bytes(str_or_bytes: Union[None, str, bytes]) -> Optional[bytes]: + pass + +class Headers: + def __init__(self, iter: Iterable[tuple[bytes, bytes]]) -> None: ... + +headers: Union[Headers, dict[Union[str, bytes], Union[str, bytes]], Iterable[tuple[bytes, bytes]]] + +if isinstance(headers, dict): + headers = Headers( + (always_bytes(k), always_bytes(v)) for k, v in headers.items() + ) + +reveal_type(headers) # N: Revealed type is "Union[__main__.Headers, typing.Iterable[tuple[builtins.bytes, builtins.bytes]]]" +[builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 38fb62fe78e06..bffd34782f517 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -14,7 +14,7 @@ P5 = ParamSpec("P5", covariant=True, bound=int) # E: The variance and bound arg [builtins fixtures/paramspec.pyi] [case testParamSpecLocations] -from typing import Callable, List +from typing import Any, Callable, List, Type from typing_extensions import ParamSpec, Concatenate P = ParamSpec('P') @@ -36,6 +36,25 @@ def foo5(x: Callable[[int, str], P]) -> None: ... # E: Invalid location for Par def foo6(x: Callable[[P], int]) -> None: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" + +def foo7( + *args: P.args, **kwargs: P.kwargs # E: ParamSpec "P" is unbound +) -> Callable[[Callable[P, T]], Type[T]]: + ... + +def wrapper(f: Callable[P, int]) -> None: + def inner(*args: P.args, **kwargs: P.kwargs) -> None: ... # OK + + def extra_args_left(x: int, *args: P.args, **kwargs: P.kwargs) -> None: ... # OK + def extra_args_between(*args: P.args, x: int, **kwargs: P.kwargs) -> None: ... # E: Arguments not allowed after ParamSpec.args + + def swapped(*args: P.kwargs, **kwargs: P.args) -> None: ... # E: Use "P.args" for variadic "*" parameter \ + # E: Use "P.kwargs" for variadic "**" parameter + def bad_kwargs(*args: P.args, **kwargs: P.args) -> None: ... # E: Use "P.kwargs" for variadic "**" parameter + def bad_args(*args: P.kwargs, **kwargs: P.kwargs) -> None: ... # E: Use "P.args" for variadic "*" parameter + + def misplaced(x: P.args) -> None: ... # E: ParamSpec components are not allowed here + def bad_kwargs_any(*args: P.args, **kwargs: Any) -> None: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [builtins fixtures/paramspec.pyi] [case testParamSpecImports] @@ -324,8 +343,9 @@ class C(Generic[P]): a = kwargs args = kwargs # E: Incompatible types in assignment (expression has type "P.kwargs", variable has type "P.args") kwargs = args # E: Incompatible types in assignment (expression has type "P.args", variable has type "P.kwargs") - args = a - kwargs = a + a1: Any + args = a1 + kwargs = a1 [builtins fixtures/dict.pyi] [case testParamSpecSubtypeChecking2] @@ -423,7 +443,7 @@ reveal_type(register(lambda: f(1))) # N: Revealed type is "def ()" reveal_type(register(lambda x: f(x), x=1)) # N: Revealed type is "def (x: Literal[1]?)" register(lambda x: f(x)) # E: Cannot infer type of lambda \ # E: Argument 1 to "register" has incompatible type "Callable[[Any], None]"; expected "Callable[[], None]" -register(lambda x: f(x), y=1) # E: Argument 1 to "register" has incompatible type "Callable[[Arg(int, 'x')], None]"; expected "Callable[[Arg(int, 'y')], None]" +register(lambda x: f(x), y=1) # E: Argument 1 to "register" has incompatible type "def (x: int) -> None"; expected "def (y: int) -> None" reveal_type(register(lambda x: f(x), 1)) # N: Revealed type is "def (Literal[1]?)" reveal_type(register(lambda x, y: g(x, y), 1, "a")) # N: Revealed type is "def (Literal[1]?, Literal['a']?)" reveal_type(register(lambda x, y: g(x, y), 1, y="a")) # N: Revealed type is "def (Literal[1]?, y: Literal['a']?)" @@ -603,10 +623,10 @@ def expects_int_first(x: Callable[Concatenate[int, P], int]) -> None: ... # N: This is likely because "one" has named arguments: "x". Consider marking them positional-only def one(x: str) -> int: ... -@expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[NamedArg(int, 'x')], int]"; expected "Callable[[int, NamedArg(int, 'x')], int]" +@expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "def two(*, x: int) -> int"; expected "def (int, /, *, x: int) -> int" def two(*, x: int) -> int: ... -@expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[KwArg(int)], int]"; expected "Callable[[int, KwArg(int)], int]" +@expects_int_first # E: Argument 1 to "expects_int_first" has incompatible type "def three(**kwargs: int) -> int"; expected "def (int, /, **kwargs: int) -> int" def three(**kwargs: int) -> int: ... @expects_int_first # Accepted @@ -901,8 +921,8 @@ class A: def func(self, action: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... -reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`6, *_P.args, **_P.kwargs) -> _R`6" -reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`10, *_P.args, **_P.kwargs) -> _R`10" +reveal_type(A.func) # N: Revealed type is "def [_P, _R] (self: __main__.A, action: def (*_P.args, **_P.kwargs) -> _R`4, *_P.args, **_P.kwargs) -> _R`4" +reveal_type(A().func) # N: Revealed type is "def [_P, _R] (action: def (*_P.args, **_P.kwargs) -> _R`8, *_P.args, **_P.kwargs) -> _R`8" def f(x: int) -> int: ... @@ -933,8 +953,8 @@ class A: def func(self, action: Job[_P, None]) -> Job[_P, None]: ... -reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`4, None]) -> __main__.Job[_P`4, None]" -reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`6, None]) -> __main__.Job[_P`6, None]" +reveal_type(A.func) # N: Revealed type is "def [_P] (self: __main__.A, action: __main__.Job[_P`3, None]) -> __main__.Job[_P`3, None]" +reveal_type(A().func) # N: Revealed type is "def [_P] (action: __main__.Job[_P`5, None]) -> __main__.Job[_P`5, None]" reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: Any], None]" def f(x: int, y: int) -> None: ... @@ -1101,7 +1121,6 @@ reveal_type(jf(1)) # N: Revealed type is "None" [builtins fixtures/paramspec.pyi] [case testGenericsInInferredParamspecReturn] -# flags: --new-type-inference from typing import Callable, TypeVar, Generic from typing_extensions import ParamSpec @@ -1193,7 +1212,7 @@ def func(callback: Callable[P, str]) -> Callable[P, str]: return inner [builtins fixtures/paramspec.pyi] -[case testParamSpecArgsAndKwargsMissmatch] +[case testParamSpecArgsAndKwargsMismatch] from typing import Callable from typing_extensions import ParamSpec @@ -1256,15 +1275,15 @@ def c3(f: Callable[P, int], *args, **kwargs) -> int: ... # It is ok to define, def c4(f: Callable[P, int], *args: int, **kwargs: str) -> int: # but not ok to call: - f(*args, **kwargs) # E: Argument 1 has incompatible type "*Tuple[int, ...]"; expected "P.args" \ - # E: Argument 2 has incompatible type "**Dict[str, str]"; expected "P.kwargs" + f(*args, **kwargs) # E: Argument 1 has incompatible type "*tuple[int, ...]"; expected "P.args" \ + # E: Argument 2 has incompatible type "**dict[str, str]"; expected "P.kwargs" return 1 def f1(f: Callable[P, int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f2(f: Callable[P, int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f3(f: Callable[P, int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f4(f: Callable[P, int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" -def f5(f: Callable[P, int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" +def f5(f: Callable[P, int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: Arguments not allowed after ParamSpec.args # Error message test: P1 = ParamSpec('P1') @@ -1286,15 +1305,15 @@ def c3(f: Callable[Concatenate[int, P], int], *args, **kwargs) -> int: ... # It is ok to define, def c4(f: Callable[Concatenate[int, P], int], *args: int, **kwargs: str) -> int: # but not ok to call: - f(1, *args, **kwargs) # E: Argument 2 has incompatible type "*Tuple[int, ...]"; expected "P.args" \ - # E: Argument 3 has incompatible type "**Dict[str, str]"; expected "P.kwargs" + f(1, *args, **kwargs) # E: Argument 2 has incompatible type "*tuple[int, ...]"; expected "P.args" \ + # E: Argument 3 has incompatible type "**dict[str, str]"; expected "P.kwargs" return 1 def f1(f: Callable[Concatenate[int, P], int], *args, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f2(f: Callable[Concatenate[int, P], int], *args: P.args, **kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f3(f: Callable[Concatenate[int, P], int], *args: P.args) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" def f4(f: Callable[Concatenate[int, P], int], **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" -def f5(f: Callable[Concatenate[int, P], int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" +def f5(f: Callable[Concatenate[int, P], int], *args: P.args, extra_keyword_arg: int, **kwargs: P.kwargs) -> int: ... # E: Arguments not allowed after ParamSpec.args [builtins fixtures/paramspec.pyi] @@ -1326,22 +1345,28 @@ from typing import Callable, ParamSpec P1 = ParamSpec('P1') P2 = ParamSpec('P2') -def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" \ + # E: ParamSpec "P2" is unbound -def f1(*args: P1.args): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f2(**kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f3(*args: P1.args, **kwargs: int): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f4(*args: int, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f1(*args: P1.args): ... # E: ParamSpec "P1" is unbound +def f2(**kwargs: P1.kwargs): ... # E: ParamSpec "P1" is unbound +def f3(*args: P1.args, **kwargs: int): ... # E: ParamSpec "P1" is unbound +def f4(*args: int, **kwargs: P1.kwargs): ... # E: ParamSpec "P1" is unbound # Error message is based on the `args` definition: -def f5(*args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P2.args" and "**kwargs" typed as "P2.kwargs" -def f6(*args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f5(*args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec "P2" is unbound \ + # E: ParamSpec "P1" is unbound +def f6(*args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P1" is unbound \ + # E: ParamSpec "P2" is unbound # Multiple `ParamSpec` variables can be found, they should not affect error message: P3 = ParamSpec('P3') -def f7(first: Callable[P3, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" -def f8(first: Callable[P3, int], *args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec must have "*args" typed as "P2.args" and "**kwargs" typed as "P2.kwargs" +def f7(first: Callable[P3, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P1" is unbound \ + # E: ParamSpec "P2" is unbound +def f8(first: Callable[P3, int], *args: P2.args, **kwargs: P1.kwargs): ... # E: ParamSpec "P2" is unbound \ + # E: ParamSpec "P1" is unbound + [builtins fixtures/paramspec.pyi] @@ -1354,7 +1379,8 @@ P = ParamSpec('P') class Some(Generic[P]): def call(self, *args: P.args, **kwargs: P.kwargs): ... -def call(*args: P.args, **kwargs: P.kwargs): ... +def call(*args: P.args, **kwargs: P.kwargs): ... # E: ParamSpec "P" is unbound + [builtins fixtures/paramspec.pyi] [case testParamSpecInferenceCrash] @@ -1452,6 +1478,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 @@ -1608,7 +1645,6 @@ def f(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... [builtins fixtures/paramspec.pyi] [case testParamSpecDecoratorAppliedToGeneric] -# flags: --new-type-inference from typing import Callable, List, TypeVar from typing_extensions import ParamSpec @@ -1646,7 +1682,6 @@ reveal_type(test(*ints)) # N: Revealed type is "__main__.Foo[[*builtins.int]]" [builtins fixtures/paramspec.pyi] [case testParamSpecArgumentParamInferenceGeneric] -# flags: --new-type-inference from typing import Callable, TypeVar from typing_extensions import ParamSpec @@ -1664,7 +1699,6 @@ y: int = call(identity, 2) [builtins fixtures/paramspec.pyi] [case testParamSpecNestedApplyNoCrash] -# flags: --new-type-inference from typing import Callable, TypeVar from typing_extensions import ParamSpec @@ -2097,7 +2131,7 @@ def d(f: Callable[P, None], fn: Callable[Concatenate[Callable[P, None], P], None reveal_type(d(a, f1)) # N: Revealed type is "def (i: builtins.int)" reveal_type(d(a, f2)) # N: Revealed type is "def (i: builtins.int)" -reveal_type(d(b, f1)) # E: Cannot infer type argument 1 of "d" \ +reveal_type(d(b, f1)) # E: Cannot infer value of type parameter "P" of "d" \ # N: Revealed type is "def (*Any, **Any)" reveal_type(d(b, f2)) # N: Revealed type is "def (builtins.int)" [builtins fixtures/paramspec.pyi] @@ -2120,34 +2154,12 @@ reveal_type(submit( # N: Revealed type is "__main__.Result" backend="asyncio", )) submit( - run, # E: Argument 1 to "submit" has incompatible type "Callable[[Callable[[], R], VarArg(object), DefaultNamedArg(str, 'backend')], R]"; expected "Callable[[Callable[[], Result], int], Result]" + run, # E: Argument 1 to "submit" has incompatible type "def [R] run(func: Callable[[], R], *args: object, backend: str = ...) -> R"; expected "Callable[[Callable[[], Result], int], Result]" run_portal, backend=int(), ) [builtins fixtures/paramspec.pyi] -[case testParamSpecGenericWithNamedArg2] -from typing import Callable, TypeVar, Type -from typing_extensions import ParamSpec - -P= ParamSpec("P") -T = TypeVar("T") - -def smoke_testable(*args: P.args, **kwargs: P.kwargs) -> Callable[[Callable[P, T]], Type[T]]: - ... - -@smoke_testable(name="bob", size=512, flt=0.5) -class SomeClass: - def __init__(self, size: int, name: str, flt: float) -> None: - pass - -# Error message is confusing, but this is a known issue, see #4530. -@smoke_testable(name=42, size="bad", flt=0.5) # E: Argument 1 has incompatible type "Type[OtherClass]"; expected "Callable[[int, str, float], OtherClass]" -class OtherClass: - def __init__(self, size: int, name: str, flt: float) -> None: - pass -[builtins fixtures/paramspec.pyi] - [case testInferenceAgainstGenericCallableUnionParamSpec] from typing import Callable, TypeVar, List, Union from typing_extensions import ParamSpec @@ -2303,3 +2315,287 @@ reveal_type(capture(fn)) # N: Revealed type is "Union[builtins.str, builtins.in 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] + +[case testParamSpecScoping] +from typing import Any, Callable, Generic +from typing_extensions import Concatenate, ParamSpec + +P = ParamSpec("P") +P2 = ParamSpec("P2") + +def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... +def contains_other(f: Callable[P2, None], c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + +def contains_only_other(c: Callable[P2, None], *args: P.args, **kwargs: P.kwargs) -> None: ... # E: ParamSpec "P" is unbound + +def puts_p_into_scope(f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + +def puts_p_into_scope_concatenate(f: Callable[Concatenate[int, P], int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + +def wrapper() -> None: + def puts_p_into_scope1(f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + +class Wrapper: + def puts_p_into_scope1(self, f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + + def contains(self, c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + + def uses(self, *args: P.args, **kwargs: P.kwargs) -> None: ... # E: ParamSpec "P" is unbound + + def method(self) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... # E: ParamSpec "P" is unbound + +class GenericWrapper(Generic[P]): + x: P.args # E: ParamSpec components are not allowed here + y: P.kwargs # E: ParamSpec components are not allowed here + + def contains(self, c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + + def puts_p_into_scope1(self, f: Callable[P, int]) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... + + def uses(self, *args: P.args, **kwargs: P.kwargs) -> None: ... + + def method(self) -> None: + def contains(c: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + def inherits(*args: P.args, **kwargs: P.kwargs) -> None: ... +[builtins fixtures/paramspec.pyi] + +[case testCallbackProtocolClassObjectParamSpec] +from typing import Any, Callable, Protocol, Optional, Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") + +class App: ... + +class MiddlewareFactory(Protocol[P]): + def __call__(self, app: App, /, *args: P.args, **kwargs: P.kwargs) -> App: + ... + +class Capture(Generic[P]): ... + +class ServerErrorMiddleware(App): + def __init__( + self, + app: App, + handler: Optional[str] = None, + debug: bool = False, + ) -> None: ... + +def fn(f: MiddlewareFactory[P]) -> Capture[P]: ... + +reveal_type(fn(ServerErrorMiddleware)) # N: Revealed type is "__main__.Capture[[handler: Union[builtins.str, None] =, debug: builtins.bool =]]" +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecDuplicateArgsKwargs] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable, Union + +_P = ParamSpec("_P") + +def run(predicate: Callable[_P, None], *args: _P.args, **kwargs: _P.kwargs) -> None: + predicate(*args, *args, **kwargs) # E: ParamSpec.args should only be passed once + predicate(*args, **kwargs, **kwargs) # E: ParamSpec.kwargs should only be passed once + predicate(*args, *args, **kwargs, **kwargs) # E: ParamSpec.args should only be passed once \ + # E: ParamSpec.kwargs should only be passed once + copy_args = args + copy_kwargs = kwargs + predicate(*args, *copy_args, **kwargs) # E: ParamSpec.args should only be passed once + predicate(*copy_args, *args, **kwargs) # E: ParamSpec.args should only be passed once + predicate(*args, **copy_kwargs, **kwargs) # E: ParamSpec.kwargs should only be passed once + predicate(*args, **kwargs, **copy_kwargs) # E: ParamSpec.kwargs should only be passed once + +def run2(predicate: Callable[Concatenate[int, _P], None], *args: _P.args, **kwargs: _P.kwargs) -> None: + predicate(*args, *args, **kwargs) # E: ParamSpec.args should only be passed once \ + # E: Argument 1 has incompatible type "*_P.args"; expected "int" + predicate(*args, **kwargs, **kwargs) # E: ParamSpec.kwargs should only be passed once \ + # E: Argument 1 has incompatible type "*_P.args"; expected "int" + predicate(1, *args, *args, **kwargs) # E: ParamSpec.args should only be passed once + predicate(1, *args, **kwargs, **kwargs) # E: ParamSpec.kwargs should only be passed once + predicate(1, *args, *args, **kwargs, **kwargs) # E: ParamSpec.args should only be passed once \ + # E: ParamSpec.kwargs should only be passed once + copy_args = args + copy_kwargs = kwargs + predicate(1, *args, *copy_args, **kwargs) # E: ParamSpec.args should only be passed once + predicate(1, *copy_args, *args, **kwargs) # E: ParamSpec.args should only be passed once + predicate(1, *args, **copy_kwargs, **kwargs) # E: ParamSpec.kwargs should only be passed once + predicate(1, *args, **kwargs, **copy_kwargs) # E: ParamSpec.kwargs should only be passed once + +def run3(predicate: Callable[Concatenate[int, str, _P], None], *args: _P.args, **kwargs: _P.kwargs) -> None: + base_ok: tuple[int, str] + predicate(*base_ok, *args, **kwargs) + base_bad: tuple[Union[int, str], ...] + predicate(*base_bad, *args, **kwargs) # E: Argument 1 has incompatible type "*tuple[Union[int, str], ...]"; expected "int" \ + # E: Argument 1 has incompatible type "*tuple[Union[int, str], ...]"; expected "str" \ + # E: Argument 1 has incompatible type "*tuple[Union[int, str], ...]"; expected "_P.args" +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 0c653d6081875..42f21e945ef0d 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -31,7 +31,7 @@ class A: reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" [builtins fixtures/list.pyi] @@ -49,7 +49,7 @@ class A: reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" [builtins fixtures/list.pyi] @@ -67,7 +67,7 @@ class A: reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" [builtins fixtures/list.pyi] @@ -120,7 +120,7 @@ class A: reveal_type(A) # N: Revealed type is "def (a: Any, b: builtins.list[builtins.int], c: Any =, d: Any =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "List[int]" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" [builtins fixtures/list.pyi] @@ -463,15 +463,15 @@ class A(Generic[T]): def bar(self) -> T: return self.x[0] def problem(self) -> T: - return self.x # E: Incompatible return value type (got "List[T]", expected "T") + return self.x # E: Incompatible return value type (got "list[T]", expected "T") reveal_type(A) # N: Revealed type is "def [T] (x: builtins.list[T`1], y: T`1) -> __main__.A[T`1]" a = A([1], 2) reveal_type(a) # N: Revealed type is "__main__.A[builtins.int]" reveal_type(a.x) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(a.y) # N: Revealed type is "builtins.int" -A(['str'], 7) # E: Cannot infer type argument 1 of "A" -A([1], '2') # E: Cannot infer type argument 1 of "A" +A(['str'], 7) # E: Cannot infer value of type parameter "T" of "A" +A([1], '2') # E: Cannot infer value of type parameter "T" of "A" [builtins fixtures/list.pyi] @@ -495,7 +495,7 @@ class A(Generic[T]): def bar(self) -> T: return self.x[0] def problem(self) -> T: - return self.x # E: Incompatible return value type (got "List[T]", expected "T") + return self.x # E: Incompatible return value type (got "list[T]", expected "T") reveal_type(A) # N: Revealed type is "def [T] (x: typing.Iterable[T`1], y: T`1) -> __main__.A[T`1]" a1 = A([1], 2) reveal_type(a1) # N: Revealed type is "__main__.A[builtins.int]" @@ -668,7 +668,7 @@ class A(Generic[T]): x: Optional[T] @classmethod def clsmeth(cls) -> None: - reveal_type(cls) # N: Revealed type is "Type[__main__.A[T`1]]" + reveal_type(cls) # N: Revealed type is "type[__main__.A[T`1]]" [builtins fixtures/classmethod.pyi] @@ -723,7 +723,7 @@ class A: b: str = attr.ib() @classmethod def new(cls) -> A: - reveal_type(cls) # N: Revealed type is "Type[__main__.A]" + reveal_type(cls) # N: Revealed type is "type[__main__.A]" return cls(6, 'hello') @classmethod def bad(cls) -> A: @@ -758,7 +758,7 @@ class A: @classmethod def foo(cls, x: Union[int, str]) -> Union[int, str]: - reveal_type(cls) # N: Revealed type is "Type[__main__.A]" + reveal_type(cls) # N: Revealed type is "type[__main__.A]" reveal_type(cls.other()) # N: Revealed type is "builtins.str" return x @@ -990,10 +990,10 @@ class C(A, B): pass @attr.s class D(A): pass -reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" -reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" -reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool" -reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`8, other: _AT`8) -> builtins.bool" +reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`29, other: _AT`29) -> builtins.bool" +reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`30, other: _AT`30) -> builtins.bool" +reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`31, other: _AT`31) -> builtins.bool" +reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`32, other: _AT`32) -> builtins.bool" A() < A() B() < B() @@ -1201,13 +1201,12 @@ class A: [builtins fixtures/bool.pyi] [case testAttrsFactoryBadReturn] -# flags: --new-type-inference import attr def my_factory() -> int: return 7 @attr.s class A: - x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "int") + x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "int") y: str = attr.ib(factory=my_factory) # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/list.pyi] @@ -1518,7 +1517,7 @@ class A: b: int = attr.ib() c: str = attr.ib() -reveal_type(A.__attrs_attrs__) # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" +reveal_type(A.__attrs_attrs__) # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" reveal_type(A.__attrs_attrs__[0]) # N: Revealed type is "attr.Attribute[builtins.int]" reveal_type(A.__attrs_attrs__.b) # N: Revealed type is "attr.Attribute[builtins.int]" A.__attrs_attrs__.x # E: "____main___A_AttrsAttributes__" has no attribute "x" @@ -1533,7 +1532,7 @@ class A: b = attr.ib() c = attr.ib() -reveal_type(A.__attrs_attrs__) # N: Revealed type is "Tuple[attr.Attribute[Any], attr.Attribute[Any], fallback=__main__.A.____main___A_AttrsAttributes__]" +reveal_type(A.__attrs_attrs__) # N: Revealed type is "tuple[attr.Attribute[Any], attr.Attribute[Any], fallback=__main__.A.____main___A_AttrsAttributes__]" reveal_type(A.__attrs_attrs__[0]) # N: Revealed type is "attr.Attribute[Any]" reveal_type(A.__attrs_attrs__.b) # N: Revealed type is "attr.Attribute[Any]" A.__attrs_attrs__.x # E: "____main___A_AttrsAttributes__" has no attribute "x" @@ -1548,7 +1547,7 @@ class A: b: int c: str -reveal_type(A.__attrs_attrs__) # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" +reveal_type(A.__attrs_attrs__) # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" reveal_type(A.__attrs_attrs__[0]) # N: Revealed type is "attr.Attribute[builtins.int]" reveal_type(A.__attrs_attrs__.b) # N: Revealed type is "attr.Attribute[builtins.int]" A.__attrs_attrs__.x # E: "____main___A_AttrsAttributes__" has no attribute "x" @@ -1576,8 +1575,8 @@ def takes_attrs_instance(inst: AttrsInstance) -> None: takes_attrs_cls(A) takes_attrs_instance(A(1, "")) -takes_attrs_cls(A(1, "")) # E: Argument 1 to "takes_attrs_cls" has incompatible type "A"; expected "Type[AttrsInstance]" -takes_attrs_instance(A) # E: Argument 1 to "takes_attrs_instance" has incompatible type "Type[A]"; expected "AttrsInstance" # N: ClassVar protocol member AttrsInstance.__attrs_attrs__ can never be matched by a class object +takes_attrs_cls(A(1, "")) # E: Argument 1 to "takes_attrs_cls" has incompatible type "A"; expected "type[AttrsInstance]" +takes_attrs_instance(A) # E: Argument 1 to "takes_attrs_instance" has incompatible type "type[A]"; expected "AttrsInstance" # N: ClassVar protocol member AttrsInstance.__attrs_attrs__ can never be matched by a class object [builtins fixtures/plugin_attrs.pyi] [case testAttrsFields] @@ -1589,7 +1588,7 @@ class A: b: int c: str -reveal_type(f(A)) # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" +reveal_type(f(A)) # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" reveal_type(f(A)[0]) # N: Revealed type is "attr.Attribute[builtins.int]" reveal_type(f(A).b) # N: Revealed type is "attr.Attribute[builtins.int]" f(A).x # E: "____main___A_AttrsAttributes__" has no attribute "x" @@ -1613,7 +1612,7 @@ class A: TA = TypeVar('TA', bound=A) def f(t: TA) -> None: - reveal_type(fields(t)) # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" + reveal_type(fields(t)) # N: Revealed type is "tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str], fallback=__main__.A.____main___A_AttrsAttributes__]" reveal_type(fields(t)[0]) # N: Revealed type is "attr.Attribute[builtins.int]" reveal_type(fields(t).b) # N: Revealed type is "attr.Attribute[builtins.int]" fields(t).x # E: "____main___A_AttrsAttributes__" has no attribute "x" @@ -1632,8 +1631,8 @@ class A: if has(A): fields(A) else: - fields(A) # E: Argument 1 to "fields" has incompatible type "Type[A]"; expected "Type[AttrsInstance]" -fields(None) # E: Argument 1 to "fields" has incompatible type "None"; expected "Type[AttrsInstance]" + fields(A) # E: Argument 1 to "fields" has incompatible type "type[A]"; expected "type[AttrsInstance]" +fields(None) # E: Argument 1 to "fields" has incompatible type "None"; expected "type[AttrsInstance]" fields(cast(Any, 42)) fields(cast(Type[Any], 43)) @@ -1651,8 +1650,8 @@ class A: b, c = bc self.__attrs_init__(b, c) -reveal_type(A) # N: Revealed type is "def (bc: Tuple[builtins.int, builtins.str]) -> __main__.A" -reveal_type(A.__init__) # N: Revealed type is "def (self: __main__.A, bc: Tuple[builtins.int, builtins.str])" +reveal_type(A) # N: Revealed type is "def (bc: tuple[builtins.int, builtins.str]) -> __main__.A" +reveal_type(A.__init__) # N: Revealed type is "def (self: __main__.A, bc: tuple[builtins.int, builtins.str])" reveal_type(A.__attrs_init__) # N: Revealed type is "def (self: __main__.A, b: builtins.int, c: builtins.str)" [builtins fixtures/plugin_attrs.pyi] @@ -1729,14 +1728,14 @@ class Some: y: str z: bool -reveal_type(Some.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]" +reveal_type(Some.__slots__) # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.str]" @dataclass(slots=True) class Other: x: int y: str -reveal_type(Other.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(Other.__slots__) # N: Revealed type is "tuple[builtins.str, builtins.str]" @dataclass @@ -1744,7 +1743,7 @@ class NoSlots: x: int y: str -NoSlots.__slots__ # E: "Type[NoSlots]" has no attribute "__slots__" +NoSlots.__slots__ # E: "type[NoSlots]" has no attribute "__slots__" [builtins fixtures/plugin_attrs.pyi] [case testAttrsWithMatchArgs] @@ -1759,8 +1758,8 @@ class ToMatch: z: int = attr.field(kw_only=True) i: int = attr.field(init=False) -reveal_type(ToMatch(x=1, y=2, z=3).__match_args__) # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]" -reveal_type(ToMatch(1, 2, z=3).__match_args__) # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]" +reveal_type(ToMatch(x=1, y=2, z=3).__match_args__) # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]" +reveal_type(ToMatch(1, 2, z=3).__match_args__) # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]" [builtins fixtures/plugin_attrs.pyi] [case testAttrsWithMatchArgsDefaultCase] @@ -1773,7 +1772,7 @@ class ToMatch1: y: int t1: ToMatch1 -reveal_type(t1.__match_args__) # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]" +reveal_type(t1.__match_args__) # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]" @attr.define class ToMatch2: @@ -1781,7 +1780,7 @@ class ToMatch2: y: int t2: ToMatch2 -reveal_type(t2.__match_args__) # N: Revealed type is "Tuple[Literal['x']?, Literal['y']?]" +reveal_type(t2.__match_args__) # N: Revealed type is "tuple[Literal['x']?, Literal['y']?]" [builtins fixtures/plugin_attrs.pyi] [case testAttrsWithMatchArgsOverrideExisting] @@ -1796,7 +1795,7 @@ class ToMatch: y: int # It works the same way runtime does: -reveal_type(ToMatch(x=1, y=2).__match_args__) # N: Revealed type is "Tuple[Literal['a']?, Literal['b']?]" +reveal_type(ToMatch(x=1, y=2).__match_args__) # N: Revealed type is "tuple[Literal['a']?, Literal['b']?]" @attr.s(auto_attribs=True) class WithoutMatch: @@ -1804,7 +1803,7 @@ class WithoutMatch: x: int y: int -reveal_type(WithoutMatch(x=1, y=2).__match_args__) # N: Revealed type is "Tuple[Literal['a']?, Literal['b']?]" +reveal_type(WithoutMatch(x=1, y=2).__match_args__) # N: Revealed type is "tuple[Literal['a']?, Literal['b']?]" [builtins fixtures/plugin_attrs.pyi] [case testAttrsWithMatchArgsOldVersion] @@ -1836,6 +1835,7 @@ class B: class AB(A, B): pass [builtins fixtures/plugin_attrs.pyi] +[typing fixtures/typing-full.pyi] [case testAttrsForwardReferenceInTypeVarBound] from typing import TypeVar, Generic @@ -2171,7 +2171,7 @@ class B: a_or_b: A[int] | B a2 = attrs.evolve(a_or_b, x=42, y=True) a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected "Never" -a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never" +a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "dict[Never, Never]"; expected "Never" [builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/check-plugin-error-codes.test b/test-data/unit/check-plugin-error-codes.test new file mode 100644 index 0000000000000..95789477977e5 --- /dev/null +++ b/test-data/unit/check-plugin-error-codes.test @@ -0,0 +1,32 @@ +[case testCustomErrorCodeFromPluginIsTargetable] +# flags: --config-file tmp/mypy.ini --show-error-codes + +def main() -> None: + return +main() # E: Custom error [custom] + +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/custom_errorcode.py + +[case testCustomErrorCodeCanBeDisabled] +# flags: --config-file tmp/mypy.ini --show-error-codes --disable-error-code=custom + +def main() -> None: + return +main() # no output expected when disabled + +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/custom_errorcode.py + +[case testCustomErrorCodeCanBeReenabled] +# flags: --config-file tmp/mypy.ini --show-error-codes --disable-error-code=custom --enable-error-code=custom + +def main() -> None: + return +main() # E: Custom error [custom] + +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/custom_errorcode.py diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 5ed2351e33e6c..fd7f0c3449daf 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -332,7 +332,7 @@ class MyHashable(Protocol): class C(MyHashable): __my_hash__ = None # E: Incompatible types in assignment \ -(expression has type "None", base class "MyHashable" defined the type as "Callable[[MyHashable], int]") +(expression has type "None", base class "MyHashable" defined the type as "Callable[[], int]") [case testProtocolsWithNoneAndStrictOptional] from typing import Protocol @@ -667,7 +667,7 @@ def fun4(x: U, y: P[U, U]) -> U: pass reveal_type(fun4('a', C())) # N: Revealed type is "builtins.object" -[case testUnrealtedGenericProtolsEquivalent] +[case testUnrealtedGenericProtocolsEquivalent] from typing import TypeVar, Protocol T = TypeVar('T') @@ -715,7 +715,7 @@ c: C var: P2[int, int] = c var2: P2[int, str] = c # E: Incompatible types in assignment (expression has type "C", variable has type "P2[int, str]") \ # N: Following member(s) of "C" have conflicts: \ - # N: attr2: expected "Tuple[int, str]", got "Tuple[int, int]" + # N: attr2: expected "tuple[int, str]", got "tuple[int, int]" class D(Generic[T]): attr1: T @@ -973,7 +973,7 @@ class B: t: P1 t = A() # E: Incompatible types in assignment (expression has type "A", variable has type "P1") \ # N: Following member(s) of "A" have conflicts: \ - # N: attr1: expected "Sequence[P2]", got "List[B]" + # N: attr1: expected "Sequence[P2]", got "list[B]" [builtins fixtures/list.pyi] [case testMutuallyRecursiveProtocolsTypesWithSubteMismatchWriteable] @@ -1373,6 +1373,19 @@ main:16: note: def meth(self, x: int) -> int main:16: note: @overload main:16: note: def meth(self, x: bytes) -> str +[case testProtocolWithMultiContravariantTypeVarOverloads] +from typing import overload, Protocol, TypeVar + +T1 = TypeVar("T1", contravariant=True) +T2 = TypeVar("T2", contravariant=True) + +class A(Protocol[T1, T2]): + @overload + def method(self, a: T1) -> None: ... + @overload + def method(self, a: T2) -> None: ... + + -- Join and meet with protocol types -- --------------------------------- @@ -1506,11 +1519,12 @@ class C: pass def f(x: P1) -> int: ... @overload def f(x: P2) -> str: ... -def f(x): +def f(x: object) -> object: if isinstance(x, P1): return P1.attr1 if isinstance(x, P2): # E: Only @runtime_checkable protocols can be used with instance and class checks - return P1.attr2 + return P2.attr2 + return None reveal_type(f(C1())) # N: Revealed type is "builtins.int" reveal_type(f(C2())) # N: Revealed type is "builtins.str" @@ -1607,13 +1621,13 @@ def f(cls: Type[P]) -> P: def g() -> P: return P() # E: Cannot instantiate protocol class "P" -f(P) # E: Only concrete class can be given where "Type[P]" is expected +f(P) # E: Only concrete class can be given where "type[P]" is expected f(B) # OK f(C) # OK x: Type[P1] xbad: Type[Pbad] f(x) # OK -f(xbad) # E: Argument 1 to "f" has incompatible type "Type[Pbad]"; expected "Type[P]" +f(xbad) # E: Argument 1 to "f" has incompatible type "type[Pbad]"; expected "type[P]" [case testInstantiationProtocolInTypeForAliases] from typing import Type, Protocol @@ -1631,7 +1645,7 @@ Alias = P GoodAlias = C Alias() # E: Cannot instantiate protocol class "P" GoodAlias() -f(Alias) # E: Only concrete class can be given where "Type[P]" is expected +f(Alias) # E: Only concrete class can be given where "type[P]" is expected f(GoodAlias) [case testInstantiationProtocolInTypeForVariables] @@ -1648,14 +1662,14 @@ class C: var: Type[P] var() if int(): - var = P # E: Can only assign concrete classes to a variable of type "Type[P]" + var = P # E: Can only assign concrete classes to a variable of type "type[P]" var = B # OK var = C # OK var_old = None # type: Type[P] # Old syntax for variable annotations var_old() if int(): - var_old = P # E: Can only assign concrete classes to a variable of type "Type[P]" + var_old = P # E: Can only assign concrete classes to a variable of type "type[P]" var_old = B # OK var_old = C # OK @@ -1754,7 +1768,7 @@ if isinstance(c1i, P1): else: reveal_type(c1i) # Unreachable if isinstance(c1i, P): - reveal_type(c1i) # N: Revealed type is "__main__." + reveal_type(c1i) # N: Revealed type is "__main__." else: reveal_type(c1i) # N: Revealed type is "__main__.C1[builtins.int]" @@ -1766,7 +1780,7 @@ else: c2: C2 if isinstance(c2, P): - reveal_type(c2) # N: Revealed type is "__main__." + reveal_type(c2) # N: Revealed type is "__main__." else: reveal_type(c2) # N: Revealed type is "__main__.C2" @@ -1825,7 +1839,7 @@ def f(x: MyProto[int]) -> None: f(t) # OK y: MyProto[str] -y = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "MyProto[str]") +y = t # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "MyProto[str]") [builtins fixtures/isinstancelist.pyi] [case testBasicNamedTupleStructuralSubtyping] @@ -1893,7 +1907,7 @@ reveal_type(apply_gen(Add5())) # N: Revealed type is "builtins.int" def apply_str(f: Callable[[str], int], x: str) -> int: return f(x) apply_str(Add5(), 'a') # E: Argument 1 to "apply_str" has incompatible type "Add5"; expected "Callable[[str], int]" \ - # N: "Add5.__call__" has type "Callable[[Arg(int, 'x')], int]" + # N: "Add5.__call__" has type "def __call__(self, x: int) -> int" [builtins fixtures/isinstancelist.pyi] [case testMoreComplexCallableStructuralSubtyping] @@ -1909,10 +1923,10 @@ class Bad1: class Bad2: def __call__(self, y: int, *rest: str) -> int: pass call_soon(Good()) -call_soon(Bad1()) # E: Argument 1 to "call_soon" has incompatible type "Bad1"; expected "Callable[[int, VarArg(str)], int]" \ - # N: "Bad1.__call__" has type "Callable[[Arg(int, 'x'), VarArg(int)], int]" -call_soon(Bad2()) # E: Argument 1 to "call_soon" has incompatible type "Bad2"; expected "Callable[[int, VarArg(str)], int]" \ - # N: "Bad2.__call__" has type "Callable[[Arg(int, 'y'), VarArg(str)], int]" +call_soon(Bad1()) # E: Argument 1 to "call_soon" has incompatible type "Bad1"; expected "def (x: int, *str) -> int" \ + # N: "Bad1.__call__" has type "def __call__(self, x: int, *rest: int) -> int" +call_soon(Bad2()) # E: Argument 1 to "call_soon" has incompatible type "Bad2"; expected "def (x: int, *str) -> int" \ + # N: "Bad2.__call__" has type "def __call__(self, y: int, *rest: str) -> int" [builtins fixtures/isinstancelist.pyi] [case testStructuralSupportForPartial] @@ -1943,7 +1957,7 @@ class Actual: def __call__(self, arg: int) -> str: pass def fun(cb: Callable[[T], S]) -> Tuple[T, S]: pass -reveal_type(fun(Actual())) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(fun(Actual())) # N: Revealed type is "tuple[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] -- Standard protocol types (SupportsInt, Sized, etc.) @@ -2339,8 +2353,7 @@ main:19: note: Protocol member AllSettable.b expected settable variable, got rea main:19: note: <2 more conflict(s) not shown> [case testProtocolsMoreConflictsNotShown] -from typing_extensions import Protocol -from typing import Generic, TypeVar +from typing import Generic, Protocol, TypeVar T = TypeVar('T') @@ -2440,9 +2453,9 @@ cls: Type[Union[C, E]] issubclass(cls, PBad) # E: Only protocols that don't have non-method members can be used with issubclass() \ # N: Protocol "PBad" has non-method member(s): x if issubclass(cls, P): - reveal_type(cls) # N: Revealed type is "Type[__main__.C]" + reveal_type(cls) # N: Revealed type is "type[__main__.C]" else: - reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + reveal_type(cls) # N: Revealed type is "type[__main__.E]" @runtime_checkable class POverload(Protocol): @@ -2473,7 +2486,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 "def bad(x: int, *args: str) -> None"; expected "Caller" \ + # N: "Caller.__call__" has type "def __call__(self, x: str, *args: int) -> None" [builtins fixtures/tuple.pyi] [out] @@ -2491,7 +2505,7 @@ def call(x: int, y: str) -> Tuple[int, str]: ... def func(caller: Caller[T, S]) -> Tuple[T, S]: pass -reveal_type(func(call)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(func(call)) # N: Revealed type is "tuple[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [out] @@ -2510,7 +2524,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 "def [T] __call__(self, x: T) -> T" [builtins fixtures/tuple.pyi] [out] @@ -2530,7 +2545,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 "def __call__(self, x: int) -> int" [builtins fixtures/tuple.pyi] [out] @@ -2557,7 +2573,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] @@ -2569,8 +2586,8 @@ class Caller(Protocol): def bad(x: int, *args: str) -> None: pass -cb: Caller = bad # E: Incompatible types in assignment (expression has type "Callable[[int, VarArg(str)], None]", variable has type "Caller") \ - # N: "Caller.__call__" has type "Callable[[Arg(str, 'x'), VarArg(int)], None]" +cb: Caller = bad # E: Incompatible types in assignment (expression has type "def bad(x: int, *args: str) -> None", variable has type "Caller") \ + # N: "Caller.__call__" has type "def __call__(self, x: str, *args: int) -> None" [builtins fixtures/tuple.pyi] [out] @@ -2596,7 +2613,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 "def __call__(self, x: str) -> None" anon(bad) [out] @@ -2619,7 +2637,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 "def __call__(self, x: str) -> None" [out] [case testJoinProtocolCallback] @@ -2706,7 +2725,7 @@ class A(Protocol[T, S]): def f() -> int: ... def test(func: A[T, S]) -> Tuple[T, S]: ... -reveal_type(test(f)) # N: Revealed type is "Tuple[builtins.str, builtins.int]" +reveal_type(test(f)) # N: Revealed type is "tuple[builtins.str, builtins.int]" [builtins fixtures/tuple.pyi] [case testProtocolsAlwaysABCs] @@ -2761,8 +2780,7 @@ p: P = N(lambda a, b, c: 'foo') [builtins fixtures/property.pyi] [case testLiteralsAgainstProtocols] -from typing import SupportsInt, SupportsAbs, TypeVar -from typing_extensions import Literal, Final +from typing import Final, Literal, SupportsInt, SupportsAbs, TypeVar T = TypeVar('T') def abs(x: SupportsAbs[T]) -> T: ... @@ -2857,7 +2875,7 @@ c1: SupportsClassGetItem = C() [case testNoneVsProtocol] # mypy: strict-optional -from typing_extensions import Protocol +from typing import Protocol class MyHashable(Protocol): def __hash__(self) -> int: ... @@ -2900,6 +2918,7 @@ hs(None) [case testPartialTypeProtocol] +# flags: --no-local-partial-types from typing import Protocol class Flapper(Protocol): @@ -2912,7 +2931,7 @@ class Blooper: reveal_type([self, x]) # N: Revealed type is "builtins.list[builtins.object]" class Gleemer: - flap = [] # E: Need type annotation for "flap" (hint: "flap: List[] = ...") + flap = [] # E: Need type annotation for "flap" (hint: "flap: list[] = ...") def gleem(self, x: Flapper) -> None: reveal_type([self, x]) # N: Revealed type is "builtins.list[builtins.object]" @@ -2938,7 +2957,7 @@ class DataArray(ObjectHashable): [case testPartialAttributeNoneType] -# flags: --no-strict-optional +# flags: --no-strict-optional --no-local-partial-types from typing import Optional, Protocol, runtime_checkable @runtime_checkable @@ -2956,6 +2975,7 @@ class MyClass: [case testPartialAttributeNoneTypeStrictOptional] +# flags: --no-local-partial-types from typing import Optional, Protocol, runtime_checkable @runtime_checkable @@ -3281,7 +3301,7 @@ class C: def foo(t: Template) -> None: ... foo(B()) # E: Argument 1 to "foo" has incompatible type "B"; expected "Template" \ # N: Following member(s) of "B" have conflicts: \ - # N: Meta: expected "Type[__main__.Template.Meta]", got "Type[__main__.B.Meta]" + # N: Meta: expected "type[__main__.Template.Meta]", got "type[__main__.B.Meta]" foo(C()) # OK [case testProtocolClassObjectAttribute] @@ -3302,10 +3322,10 @@ class D: def test(arg: P) -> None: ... test(A) # OK test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: foo: expected "int", got "str" -test(D) # E: Argument 1 to "test" has incompatible type "Type[D]"; expected "P" \ +test(D) # E: Argument 1 to "test" has incompatible type "type[D]"; expected "P" \ # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "D" [case testProtocolClassObjectClassVarRejected] @@ -3318,7 +3338,7 @@ class B: foo: ClassVar[int] def test(arg: P) -> None: ... -test(B) # E: Argument 1 to "test" has incompatible type "Type[B]"; expected "P" \ +test(B) # E: Argument 1 to "test" has incompatible type "type[B]"; expected "P" \ # N: ClassVar protocol member P.foo can never be matched by a class object [case testProtocolClassObjectPropertyRejected] @@ -3338,11 +3358,11 @@ class D: def test(arg: P) -> None: ... # TODO: skip type mismatch diagnostics in this case. -test(B) # E: Argument 1 to "test" has incompatible type "Type[B]"; expected "P" \ +test(B) # E: Argument 1 to "test" has incompatible type "type[B]"; expected "P" \ # N: Following member(s) of "B" have conflicts: \ # N: foo: expected "int", got "Callable[[B], int]" \ # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "B" -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "C" test(D) # OK [builtins fixtures/property.pyi] @@ -3360,7 +3380,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo(obj: Any) -> int \ @@ -3380,7 +3400,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo(obj: B) -> int \ @@ -3414,7 +3434,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: @overload \ @@ -3442,7 +3462,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo() -> int \ @@ -3465,7 +3485,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo() -> int \ @@ -3489,12 +3509,12 @@ class C(AA[str]): ... def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ - # N: def foo(obj: Any) -> List[int] \ + # N: def foo(obj: Any) -> list[int] \ # N: Got: \ - # N: def foo(self: A[List[str]]) -> List[str] + # N: def foo(self: A[list[str]]) -> list[str] [builtins fixtures/list.pyi] [case testProtocolClassObjectGenericClassMethod] @@ -3514,12 +3534,12 @@ class C(AA[str]): ... def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ - # N: def foo() -> List[int] \ + # N: def foo() -> list[int] \ # N: Got: \ - # N: def foo() -> List[str] + # N: def foo() -> list[str] [builtins fixtures/isinstancelist.pyi] [case testProtocolClassObjectSelfTypeInstanceMethod] @@ -3536,7 +3556,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def [T] foo(arg: T) -> T \ @@ -3559,7 +3579,7 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo() -> B \ @@ -3583,13 +3603,14 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: "C" has constructor incompatible with "__call__" of "P" \ # N: Following member(s) of "C" have conflicts: \ # 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 "def __call__(self, x: int, y: int) -> Any" [case testProtocolClassObjectPureCallback] from typing import Any, ClassVar, Protocol @@ -3604,13 +3625,14 @@ class C: def test(arg: P) -> None: ... test(B) # OK -test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: "C" has constructor incompatible with "__call__" of "P" \ # N: Following member(s) of "C" have conflicts: \ # 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 "def __call__(self, x: int, y: int) -> Any" [builtins fixtures/type.pyi] [case testProtocolClassObjectCallableError] @@ -3627,13 +3649,13 @@ class C: def __call__(self, el: str) -> None: return None -p: P = C # E: Incompatible types in assignment (expression has type "Type[C]", variable has type "P") \ +p: P = C # E: Incompatible types in assignment (expression has type "type[C]", variable has type "P") \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def __call__(app: int) -> Callable[[str], None] \ # N: Got: \ # N: def __init__(app: str) -> C \ - # N: "P.__call__" has type "Callable[[Arg(int, 'app')], Callable[[str], None]]" + # N: "P.__call__" has type "def __call__(self, app: int) -> Callable[[str], None]" [builtins fixtures/type.pyi] @@ -3659,10 +3681,10 @@ c: Type[C] d: Type[D] test(a) # OK test(b) # OK -test(c) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(c) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: foo: expected "int", got "str" -test(d) # E: Argument 1 to "test" has incompatible type "Type[D]"; expected "P" \ +test(d) # E: Argument 1 to "test" has incompatible type "type[D]"; expected "P" \ # N: Only class variables allowed for class object access on protocols, foo is an instance variable of "D" [case testProtocolTypeTypeInstanceMethod] @@ -3680,7 +3702,7 @@ def test(arg: P) -> None: ... b: Type[B] c: Type[C] test(b) # OK -test(c) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(c) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo(cls: Any) -> int \ @@ -3704,7 +3726,7 @@ def test(arg: P) -> None: ... b: Type[B] c: Type[C] test(b) # OK -test(c) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(c) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def foo() -> int \ @@ -3728,7 +3750,7 @@ def test(arg: P) -> None: ... b: Type[B] c: Type[C] test(b) # OK -test(c) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" \ +test(c) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \ # N: Following member(s) of "C" have conflicts: \ # N: Expected: \ # N: def [T] foo(arg: T) -> T \ @@ -3792,7 +3814,7 @@ def f_good(t: S) -> S: return t g: C = f_bad # E: Incompatible types in assignment (expression has type "Callable[[int], int]", variable has type "C") \ - # N: "C.__call__" has type "Callable[[Arg(T, 't')], T]" + # N: "C.__call__" has type "def [T] __call__(self, t: T) -> T" g = f_good # OK [case testModuleAsProtocolImplementation] @@ -3960,7 +3982,7 @@ func(some_module) # E: Argument 1 to "func" has incompatible type Module; expec # N: Protocol member My.a expected settable variable, got read-only attribute [file some_module.py] -from typing_extensions import Final +from typing import Final a: Final = 1 [builtins fixtures/module.pyi] @@ -4177,7 +4199,7 @@ class WriteToMeOrReadFromMe(WriteToMe[AnyStr], SupportsRead[AnyStr]): ... copyfileobj(WriteToMeOrReadFromMe[bytes](), WriteToMe[bytes]()) -[case testOverloadedMethodWithExplictSelfTypes] +[case testOverloadedMethodWithExplicitSelfTypes] from typing import Generic, overload, Protocol, TypeVar, Union AnyStr = TypeVar("AnyStr", str, bytes) @@ -4209,9 +4231,446 @@ 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" + f(a, b) # E: Cannot infer value of type parameter "AnyStr" of "f" def g4(a: Input[bytes], b: Output[str]) -> None: - f(a, b) # E: Cannot infer type argument 1 of "f" + f(a, b) # E: Cannot infer value of type parameter "AnyStr" 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] + +[case testSetterPropertyProtocolSubtypingBoth] +from typing import Protocol + +class B1: ... +class C1(B1): ... +class B2: ... +class C2(B2): ... + +class P1(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class P2(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: B2) -> None: ... + +class A1: + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class A2: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class A3: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: str) -> None: ... + +class A4: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... + +def f1(x: P1) -> None: ... +def f2(x: P2) -> None: ... + +a1: A1 +a2: A2 +a3: A3 +a4: A4 + +f1(a1) +f1(a2) +f1(a3) # E: Argument 1 to "f1" has incompatible type "A3"; expected "P1" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "C2", got "str" +f1(a4) # E: Argument 1 to "f1" has incompatible type "A4"; expected "P1" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "C2", got "str" + +f2(a1) # E: Argument 1 to "f2" has incompatible type "A1"; expected "P2" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected setter type "B2", got "C2" \ + # N: Setter types should behave contravariantly +f2(a2) # E: Argument 1 to "f2" has incompatible type "A2"; expected "P2" \ + # N: Following member(s) of "A2" have conflicts: \ + # N: foo: expected setter type "B2", got "C2" \ + # N: Setter types should behave contravariantly +f2(a3) # E: Argument 1 to "f2" has incompatible type "A3"; expected "P2" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "B2", got "str" +f2(a4) # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "B2", got "str" +[builtins fixtures/property.pyi] + +[case testSetterPropertyProtocolSubtypingVarSuper] +from typing import Protocol + +class B1: ... +class C1(B1): ... + +class P1(Protocol): + foo: B1 + +class P2(Protocol): + foo: C1 + +class A1: + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C1) -> None: ... + +class A2: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: B1) -> None: ... + +class A3: + @property + def foo(self) -> C1: ... + @foo.setter + def foo(self, x: str) -> None: ... + +class A4: + @property + def foo(self) -> str: ... + @foo.setter + def foo(self, x: str) -> None: ... + +def f1(x: P1) -> None: ... +def f2(x: P2) -> None: ... + +a1: A1 +a2: A2 +a3: A3 +a4: A4 + +f1(a1) # E: Argument 1 to "f1" has incompatible type "A1"; expected "P1" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected setter type "B1", got "C1" \ + # N: Setter types should behave contravariantly +f1(a2) +f1(a3) # E: Argument 1 to "f1" has incompatible type "A3"; expected "P1" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "B1", got "str" +f1(a4) # E: Argument 1 to "f1" has incompatible type "A4"; expected "P1" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" + +f2(a1) # E: Argument 1 to "f2" has incompatible type "A1"; expected "P2" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected "C1", got "B1" +f2(a2) +f2(a3) # E: Argument 1 to "f2" has incompatible type "A3"; expected "P2" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected setter type "C1", got "str" +f2(a4) # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "C1", got "str" +[builtins fixtures/property.pyi] + +[case testSetterPropertyProtocolSubtypingVarSub] +from typing import Protocol + +class B1: ... +class C1(B1): ... +class B2: ... +class C2(B2): ... + +class P1(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C2) -> None: ... + +class P2(Protocol): + @property + def foo(self) -> B1: ... + @foo.setter + def foo(self, x: C1) -> None: ... + +class A1: + foo: B1 + +class A2: + foo: B2 + +class A3: + foo: C2 + +class A4: + foo: str + +def f1(x: P1) -> None: ... +def f2(x: P2) -> None: ... + +a1: A1 +a2: A2 +a3: A3 +a4: A4 + +f1(a1) # E: Argument 1 to "f1" has incompatible type "A1"; expected "P1" \ + # N: Following member(s) of "A1" have conflicts: \ + # N: foo: expected setter type "C2", got "B1" +f1(a2) # E: Argument 1 to "f1" has incompatible type "A2"; expected "P1" \ + # N: Following member(s) of "A2" have conflicts: \ + # N: foo: expected "B1", got "B2" +f1(a3) # E: Argument 1 to "f1" has incompatible type "A3"; expected "P1" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected "B1", got "C2" +f1(a4) # E: Argument 1 to "f1" has incompatible type "A4"; expected "P1" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "C2", got "str" + +f2(a1) +f2(a2) # E: Argument 1 to "f2" has incompatible type "A2"; expected "P2" \ + # N: Following member(s) of "A2" have conflicts: \ + # N: foo: expected "B1", got "B2" \ + # N: foo: expected setter type "C1", got "B2" +f2(a3) # E: Argument 1 to "f2" has incompatible type "A3"; expected "P2" \ + # N: Following member(s) of "A3" have conflicts: \ + # N: foo: expected "B1", got "C2" \ + # N: foo: expected setter type "C1", got "C2" +f2(a4) # E: Argument 1 to "f2" has incompatible type "A4"; expected "P2" \ + # N: Following member(s) of "A4" have conflicts: \ + # N: foo: expected "B1", got "str" \ + # N: foo: expected setter type "C1", got "str" +[builtins fixtures/property.pyi] + + +[case testExplicitProtocolJoinPreference] +from typing import Protocol, TypeVar + +T = TypeVar("T") + +class Proto1(Protocol): + def foo(self) -> int: ... +class Proto2(Proto1): + def bar(self) -> str: ... +class Proto3(Proto2): + def baz(self) -> str: ... + +class Base: ... + +class A(Base, Proto3): ... +class B(Base, Proto3): ... + +def join(a: T, b: T) -> T: ... + +def main(a: A, b: B) -> None: + reveal_type(join(a, b)) # N: Revealed type is "__main__.Proto3" + reveal_type(join(b, a)) # N: Revealed type is "__main__.Proto3" + +[case testProtocolImplementationWithDescriptors] +from typing import Any, Protocol + +class Descr: + def __get__(self, inst: Any, owner: Any) -> int: ... + +class DescrBad: + def __get__(self, inst: Any, owner: Any) -> str: ... + +class Proto(Protocol): + x: int + +class C: + x = Descr() + +class CBad: + x = DescrBad() + +a: Proto = C() +b: Proto = CBad() # E: Incompatible types in assignment (expression has type "CBad", variable has type "Proto") \ + # N: Following member(s) of "CBad" have conflicts: \ + # N: x: expected "int", got "str" + +[case testProtocolCheckDefersNode] +from typing import Any, Callable, Protocol + +class Proto(Protocol): + def f(self) -> int: + ... + +def defer(f: Callable[[Any], int]) -> Callable[[Any], str]: + ... + +def bad() -> Proto: + return Impl() # E: Incompatible return value type (got "Impl", expected "Proto") \ + # N: Following member(s) of "Impl" have conflicts: \ + # N: Expected: \ + # N: def f(self) -> int \ + # N: Got: \ + # N: def f() -> str \ + +class Impl: + @defer + def f(self) -> int: ... + +[case testInferCallableProtoWithAnySubclass] +from typing import Any, Generic, Protocol, TypeVar + +T = TypeVar("T", covariant=True) + +Unknown: Any +class Mock(Unknown): + def __init__(self, **kwargs: Any) -> None: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + +class Factory(Protocol[T]): + def __call__(self, **kwargs: Any) -> T: ... + + +class Test(Generic[T]): + def __init__(self, f: Factory[T]) -> None: + ... + +t = Test(Mock()) +reveal_type(t) # N: Revealed type is "__main__.Test[Any]" +[builtins fixtures/dict.pyi] + +[case testProtocolClassObjectDescriptor] +from typing import Any, Protocol, overload + +class Desc: + @overload + def __get__(self, instance: None, owner: Any) -> Desc: ... + @overload + def __get__(self, instance: object, owner: Any) -> int: ... + def __get__(self, instance, owner): + pass + +class HasDesc(Protocol): + attr: Desc + +class HasInt(Protocol): + attr: int + +class C: + attr = Desc() + +x: HasInt = C() +y: HasDesc = C +z: HasInt = C # E: Incompatible types in assignment (expression has type "type[C]", variable has type "HasInt") \ + # N: Following member(s) of "C" have conflicts: \ + # N: attr: expected "int", got "Desc" + +[case testProtocolErrorReportingNoDuplicates] +from typing import Callable, Protocol, TypeVar + +class P(Protocol): + def meth(self) -> int: ... + +class C: + def meth(self) -> str: ... + +def foo() -> None: + c: P = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P") \ + # N: Following member(s) of "C" have conflicts: \ + # N: Expected: \ + # N: def meth(self) -> int \ + # N: Got: \ + # N: def meth(self) -> str + x = defer() + +T = TypeVar("T") +def deco(fn: Callable[[], T]) -> Callable[[], list[T]]: ... + +@deco +def defer() -> int: ... +[builtins fixtures/list.pyi] + +[case testProtocolClassValDescriptor] +from typing import Any, Protocol, overload, ClassVar, Type + +class Desc: + @overload + def __get__(self, instance: None, owner: object) -> Desc: ... + @overload + def __get__(self, instance: object, owner: object) -> int: ... + def __get__(self, instance, owner): + pass + +class P(Protocol): + x: ClassVar[Desc] + +class C: + x = Desc() + +t: P = C() +reveal_type(t.x) # N: Revealed type is "builtins.int" +tt: Type[P] = C +reveal_type(tt.x) # N: Revealed type is "__main__.Desc" + +bad: P = C # E: Incompatible types in assignment (expression has type "type[C]", variable has type "P") \ + # N: Following member(s) of "C" have conflicts: \ + # N: x: expected "int", got "Desc" + +[case testProtocolClassValCallable] +from typing import Any, Protocol, overload, ClassVar, Type, Callable + +class P(Protocol): + foo: Callable[[object], int] + bar: ClassVar[Callable[[object], int]] + +class C: + foo: Callable[[object], int] + bar: ClassVar[Callable[[object], int]] + +t: P = C() +reveal_type(t.foo) # N: Revealed type is "def (builtins.object) -> builtins.int" +reveal_type(t.bar) # N: Revealed type is "def () -> builtins.int" +tt: Type[P] = C +reveal_type(tt.foo) # N: Revealed type is "def (builtins.object) -> builtins.int" +reveal_type(tt.bar) # N: Revealed type is "def (builtins.object) -> builtins.int" + +[case testProtocolDecoratedSelfBound] +from abc import abstractmethod +from typing import Protocol, Self + +class Proto(Protocol): + @abstractmethod + def foo(self, x: Self) -> None: ... + +class Impl: + def foo(self, x: Self) -> None: + pass + +x: Proto = Impl() diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index e7028a027e256..8bc781d091c3e 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -11,11 +11,30 @@ match m: -- Literal Pattern -- [case testMatchLiteralPatternNarrows] +# flags: --warn-unreachable m: object match m: case 1: reveal_type(m) # N: Revealed type is "Literal[1]" + case 2: + reveal_type(m) # N: Revealed type is "Literal[2]" + case other: + reveal_type(other) # N: Revealed type is "builtins.object" + +[case testMatchLiteralPatternNarrows2] +# flags: --warn-unreachable +from typing import Any + +m: Any + +match m: + case 1: + reveal_type(m) # N: Revealed type is "Literal[1]" + case 2: + reveal_type(m) # N: Revealed type is "Literal[2]" + case other: + reveal_type(other) # N: Revealed type is "Any" [case testMatchLiteralPatternAlreadyNarrower-skip] m: bool @@ -64,7 +83,7 @@ m: A match m: case b.b: - reveal_type(m) # N: Revealed type is "__main__.1" + reveal_type(m) # N: Revealed type is "__main__." [file b.py] class B: ... b: B @@ -240,7 +259,7 @@ match m: reveal_type(a) # N: Revealed type is "builtins.int" reveal_type(b) # N: Revealed type is "builtins.str" reveal_type(c) # N: Revealed type is "builtins.bool" - reveal_type(m) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" + reveal_type(m) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]" [builtins fixtures/list.pyi] [case testMatchSequencePatternTupleTooLong] @@ -270,7 +289,7 @@ m: Tuple[object, object] match m: case [1, "str"]: - reveal_type(m) # N: Revealed type is "Tuple[Literal[1], Literal['str']]" + reveal_type(m) # N: Revealed type is "tuple[Literal[1], Literal['str']]" [builtins fixtures/list.pyi] [case testMatchSequencePatternTupleStarred] @@ -282,7 +301,7 @@ match m: reveal_type(a) # N: Revealed type is "builtins.int" reveal_type(b) # N: Revealed type is "builtins.list[builtins.str]" reveal_type(c) # N: Revealed type is "builtins.bool" - reveal_type(m) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" + reveal_type(m) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]" [builtins fixtures/list.pyi] [case testMatchSequencePatternTupleStarredUnion] @@ -294,13 +313,13 @@ match m: reveal_type(a) # N: Revealed type is "builtins.int" reveal_type(b) # N: Revealed type is "builtins.list[Union[builtins.str, builtins.float]]" reveal_type(c) # N: Revealed type is "builtins.bool" - reveal_type(m) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.float, builtins.bool]" + reveal_type(m) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.float, builtins.bool]" [builtins fixtures/list.pyi] [case testMatchSequencePatternTupleStarredTooShort] from typing import Tuple m: Tuple[int] -reveal_type(m) # N: Revealed type is "Tuple[builtins.int]" +reveal_type(m) # N: Revealed type is "tuple[builtins.int]" match m: case [a, *b, c]: @@ -326,12 +345,60 @@ class Example: SubClass: type[Example] match [SubClass("a"), SubClass("b")]: - case [SubClass(value), *rest]: # E: Expected type in class pattern; found "Type[__main__.Example]" + case [SubClass(value), *rest]: # E: Expected type in class pattern; found "type[__main__.Example]" reveal_type(value) # E: Cannot determine type of "value" \ # N: Revealed type is "Any" reveal_type(rest) # N: Revealed type is "builtins.list[__main__.Example]" [builtins fixtures/tuple.pyi] +# Narrowing union-based values via a literal pattern on an indexed/attribute subject +# ------------------------------------------------------------------------------- +# Literal patterns against a union of types can be used to narrow the subject +# itself, not just the expression being matched. Previously, the patterns below +# failed to narrow the `d` variable, leading to errors for missing members; we +# now propagate the type information up to the parent. + +[case testMatchNarrowingUnionTypedDictViaIndex] +from typing import Literal, TypedDict + +class A(TypedDict): + tag: Literal["a"] + name: str + +class B(TypedDict): + tag: Literal["b"] + num: int + +d: A | B +match d["tag"]: + case "a": + reveal_type(d) # N: Revealed type is "TypedDict('__main__.A', {'tag': Literal['a'], 'name': builtins.str})" + reveal_type(d["name"]) # N: Revealed type is "builtins.str" + case "b": + reveal_type(d) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['b'], 'num': builtins.int})" + reveal_type(d["num"]) # N: Revealed type is "builtins.int" +[typing fixtures/typing-typeddict.pyi] + +[case testMatchNarrowingUnionClassViaAttribute] +from typing import Literal + +class A: + tag: Literal["a"] + name: str + +class B: + tag: Literal["b"] + num: int + +d: A | B +match d.tag: + case "a": + reveal_type(d) # N: Revealed type is "__main__.A" + reveal_type(d.name) # N: Revealed type is "builtins.str" + case "b": + reveal_type(d) # N: Revealed type is "__main__.B" + reveal_type(d.num) # N: Revealed type is "builtins.int" + [case testMatchSequenceUnion-skip] from typing import List, Union m: Union[List[List[str]], str] @@ -648,19 +715,39 @@ 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 match m: case xyz(y): # E: Name "xyz" is not defined reveal_type(m) # N: Revealed type is "Any" - reveal_type(y) # E: Cannot determine type of "y" \ - # N: Revealed type is "Any" + reveal_type(y) # N: Revealed type is "Any" match m: case xyz(z=x): # E: Name "xyz" is not defined - reveal_type(x) # E: Cannot determine type of "x" \ - # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Any" + case (xyz1() as n) | (xyz2(attr=n)): # E: Name "xyz1" is not defined \ + # E: Name "xyz2" is not defined + reveal_type(n) # N: Revealed type is "Any" [case testMatchClassPatternCaptureDataclass] from dataclasses import dataclass @@ -751,6 +838,21 @@ match m: reveal_type(j) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] +[case testMatchSequencePatternCaptureNamedTuple] +from typing import NamedTuple + +class N(NamedTuple): + x: int + y: str + +a = N(1, "a") + +match a: + case [x, y]: + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + [case testMatchClassPatternCaptureGeneric] from typing import Generic, TypeVar @@ -914,21 +1016,30 @@ m: B match m: case A(): - reveal_type(m) # N: Revealed type is "__main__.2" + reveal_type(m) # N: Revealed type is "__main__." case A(i, j): - reveal_type(m) # N: Revealed type is "__main__.3" + reveal_type(m) # N: Revealed type is "__main__." [builtins fixtures/tuple.pyi] [case testMatchClassPatternNonexistentKeyword] +from typing import Any class A: ... m: object +n: Any match m: case A(a=j): # E: Class "__main__.A" has no attribute "a" reveal_type(m) # N: Revealed type is "__main__.A" reveal_type(j) # N: Revealed type is "Any" +match n: + # Matching against object should not emit an error for non-existing keys + case object(a=k): + reveal_type(n) # N: Revealed type is "builtins.object" + reveal_type(n.a) # N: Revealed type is "Any" + reveal_type(k) # N: Revealed type is "Any" + [case testMatchClassPatternDuplicateKeyword] class A: a: str @@ -987,6 +1098,143 @@ match m: case Foo(): pass +[case testMatchClassPatternCallable] +# flags: --warn-unreachable +from typing import Callable, Any + +class FnImpl: + def __call__(self, x: object, /) -> int: ... + +def test_any(x: Any) -> None: + match x: + case Callable() as fn: + reveal_type(fn) # N: Revealed type is "def (*Any, **Any) -> Any" + case other: + reveal_type(other) # N: Revealed type is "Any" + +def test_object(x: object) -> None: + match x: + case Callable() as fn: + reveal_type(fn) # N: Revealed type is "def (*Any, **Any) -> Any" + case other: + reveal_type(other) # N: Revealed type is "builtins.object" + +def test_impl(x: FnImpl) -> None: + match x: + case Callable() as fn: + reveal_type(fn) # N: Revealed type is "__main__.FnImpl" + case other: + reveal_type(other) # E: Statement is unreachable + +def test_callable(x: Callable[[object], int]) -> None: + match x: + case Callable() as fn: + reveal_type(fn) # N: Revealed type is "def (builtins.object) -> builtins.int" + case other: + reveal_type(other) # E: Statement is unreachable + +[case testMatchClassPatternCallbackProtocol] +# flags: --warn-unreachable +from typing import Any, Callable +from typing_extensions import Protocol, runtime_checkable + +@runtime_checkable +class FnProto(Protocol): + def __call__(self, x: int, /) -> object: ... + +class FnImpl: + def __call__(self, x: object, /) -> int: ... + +def test_any(x: Any) -> None: + match x: + case FnProto() as fn: + reveal_type(fn) # N: Revealed type is "__main__.FnProto" + case other: + reveal_type(other) # N: Revealed type is "Any" + +def test_object(x: object) -> None: + match x: + case FnProto() as fn: + reveal_type(fn) # N: Revealed type is "__main__.FnProto" + case other: + reveal_type(other) # N: Revealed type is "builtins.object" + +def test_impl(x: FnImpl) -> None: + match x: + case FnProto() as fn: + reveal_type(fn) # N: Revealed type is "__main__.FnImpl" + case other: + reveal_type(other) # E: Statement is unreachable + +def test_callable(x: Callable[[object], int]) -> None: + match x: + case FnProto() as fn: + reveal_type(fn) # N: Revealed type is "def (builtins.object) -> builtins.int" + case other: + reveal_type(other) # E: Statement is unreachable + +[builtins fixtures/dict.pyi] + +[case testMatchClassPatternAnyCallableProtocol] +# flags: --warn-unreachable +from typing import Any, Callable +from typing_extensions import Protocol, runtime_checkable + +@runtime_checkable +class AnyCallable(Protocol): + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + +class FnImpl: + def __call__(self, x: object, /) -> int: ... + +def test_object(x: object) -> None: + match x: + case AnyCallable() as fn: + reveal_type(fn) # N: Revealed type is "__main__.AnyCallable" + case other: + reveal_type(other) # N: Revealed type is "builtins.object" + +def test_impl(x: FnImpl) -> None: + match x: + case AnyCallable() as fn: + reveal_type(fn) # N: Revealed type is "__main__.FnImpl" + case other: + reveal_type(other) # E: Statement is unreachable + +def test_callable(x: Callable[[object], int]) -> None: + match x: + case AnyCallable() as fn: + reveal_type(fn) # N: Revealed type is "def (builtins.object) -> builtins.int" + case other: + reveal_type(other) # E: Statement is unreachable + +[builtins fixtures/dict.pyi] + + +[case testMatchClassPatternProtocol] +from typing import Any +from typing_extensions import Protocol, runtime_checkable + +@runtime_checkable +class Proto(Protocol): + def foo(self, x: int, /) -> object: ... + +class Impl: + def foo(self, x: object, /) -> int: ... + +def test_object(x: object) -> None: + match x: + case Proto() as y: + reveal_type(y) # N: Revealed type is "__main__.Proto" + +def test_impl(x: Impl) -> None: + match x: + case Proto() as y: + reveal_type(y) # N: Revealed type is "__main__.Impl" + +[builtins fixtures/dict.pyi] + + [case testMatchClassPatternNestedGenerics] # From cpython test_patma.py x = [[{0: 0}]] @@ -1122,13 +1370,13 @@ match m: case 1 | "foo": reveal_type(m) # N: Revealed type is "Union[Literal[1], Literal['foo']]" -[case testMatchOrPatterCapturesMissing] +[case testMatchOrPatternCapturesMissing] from typing import List m: List[int] match m: case [x, y] | list(x): # E: Alternative patterns bind different names - reveal_type(x) # N: Revealed type is "builtins.object" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.list[builtins.int]]" reveal_type(y) # N: Revealed type is "builtins.int" [builtins fixtures/list.pyi] @@ -1137,7 +1385,7 @@ m: object match m: case list(x) | dict(x): - reveal_type(x) # N: Revealed type is "typing.Iterable[Any]" + reveal_type(x) # N: Revealed type is "Union[builtins.list[Any], builtins.dict[Any, Any]]" [builtins fixtures/dict.pyi] -- Interactions -- @@ -1220,7 +1468,7 @@ def main() -> None: case a: reveal_type(a) # N: Revealed type is "builtins.int" -[case testMatchCapturePatternFromAsyncFunctionReturningUnion-xfail] +[case testMatchCapturePatternFromAsyncFunctionReturningUnion] async def func1(arg: bool) -> str | int: ... async def func2(arg: bool) -> bytes | int: ... @@ -1265,7 +1513,7 @@ m: str match m: case a if a := 1: # E: Incompatible types in assignment (expression has type "int", variable has type "str") - reveal_type(a) # N: Revealed type is "Never" + reveal_type(a) # N: Revealed type is "Literal[1]?" [case testMatchAssigningPatternGuard] m: str @@ -1290,7 +1538,7 @@ m: A match m: case a if isinstance(a, B): - reveal_type(a) # N: Revealed type is "__main__." + reveal_type(a) # N: Revealed type is "__main__." [builtins fixtures/isinstancelist.pyi] [case testMatchUnreachablePatternGuard] @@ -1301,6 +1549,16 @@ match m: reveal_type(a) [builtins fixtures/isinstancelist.pyi] +[case testMatchSubjectAssignExprWithGuard] +from typing import Optional +def func() -> Optional[str]: ... + +match m := func(): + case _ if not m: + reveal_type(m) # N: Revealed type is "Union[Literal[''], None]" + case _: + reveal_type(m) # N: Revealed type is "builtins.str" + -- Exhaustiveness -- [case testMatchUnionNegativeNarrowing] @@ -1323,7 +1581,7 @@ m: Union[str, bytes, int] match m: case str(a) | bytes(a): - reveal_type(a) # N: Revealed type is "builtins.object" + reveal_type(a) # N: Revealed type is "Union[builtins.str, builtins.bytes]" reveal_type(m) # N: Revealed type is "Union[builtins.str, builtins.bytes]" case b: reveal_type(b) # N: Revealed type is "builtins.int" @@ -1423,8 +1681,7 @@ def f(value: Literal[1] | Literal[2]) -> int: [typing fixtures/typing-medium.pyi] [case testMatchSequencePatternNegativeNarrowing] -from typing import Union, Sequence, Tuple -from typing_extensions import Literal +from typing import Literal, Union, Sequence, Tuple m1: Sequence[int | str] @@ -1438,41 +1695,43 @@ m2: Tuple[int | str] match m2: case (int(),): - reveal_type(m2) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(m2) # N: Revealed type is "tuple[builtins.int]" case r2: - reveal_type(m2) # N: Revealed type is "Tuple[builtins.str]" + reveal_type(m2) # N: Revealed type is "tuple[builtins.str]" m3: Tuple[Union[int, str]] match m3: case (1,): - reveal_type(m3) # N: Revealed type is "Tuple[Literal[1]]" + 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]]" + 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]]" + 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]]" + 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]" + 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']]]" + 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']]]" + reveal_type(m5) # N: Revealed type is "tuple[Literal[2], Union[Literal['a'], Literal['b']]]" -match m5: +m6: Tuple[Literal[1, 2], Literal["a", "b"]] + +match m6: case (1, "a"): - reveal_type(m5) # N: Revealed type is "Tuple[Literal[1], Literal['a']]" + reveal_type(m6) # 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']]]" + reveal_type(m6) # N: Revealed type is "tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]" [builtins fixtures/tuple.pyi] @@ -1524,6 +1783,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 @@ -1541,6 +1856,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: @@ -1618,6 +1934,23 @@ 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 testMatchNarrowDownUnionUsingClassPattern] + +class Foo: ... +class Bar(Foo): ... + +def test_1(bar: Bar) -> None: + match bar: + case Foo() as foo: + reveal_type(foo) # N: Revealed type is "__main__.Bar" + +def test_2(bar: Bar | str) -> None: + match bar: + case Foo() as foo: + reveal_type(foo) # N: Revealed type is "__main__.Bar" + [case testMatchAssertFalseToSilenceFalsePositives] class C: @@ -1648,6 +1981,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 @@ -1656,10 +2005,10 @@ class C: pass def f(x: A) -> None: match x: case B() as y: - reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "__main__." case C() as y: - reveal_type(y) # N: Revealed type is "__main__." - reveal_type(y) # N: Revealed type is "Union[__main__., __main__.]" + reveal_type(y) # N: Revealed type is "__main__." + reveal_type(y) # N: Revealed type is "Union[__main__., __main__.]" [case testMatchWithBreakAndContinue] def f(x: int | str | None) -> None: @@ -1739,9 +2088,9 @@ class AnnAssign(stmt): value: str simple: int -reveal_type(AST.__match_args__) # N: Revealed type is "Tuple[()]" -reveal_type(stmt.__match_args__) # N: Revealed type is "Tuple[()]" -reveal_type(AnnAssign.__match_args__) # N: Revealed type is "Tuple[Literal['target']?, Literal['annotation']?, Literal['value']?, Literal['simple']?]" +reveal_type(AST.__match_args__) # N: Revealed type is "tuple[()]" +reveal_type(stmt.__match_args__) # N: Revealed type is "tuple[()]" +reveal_type(AnnAssign.__match_args__) # N: Revealed type is "tuple[Literal['target']?, Literal['annotation']?, Literal['value']?, Literal['simple']?]" AnnAssign.__match_args__ = ('a', 'b', 'c', 'd') # E: Cannot assign to "__match_args__" __match_args__ = 0 @@ -1812,8 +2161,7 @@ match var: [builtins fixtures/tuple.pyi] [case testMatchNamedAndKeywordsAreTheSame] -from typing import Generic, TypeVar, Union -from typing_extensions import Final +from typing import Generic, Final, TypeVar, Union from dataclasses import dataclass T = TypeVar("T") @@ -1822,7 +2170,7 @@ class Regular: x: str y: int __match_args__ = ("x",) -class ReveresedOrder: +class ReversedOrder: x: int y: str __match_args__ = ("y",) @@ -1840,7 +2188,7 @@ class GenericDataclass(Generic[T]): input_arg: Union[ Regular, - ReveresedOrder, + ReversedOrder, GenericRegular[str], GenericWithFinal[str], RegularSubtype, @@ -1851,7 +2199,7 @@ input_arg: Union[ match input_arg: case Regular(a): reveal_type(a) # N: Revealed type is "builtins.str" - case ReveresedOrder(a): + case ReversedOrder(a): reveal_type(a) # N: Revealed type is "builtins.str" case GenericWithFinal(a): reveal_type(a) # N: Revealed type is "builtins.str" @@ -1866,7 +2214,7 @@ match input_arg: match input_arg: case Regular(x=a): reveal_type(a) # N: Revealed type is "builtins.str" - case ReveresedOrder(x=b): # Order is different + case ReversedOrder(x=b): # Order is different reveal_type(b) # N: Revealed type is "builtins.int" case GenericWithFinal(x=a): reveal_type(a) # N: Revealed type is "builtins.str" @@ -1885,12 +2233,12 @@ S = TypeVar("S", int, str) def my_func(pairs: Iterable[tuple[S, S]]) -> None: for pair in pairs: - reveal_type(pair) # N: Revealed type is "Tuple[builtins.int, builtins.int]" \ - # N: Revealed type is "Tuple[builtins.str, builtins.str]" + reveal_type(pair) # N: Revealed type is "tuple[builtins.int, builtins.int]" \ + # N: Revealed type is "tuple[builtins.str, builtins.str]" match pair: case _: - reveal_type(pair) # N: Revealed type is "Tuple[builtins.int, builtins.int]" \ - # N: Revealed type is "Tuple[builtins.str, builtins.str]" + reveal_type(pair) # N: Revealed type is "tuple[builtins.int, builtins.int]" \ + # N: Revealed type is "tuple[builtins.str, builtins.str]" [builtins fixtures/tuple.pyi] [case testPossiblyUndefinedMatch] @@ -2023,9 +2371,11 @@ def f() -> None: match x := returns_a_or_none(): case A(): reveal_type(x.a) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Union[__main__.A, None]" match x := returns_a(): case A(): reveal_type(x.a) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "__main__.A" y = returns_a_or_none() match y: case A(): @@ -2080,7 +2430,7 @@ def match_stmt_error4(x: Optional[list[str]]) -> None: if x is None: x = ["a"] def nested() -> list[str]: - return x # E: Incompatible return value type (got "Optional[List[str]]", expected "List[str]") + return x # E: Incompatible return value type (got "Optional[list[str]]", expected "list[str]") match ["a"]: case [*x]: pass @@ -2289,3 +2639,558 @@ 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] + +[case testMatchSequenceReachableFromAny] +# flags: --warn-unreachable +from typing import Any + +def maybe_list(d: Any) -> int: + match d: + case []: + return 0 + case [[_]]: + return 1 + case [_]: + return 1 + case _: + return 2 + +def with_guard(d: Any) -> None: + match d: + case [s] if isinstance(s, str): + reveal_type(s) # N: Revealed type is "builtins.str" + match d: + case (s,) if isinstance(s, str): + reveal_type(s) # N: Revealed type is "builtins.str" + +def nested_in_dict(d: dict[str, Any]) -> int: + match d: + case {"src": ["src"]}: + return 1 + case _: + return 0 + +[builtins fixtures/dict.pyi] + +[case testMatchRebindsOuterFunctionName] +# flags: --warn-unreachable +from typing import Literal + +def x() -> tuple[Literal["test"]]: ... + +match x(): + case (x,) if x == "test": # E: Incompatible types in capture pattern (pattern captures type "Literal['test']", variable has type "Callable[[], tuple[Literal['test']]]") + reveal_type(x) # N: Revealed type is "def () -> tuple[Literal['test']]" + case foo: + foo + +[builtins fixtures/dict.pyi] + +[case testMatchRebindsInnerFunctionName] +# flags: --warn-unreachable +class Some: + value: int | str + __match_args__ = ("value",) + +def fn1(x: Some | int | str) -> None: + match x: + case int(): + def value(): + return 1 + reveal_type(value) # N: Revealed type is "def () -> Any" + case str(): + def value(): + return 1 + reveal_type(value) # N: Revealed type is "def () -> Any" + case Some(value): # E: Incompatible types in capture pattern (pattern captures type "Union[int, str]", variable has type "Callable[[], Any]") + pass + +def fn2(x: Some | int | str) -> None: + match x: + case int(): + def value() -> str: + return "" + reveal_type(value) # N: Revealed type is "def () -> builtins.str" + case str(): + def value() -> int: # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def value() -> str \ + # N: Redefinition: \ + # N: def value() -> int + return 1 + reveal_type(value) # N: Revealed type is "def () -> builtins.str" + case Some(value): # E: Incompatible types in capture pattern (pattern captures type "Union[int, str]", variable has type "Callable[[], str]") + pass +[builtins fixtures/dict.pyi] + +[case testMatchFunctionCall] +# flags: --warn-unreachable + +def fn() -> int | str: ... + +match fn(): + case str(s): + reveal_type(s) # N: Revealed type is "builtins.str" + case int(i): + reveal_type(i) # N: Revealed type is "builtins.int" + case other: + other # E: Statement is unreachable + +[case testMatchAttribute] +# flags: --warn-unreachable + +class A: + foo: int | str + +match A().foo: + case str(s): + reveal_type(s) # N: Revealed type is "builtins.str" + case int(i): + reveal_type(i) # N: Revealed type is "builtins.int" + case other: + other # E: Statement is unreachable + +[case testMatchLiteral] +# flags: --warn-unreachable + +def int_literal() -> None: + match 12: + case 1 as s: + reveal_type(s) # N: Revealed type is "Literal[1]" + case int(i): + reveal_type(i) # N: Revealed type is "Literal[12]?" + case other: + other # E: Statement is unreachable + +def str_literal() -> None: + match 'foo': + case 'a' as s: + reveal_type(s) # N: Revealed type is "Literal['a']" + case str(i): + reveal_type(i) # N: Revealed type is "Literal['foo']?" + case other: + other # E: Statement is unreachable + +[case testMatchOperations] +# flags: --warn-unreachable + +x: int +match -x: + case -1 as s: + reveal_type(s) # N: Revealed type is "Literal[-1]" + case int(s): + reveal_type(s) # N: Revealed type is "builtins.int" + case other: + other # E: Statement is unreachable + +match 1 + 2: + case 3 as s: + reveal_type(s) # N: Revealed type is "Literal[3]" + case int(s): + reveal_type(s) # N: Revealed type is "builtins.int" + case other: + other # E: Statement is unreachable + +match 1 > 2: + case True as s: + reveal_type(s) # N: Revealed type is "Literal[True]" + case False as s: + reveal_type(s) # N: Revealed type is "Literal[False]" + case other: + other # E: Statement is unreachable +[builtins fixtures/ops.pyi] + +[case testMatchDictItem] +# flags: --warn-unreachable + +m: dict[str, int | str] +k: str + +match m[k]: + case str(s): + reveal_type(s) # N: Revealed type is "builtins.str" + case int(i): + reveal_type(i) # N: Revealed type is "builtins.int" + case other: + other # E: Statement is unreachable + +[builtins fixtures/dict.pyi] + +[case testMatchLiteralValuePathological] +# flags: --warn-unreachable + +match 0: + case 0 as i: + reveal_type(i) # N: Revealed type is "Literal[0]?" + case int(i): + i # E: Statement is unreachable + case other: + other # E: Statement is unreachable + +[case testMatchNamedTupleSequence] +from typing import Any, NamedTuple + +class T(NamedTuple): + t: list[Any] + +class K(NamedTuple): + k: int + +def f(t: T) -> None: + match t: + case T([K() as k]): + reveal_type(k) # N: Revealed type is "tuple[builtins.int, fallback=__main__.K]" +[builtins fixtures/tuple.pyi] + +[case testMatchTypeObjectTypeVar] +# flags: --warn-unreachable +from typing import TypeVar +import b + +T_Choice = TypeVar("T_Choice", bound=b.One | b.Two) + +def switch(choice: type[T_Choice]) -> None: + match choice: + case b.One: + reveal_type(choice) # N: Revealed type is "def () -> b.One" + case b.Two: + reveal_type(choice) # N: Revealed type is "def () -> b.Two" + case _: + reveal_type(choice) # N: Revealed type is "type[T_Choice`-1]" + +[file b.py] +class One: ... +class Two: ... + +[builtins fixtures/tuple.pyi] + +[case testNewRedefineMatchBasics] +# flags: --allow-redefinition-new --local-partial-types + +def f1(x: int | str | list[bytes]) -> None: + match x: + case int(): + reveal_type(x) # N: Revealed type is "builtins.int" + case str(y): + reveal_type(y) # N: Revealed type is "builtins.str" + case [y]: + reveal_type(y) # N: Revealed type is "builtins.bytes" + reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.bytes]" + +[case testNewRedefineLoopWithMatch] +# flags: --allow-redefinition-new --local-partial-types + +def f1() -> None: + while True: + x = object() + match x: + case str(y): + pass + case int(): + pass + if int(): + continue + +def f2() -> None: + for x in [""]: + match str(): + case "a": + y = "" + case "b": + y = 1 + return + reveal_type(y) # N: Revealed type is "builtins.str" +[builtins fixtures/list.pyi] + +[case testExhaustiveMatchNoFlag] + +a: int = 5 +match a: + case 1: + pass + case _: + pass + +b: str = "hello" +match b: + case "bye": + pass + case _: + pass + +[case testNonExhaustiveMatchNoFlag] + +a: int = 5 +match a: + case 1: + pass + +b: str = "hello" +match b: + case "bye": + pass + + +[case testExhaustiveMatchWithFlag] +# flags: --enable-error-code exhaustive-match + +a: int = 5 +match a: + case 1: + pass + case _: + pass + +b: str = "hello" +match b: + case "bye": + pass + case _: + pass + +[case testNonExhaustiveMatchWithFlag] +# flags: --enable-error-code exhaustive-match + +a: int = 5 +match a: # E: Match statement has unhandled case for values of type "int" \ + # N: If match statement is intended to be non-exhaustive, add `case _: pass` + case 1: + pass + +b: str = "hello" +match b: # E: Match statement has unhandled case for values of type "str" \ + # N: If match statement is intended to be non-exhaustive, add `case _: pass` + case "bye": + pass +[case testNonExhaustiveMatchEnumWithFlag] +# flags: --enable-error-code exhaustive-match + +import enum + +class Color(enum.Enum): + RED = 1 + BLUE = 2 + GREEN = 3 + +val: Color = Color.RED + +match val: # E: Match statement has unhandled case for values of type "Literal[Color.GREEN]" \ + # N: If match statement is intended to be non-exhaustive, add `case _: pass` + case Color.RED: + a = "red" + case Color.BLUE: + a= "blue" +[builtins fixtures/enum.pyi] + +[case testExhaustiveMatchEnumWithFlag] +# flags: --enable-error-code exhaustive-match + +import enum + +class Color(enum.Enum): + RED = 1 + BLUE = 2 + +val: Color = Color.RED + +match val: + case Color.RED: + a = "red" + case Color.BLUE: + a= "blue" +[builtins fixtures/enum.pyi] + +[case testNonExhaustiveMatchEnumMultipleMissingMatchesWithFlag] +# flags: --enable-error-code exhaustive-match + +import enum + +class Color(enum.Enum): + RED = 1 + BLUE = 2 + GREEN = 3 + +val: Color = Color.RED + +match val: # E: Match statement has unhandled case for values of type "Literal[Color.BLUE, Color.GREEN]" \ + # N: If match statement is intended to be non-exhaustive, add `case _: pass` + case Color.RED: + a = "red" +[builtins fixtures/enum.pyi] + +[case testExhaustiveMatchEnumFallbackWithFlag] +# flags: --enable-error-code exhaustive-match + +import enum + +class Color(enum.Enum): + RED = 1 + BLUE = 2 + GREEN = 3 + +val: Color = Color.RED + +match val: + case Color.RED: + a = "red" + case _: + a = "other" +[builtins fixtures/enum.pyi] + +# Fork of testMatchNarrowingUnionTypedDictViaIndex to check behaviour with exhaustive match flag +[case testExhaustiveMatchNarrowingUnionTypedDictViaIndex] +# flags: --enable-error-code exhaustive-match + +from typing import Literal, TypedDict + +class A(TypedDict): + tag: Literal["a"] + name: str + +class B(TypedDict): + tag: Literal["b"] + num: int + +d: A | B +match d["tag"]: # E: Match statement has unhandled case for values of type "Literal['b']" \ + # N: If match statement is intended to be non-exhaustive, add `case _: pass` \ + # E: Match statement has unhandled case for values of type "B" + case "a": + reveal_type(d) # N: Revealed type is "TypedDict('__main__.A', {'tag': Literal['a'], 'name': builtins.str})" + reveal_type(d["name"]) # N: Revealed type is "builtins.str" +[typing fixtures/typing-typeddict.pyi] + +[case testEnumTypeObjectMember] +import enum +from typing import NoReturn + +def assert_never(x: NoReturn) -> None: ... + +class ValueType(enum.Enum): + INT = int + STR = str + +value_type: ValueType = ValueType.INT + +match value_type: + case ValueType.INT: + pass + case ValueType.STR: + pass + case _: + assert_never(value_type) +[builtins fixtures/tuple.pyi] + +[case testAssignmentToFinalInMatchCaseNotAllowed] +from typing import Final + +FOO: Final[int] = 10 + +val: int = 8 +match val: + case FOO: # E: Cannot assign to final name "FOO" + pass + +[case testMatchExhaustivenessWithDeferral] +# flags: --enable-error-code exhaustive-match +from typing import Literal +import unknown_module # E: Cannot find implementation or library stub for module named "unknown_module" \ + # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports + +def foo(e: Literal[0, 1]) -> None: + match e: + case 0: + defer + case 1: + ... + +defer = unknown_module.foo + +[case testMatchErrorsIncorrectName] +class A: + pass + +match 5: + case A.blah(): # E: "type[A]" has no attribute "blah" + pass + +[case testMatchAllowsAnyClassArgsForAny] +match 5: + case BlahBlah(a, b): # E: Name "BlahBlah" is not defined + reveal_type(a) # N: Revealed type is "Any" + reveal_type(b) # N: Revealed type is "Any" + case BlahBlah(c=c): # E: Name "BlahBlah" is not defined + reveal_type(c) # N: Revealed type is "Any" + +[case testMatchAllowsNoneTypeAsClass] +import types + +class V: + X = types.NoneType + +def fun(val: str | None): + match val: + case V.X(): + reveal_type(val) # N: Revealed type is "None" + + match val: + case types.NoneType(): + reveal_type(val) # N: Revealed type is "None" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 28951824999f8..c2a0bb09810aa 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -81,9 +81,9 @@ reveal_type(coro) # N: Revealed type is "def () -> typing.Coroutine[Any, Any, t [case testTypeVarTupleNewSyntaxAnnotations] Ints = tuple[int, int, int] x: tuple[str, *Ints] -reveal_type(x) # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.int, builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.str, builtins.int, builtins.int, builtins.int]" y: tuple[int, *tuple[int, ...]] -reveal_type(y) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(y) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleNewSyntaxGenerics] @@ -95,8 +95,8 @@ class C(Generic[T, *Ts]): attr: tuple[int, *Ts, str] def test(self) -> None: - reveal_type(self.attr) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`2], builtins.str]" - self.attr = ci # E: Incompatible types in assignment (expression has type "C[*Tuple[int, ...]]", variable has type "Tuple[int, *Ts, str]") + reveal_type(self.attr) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`2], builtins.str]" + self.attr = ci # E: Incompatible types in assignment (expression has type "C[*tuple[int, ...]]", variable has type "tuple[int, *Ts, str]") def meth(self, *args: *Ts) -> T: ... ci: C[*tuple[int, ...]] @@ -135,11 +135,11 @@ myclass1 = MyClass(float) reveal_type(myclass1) # N: Revealed type is "__main__.MyClass[builtins.float, None]" myclass2 = MyClass(float, float) reveal_type(myclass2) # N: Revealed type is "__main__.MyClass[builtins.float, builtins.float]" -myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" matches argument types "Type[float]", "Type[float]", "Type[float]" \ +myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" matches argument types "type[float]", "type[float]", "type[float]" \ # N: Possible overload variants: \ # N: def [T1, T2] __init__(self) -> MyClass[None, None] \ - # N: def [T1, T2] __init__(self, Type[T1], /) -> MyClass[T1, None] \ - # N: def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2] + # N: def [T1, T2] __init__(self, type[T1], /) -> MyClass[T1, None] \ + # N: def [T1, T2] __init__(type[T1], type[T2], /) -> MyClass[T1, T2] reveal_type(myclass3) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] @@ -157,7 +157,7 @@ Alias1 = Callable[[*Ts], int] # E: Variable "__main__.Ts" is not valid as a typ x1: Alias1[int] # E: Bad number of arguments for type alias, expected 0, given 1 reveal_type(x1) # N: Revealed type is "def (*Any) -> builtins.int" x1 = good -x1 = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]") +x1 = bad # E: Incompatible types in assignment (expression has type "def bad(*x: int, y: int) -> int", variable has type "def (*Any) -> int") Alias2 = Callable[[*T], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple) x2: Alias2[int] @@ -169,7 +169,155 @@ x3: Alias3[int] # E: Bad number of arguments for type alias, expected 0, given reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int" IntList = List[int] -Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple) +Alias4 = Callable[[*IntList], int] # E: "list[int]" cannot be unpacked (must be tuple or TypeVarTuple) 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] + +[case testLambdaInExceptStarBlock] +# flags: --python-version 3.11 +def foo(): + try: + pass + except* Exception: + x = lambda: 0 + return lambda: 0 # E: "return" not allowed in except* block + +def loop(): + while True: + try: + pass + except* Exception: + x = lambda: 0 + return lambda: 0 # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testRedefineLocalWithinExceptStarTryClauses] +# flags: --allow-redefinition +def fn_str(_: str) -> int: ... +def fn_int(_: int) -> None: ... +def fn_exc(_: Exception) -> str: ... + +def in_block() -> None: + try: + a = "" + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int" + except* Exception: + b = "" + b = fn_str(b) + fn_int(b) + else: + c = "" + c = fn_str(c) + fn_int(c) + finally: + d = "" + d = fn_str(d) + fn_int(d) + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(b) # N: Revealed type is "builtins.int" + reveal_type(c) # N: Revealed type is "builtins.int" + reveal_type(d) # N: Revealed type is "builtins.int" + +def across_blocks() -> None: + try: + a = "" + except* Exception: + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + else: + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(a) # N: Revealed type is "builtins.str" + +def exc_name() -> None: + try: + pass + except* RuntimeError as e: + e = fn_exc(e) +[builtins fixtures/exception.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 085cc052705d7..c501962967d51 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -90,10 +90,10 @@ reveal_type(ident('x')) # N: Revealed type is "builtins.str" a: TV # E: Name "TV" is not defined def tup[T, S](x: T, y: S) -> tuple[T, S]: - reveal_type((x, y)) # N: Revealed type is "Tuple[T`-1, S`-2]" + reveal_type((x, y)) # N: Revealed type is "tuple[T`-1, S`-2]" return (x, y) -reveal_type(tup(1, 'x')) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(tup(1, 'x')) # N: Revealed type is "tuple[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [case testPEP695GenericClassSyntax] @@ -246,6 +246,7 @@ class Invariant[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] +[typing fixtures/typing-full.pyi] [case testPEP695InferVarianceCalculateOnDemand] class Covariant[T]: @@ -910,10 +911,10 @@ reveal_type(f) # N: Revealed type is "def (builtins.str, Union[builtins.int, No [case testPEP695TypeVarTuple] def f[*Ts](t: tuple[*Ts]) -> tuple[*Ts]: - reveal_type(t) # N: Revealed type is "Tuple[Unpack[Ts`-1]]" + reveal_type(t) # N: Revealed type is "tuple[Unpack[Ts`-1]]" return t -reveal_type(f((1, 'x'))) # N: Revealed type is "Tuple[Literal[1]?, Literal['x']?]" +reveal_type(f((1, 'x'))) # N: Revealed type is "tuple[Literal[1]?, Literal['x']?]" a: tuple[int, ...] reveal_type(f(a)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" @@ -933,7 +934,7 @@ from typing import Callable type C[*Ts] = tuple[*Ts, int] a: C[str, None] -reveal_type(a) # N: Revealed type is "Tuple[builtins.str, None, builtins.int]" +reveal_type(a) # N: Revealed type is "tuple[builtins.str, None, builtins.int]" [builtins fixtures/tuple.pyi] [case testPEP695IncrementalFunction] @@ -1227,6 +1228,43 @@ class C[T]: def f[S, S](x: S) -> S: # E: "S" already defined as a type parameter return x +[case testPEP695TypeVarNameClashNoCrashForwardReference] +# https://github.com/python/mypy/issues/18507 +from typing import TypeVar +T = TypeVar("T", bound=Foo) # E: Name "Foo" is used before definition + +class Foo: ... +class Bar[T]: ... + +[case testPEP695TypeVarNameClashNoCrashDeferredSymbol] +# https://github.com/python/mypy/issues/19526 +T = Unknown # E: Name "Unknown" is not defined + +class Foo[T]: ... +class Bar[*T]: ... +class Baz[**T]: ... +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeVarNameClashTypeAlias] +type Tb = object +type Ta[Tb] = 'B[Tb]' +class A[Ta]: ... +class B[Tb](A[Ta]): ... + +[case testPEP695TypeVarNameClashStarImport] +# Similar to +# https://github.com/python/mypy/issues/19946 +import a + +[file a.py] +from b import * +class Foo[T]: ... + +[file b.py] +from a import * +class Bar[T]: ... +[builtins fixtures/tuple.pyi] + [case testPEP695ClassDecorator] from typing import Any @@ -1370,8 +1408,8 @@ class C: class D(C): pass -reveal_type(C.m(1)) # N: Revealed type is "Tuple[__main__.C, builtins.int]" -reveal_type(D.m(1)) # N: Revealed type is "Tuple[__main__.D, builtins.int]" +reveal_type(C.m(1)) # N: Revealed type is "tuple[__main__.C, builtins.int]" +reveal_type(D.m(1)) # N: Revealed type is "tuple[__main__.D, builtins.int]" class E[T]: def m(self) -> Self: @@ -1384,9 +1422,9 @@ class F[T](E[T]): pass reveal_type(E[int]().m()) # N: Revealed type is "__main__.E[builtins.int]" -reveal_type(E[int]().mm(b'x')) # N: Revealed type is "Tuple[__main__.E[builtins.int], builtins.bytes]" +reveal_type(E[int]().mm(b'x')) # N: Revealed type is "tuple[__main__.E[builtins.int], builtins.bytes]" reveal_type(F[str]().m()) # N: Revealed type is "__main__.F[builtins.str]" -reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins.str], builtins.bytes]" +reveal_type(F[str]().mm(b'x')) # N: Revealed type is "tuple[__main__.F[builtins.str], builtins.bytes]" [builtins fixtures/tuple.pyi] [case testPEP695CallAlias] @@ -1487,7 +1525,7 @@ class C: reveal_type(C.a) # N: Revealed type is "builtins.int" reveal_type(C.b) # N: Revealed type is "Union[builtins.list[builtins.str], None]" -C.A = str # E: Incompatible types in assignment (expression has type "Type[str]", variable has type "TypeAliasType") +C.A = str # E: Incompatible types in assignment (expression has type "type[str]", variable has type "TypeAliasType") x: C.A y: C.B[int] @@ -1525,6 +1563,29 @@ 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] class C: pass type C = int # E: Name "C" already defined on line 1 @@ -1612,8 +1673,8 @@ class M[T: (int, str)](NamedTuple): c: M[int] d: M[str] e: M[bool] # E: Value of type variable "T" of "M" cannot be "bool" - [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testPEP695GenericTypedDict] from typing import TypedDict @@ -1873,3 +1934,292 @@ d1: Multi[int, str] = Multi[float, str]() # E: Incompatible types in assignment 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] + +[case testTypeAliasNormalization] +from collections.abc import Callable +from typing import Unpack +from typing_extensions import TypeAlias + +type RK_function_args = tuple[float, int] +type RK_functionBIS = Callable[[Unpack[RK_function_args], int], int] + +def ff(a: float, b: int, c: int) -> int: + return 2 + +bis: RK_functionBIS = ff +res: int = bis(1.0, 2, 3) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasNotReadyClass] +class CustomizeResponse: + related_resources: "ResourceRule" + +class ResourceRule: pass + +class DecoratorController: + type CustomizeResponse = CustomizeResponse + +x: DecoratorController.CustomizeResponse +reveal_type(x.related_resources) # N: Revealed type is "__main__.ResourceRule" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeAliasRecursiveOuterClass] +class A: + type X = X # E: Cannot resolve name "X" (possible cyclic definition) +class X: ... + +class AA: + XX = XX # OK, we allow this as a special case. +class XX: ... + +class Y: ... +class B: + type Y = Y + +reveal_type(AA.XX) # N: Revealed type is "def () -> __main__.XX" +y: B.Y +reveal_type(y) # N: Revealed type is "__main__.Y" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasRecursiveInvalid] +type X = X # E: Cannot resolve name "X" (possible cyclic definition) +type Z = Z[int] # E: Cannot resolve name "Z" (possible cyclic definition) +def foo() -> None: + type X = X # OK, refers to outer (invalid) X + x: X + reveal_type(x) # N: Revealed type is "Any" + type Y = Y # E: Cannot resolve name "Y" (possible cyclic definition) \ + # N: Recursive types are not allowed at function scope +class Z: ... # E: Name "Z" already defined on line 2 +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695MultipleUnpacksInBareApplicationNoCrash] +# https://github.com/python/mypy/issues/18856 +class A[*Ts]: ... + +A[*tuple[int, ...], *tuple[int, ...]] # E: More than one variadic Unpack in a type is not allowed +A[*tuple[*tuple[int, ...]], *tuple[*tuple[int, ...]]] # E: More than one variadic Unpack in a type is not allowed +a: A[*tuple[int, ...], *tuple[int, ...]] # E: More than one variadic Unpack in a type is not allowed +def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ... # E: More than one variadic Unpack in a type is not allowed + +tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one variadic Unpack in a type is not allowed +b: tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one variadic Unpack in a type is not allowed +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testForwardNestedPrecedesForwardGlobal] +from typing import NewType + +class W[T]: pass + +class R: + class M(W[Action.V], type): + FOO = R.Action.V(0) + class Action(metaclass=M): + V = NewType('V', int) + +class Action: + pass + +[case testPEP695TypeVarConstraintsDefaultAliases] +from typing import Generic +from typing_extensions import TypeVar + +type K = int +type V = int +type L = list[int] + +T1 = TypeVar("T1", str, K, default=K) +T2 = TypeVar("T2", str, K, default=V) +T3 = TypeVar("T3", str, L, default=L) + +class A1(Generic[T1]): + x: T1 +class A2(Generic[T2]): + x: T2 +class A3(Generic[T3]): + x: T3 + +reveal_type(A1().x) # N: Revealed type is "builtins.int" +reveal_type(A2().x) # N: Revealed type is "builtins.int" +reveal_type(A3().x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testDataclassWithTypeVarTuple] +# flags: --python-version 3.13 +# https://github.com/python/mypy/issues/19559 +from typing import Callable +from dataclasses import dataclass + +@dataclass +class Test[*Ts, R]: + a: Callable[[*Ts], R] +[builtins fixtures/dict.pyi] + +[case testPEP695AliasDoesNotReferToFullname] +# https://github.com/python/mypy/issues/19698 +from typing import TypeAliasType +type D = dict +type T = type +type TA = TypeAliasType + +D() # E: "TypeAliasType" not callable +X = TA("Y") # E: "TypeAliasType" not callable + +x: object +if T(x) is str: # E: "TypeAliasType" not callable + reveal_type(x) # N: Revealed type is "builtins.object" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasForwardReferenceInUnusedTypeVar] +# https://discuss.python.org/t/103305 +type Alias1[T: "A"] = int +type Alias2[T: ("A", int)] = int +class A: ... + +x1: Alias1[A] # ok +x2: Alias2[A] # ok + +[case testUndefinedUnpackInPEP696Base] +# Typo below is intentional. +class MyTuple[*Ts](tuple[*TS]): # E: Name "TS" is not defined + ... + +x: MyTuple[int, str] +reveal_type(x[0]) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testAnnotatedWithCallableAsParameterTypeKeyword] +from typing_extensions import Annotated + +def something() -> None: ... + +type A = list[Annotated[str, something()]] +a: A +reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testAnnotatedWithCallableAsParameterTypeKeywordDeeper] +from typing_extensions import Annotated + +def something() -> None: ... + +type A = list[Annotated[Annotated[str, something()], something()]] +a: A +reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/tuple.pyi] +[case testPEP695TypeAliasRecursiveInParameterBound] +from typing import Any + +type A1[T: B1] = list[int] +type B1 = None | A1[B1] +x1: A1[B1] +y1: A1[int] # E: Type argument "int" of "A1" must be a subtype of "B1" +z1: A1[None] + +type A2[T: B2] = list[T] +type B2 = None | A2[Any] +x2: A2[B2] +y2: A2[int] # E: Type argument "int" of "A2" must be a subtype of "B2" +z2: A2[None] + +type A3[T: B3] = list[T] +type B3 = None | A3[B3] +x3: A3[B3] +y3: A3[int] # E: Type argument "int" of "A3" must be a subtype of "B3" +z3: A3[None] +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasRecursiveTupleUnionNoCrash] +from collections.abc import Hashable + +type HashableArg = int | tuple[Hashable | HashableArg] +x: HashableArg +reveal_type(x) # N: Revealed type is "Union[builtins.int, tuple[Union[typing.Hashable, ...]]]" +if isinstance(x, tuple): + y, = x + reveal_type(y) # N: Revealed type is "Union[typing.Hashable, Union[builtins.int, tuple[Union[typing.Hashable, ...]]]]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test index 0ba64ad67c91f..117d20ceaf0b0 100644 --- a/test-data/unit/check-python313.test +++ b/test-data/unit/check-python313.test @@ -1,11 +1,321 @@ -[case testPEP695TypeParameterDefaultNotSupported] -class C[T = None]: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax - pass +[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] -def f[T = list[int]]() -> None: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax - pass +[case testPEP695TypeParameterDefaultBasic] +from typing import Callable -def g[**P = [int, str]]() -> None: # E: Type parameter default types not supported when using Python 3.12 type parameter syntax - pass +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]" -type A[T, S = int, U = str] = list[T] # E: Type parameter default types not supported when using Python 3.12 type parameter syntax +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/dict.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] + +[case testPEP695TypeParameterDefaultTypeAlias4] +# flags: --disallow-any-generics +class A[L = int, M = str]: ... +TD1 = A[float] +type TD2 = A[float] + +def func_d1( + a: TD1, + b: TD1[float], # E: Bad number of arguments for type alias, expected 0, given 1 + c: TD2, + d: TD2[float], # E: Bad number of arguments for type alias, expected 0, given 1 +) -> None: + reveal_type(a) # N: Revealed type is "__main__.A[builtins.float, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.A[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.A[builtins.float, builtins.str]" + reveal_type(d) # N: Revealed type is "__main__.A[builtins.float, builtins.str]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeVarConstraintsDefaultAliasesInline] +type K = int +type V = int + +class A1[T: (str, int) = K]: + x: T +class A2[T: (str, K) = K]: + x: T +class A3[T: (str, K) = V]: + x: T + +reveal_type(A1().x) # N: Revealed type is "builtins.int" +reveal_type(A2().x) # N: Revealed type is "builtins.int" +reveal_type(A3().x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultToAnotherTypeVar] +class A[X, Y = X, Z = Y]: + x: X + y: Y + z: Z + +a1: A[int] +reveal_type(a1.x) # N: Revealed type is "builtins.int" +reveal_type(a1.y) # N: Revealed type is "builtins.int" +# TODO: this must reveal `int` as well: +reveal_type(a1.z) # N: Revealed type is "X`1" + +a2: A[int, str] +reveal_type(a2.x) # N: Revealed type is "builtins.int" +reveal_type(a2.y) # N: Revealed type is "builtins.str" +reveal_type(a2.z) # N: Revealed type is "builtins.str" + +a3: A[int, str, bool] +reveal_type(a3.x) # N: Revealed type is "builtins.int" +reveal_type(a3.y) # N: Revealed type is "builtins.str" +reveal_type(a3.z) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultToAnotherTypeVarWrong] +class A[Y = X, X = int]: ... # E: Name "X" is not defined + +class B[Y = X]: ... # E: Name "X" is not defined +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-python314.test b/test-data/unit/check-python314.test new file mode 100644 index 0000000000000..f1043aab860ae --- /dev/null +++ b/test-data/unit/check-python314.test @@ -0,0 +1,3 @@ +[case testTemplateString] +reveal_type(t"mypy") # E: PEP 750 template strings are not yet supported \ + # N: Revealed type is "Any" diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 199014a66fedb..dd3f793fd02ba 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -17,8 +17,8 @@ def f(): ... # E: Function is missing a return type annotation \ # flags: --disallow-untyped-defs --warn-unused-ignores def d(f): ... # type: ignore @d -# type: ignore -def f(): ... # type: ignore # E: Unused "type: ignore" comment +# type: ignore # E: Unused "type: ignore" comment +def f(): ... # type: ignore [case testIgnoreDecoratedFunction2] # flags: --disallow-untyped-defs @@ -211,8 +211,7 @@ h(arg=0) # E: Unexpected keyword argument "arg" for "h" i(arg=0) # E: Unexpected keyword argument "arg" [case testWalrus] -from typing import NamedTuple, Optional, List -from typing_extensions import Final +from typing import Final, NamedTuple, Optional, List if a := 2: reveal_type(a) # N: Revealed type is "builtins.int" @@ -385,8 +384,7 @@ reveal_type(z2) # E: Name "z2" is not defined # N: Revealed type is "Any" [builtins fixtures/isinstancelist.pyi] [case testWalrusConditionalTypeBinder] -from typing import Tuple, Union -from typing_extensions import Literal +from typing import Literal, Tuple, Union class Good: @property @@ -406,9 +404,9 @@ else: def get_things() -> Union[Tuple[Good], Tuple[Bad]]: ... if (things := get_things())[0].is_good: - reveal_type(things) # N: Revealed type is "Tuple[__main__.Good]" + reveal_type(things) # N: Revealed type is "tuple[__main__.Good]" else: - reveal_type(things) # N: Revealed type is "Tuple[__main__.Bad]" + reveal_type(things) # N: Revealed type is "tuple[__main__.Bad]" [builtins fixtures/list.pyi] [case testWalrusConditionalTypeCheck] @@ -445,7 +443,7 @@ reveal_type(maybe_str) # N: Revealed type is "Union[builtins.str, None]" from typing import List def check_partial_list() -> None: - if (x := []): # E: Need type annotation for "x" (hint: "x: List[] = ...") + if (x := []): # E: Need type annotation for "x" (hint: "x: list[] = ...") pass y: List[str] @@ -469,8 +467,7 @@ reveal_type(x) # N: Revealed type is "Literal[0]" [case testWalrusAssignmentAndConditionScopeForProperty] # flags: --warn-unreachable - -from typing_extensions import Literal +from typing import Literal class PropertyWrapper: @property @@ -497,8 +494,7 @@ reveal_type(y) # N: Revealed type is "Literal[False]" [case testWalrusAssignmentAndConditionScopeForFunction] # flags: --warn-unreachable - -from typing_extensions import Literal +from typing import Literal def f() -> str: ... @@ -678,7 +674,8 @@ main:16: note: def foo(cls, float, /) -> Any main:16: note: def foo(cls, a: str) -> Any [case testUnpackWithDuplicateNamePositionalOnly] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -686,6 +683,7 @@ class Person(TypedDict): def foo(name: str, /, **kwargs: Unpack[Person]) -> None: # Allowed ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testPossiblyUndefinedWithAssignmentExpr] # flags: --enable-error-code possibly-undefined @@ -792,7 +790,7 @@ dct: Dict[str, int] = {"a": "b", **other} main:5: error: Dict entry 0 has incompatible type "str": "str"; expected "str": "int" dct: Dict[str, int] = {"a": "b", **other} ^~~~~~~~ -main:5: error: Unpacked dict entry 1 has incompatible type "Dict[str, str]"; expected "SupportsKeysAndGetItem[str, int]" +main:5: error: Unpacked dict entry 1 has incompatible type "dict[str, str]"; expected "SupportsKeysAndGetItem[str, int]" dct: Dict[str, int] = {"a": "b", **other} ^~~~~ diff --git a/test-data/unit/check-python39.test b/test-data/unit/check-python39.test index e17bf1e7ab5b9..86a9126ff483a 100644 --- a/test-data/unit/check-python39.test +++ b/test-data/unit/check-python39.test @@ -19,8 +19,6 @@ reveal_type(f) # N: Revealed type is "def (builtins.int) -> builtins.str" [builtins fixtures/list.pyi] [case testStarredExpressionsInForLoop] -# flags: --python-version 3.9 - a = b = c = [1, 2, 3] for x in *a, *b, *c: reveal_type(x) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index ac1ea0c0035aa..c09f1e6b90c0b 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -12,7 +12,7 @@ if isinstance(x, list): x = x[0] class Bad: ... -x = ["foo", {"bar": [Bad()]}] # E: List item 0 has incompatible type "Bad"; expected "Union[str, List[JSON], Dict[str, JSON]]" +x = ["foo", {"bar": [Bad()]}] # E: List item 0 has incompatible type "Bad"; expected "Union[str, list[JSON], dict[str, JSON]]" [builtins fixtures/isinstancelist.pyi] [case testRecursiveAliasBasicGenericSubtype] @@ -95,7 +95,7 @@ A = Union[B, int] B = Callable[[C], int] C = Type[A] x: A -reveal_type(x) # N: Revealed type is "Union[def (Union[Type[def (...) -> builtins.int], Type[builtins.int]]) -> builtins.int, builtins.int]" +reveal_type(x) # N: Revealed type is "Union[def (Union[type[def (...) -> builtins.int], type[builtins.int]]) -> builtins.int, builtins.int]" [case testRecursiveAliasesProhibited-skip] from typing import Type, Callable, Union @@ -161,7 +161,7 @@ y: C reveal_type(y.x) # N: Revealed type is "builtins.int" reveal_type(y[0]) # N: Revealed type is "builtins.int" x: A -reveal_type(x) # N: Revealed type is "__main__.G[Tuple[builtins.int, fallback=__main__.C]]" +reveal_type(x) # N: Revealed type is "__main__.G[tuple[builtins.int, fallback=__main__.C]]" [builtins fixtures/list.pyi] [case testRecursiveAliasViaBaseClassImported] @@ -189,7 +189,7 @@ class A(NamedTuple('A', [('attr', List[Exp])])): pass class B(NamedTuple('B', [('val', object)])): pass def my_eval(exp: Exp) -> int: - reveal_type(exp) # N: Revealed type is "Union[Tuple[builtins.list[...], fallback=__main__.A], Tuple[builtins.object, fallback=__main__.B]]" + reveal_type(exp) # N: Revealed type is "Union[tuple[builtins.list[...], fallback=__main__.A], tuple[builtins.object, fallback=__main__.B]]" if isinstance(exp, A): my_eval(exp[0][0]) return my_eval(exp.attr[0]) @@ -409,10 +409,11 @@ def local() -> None: x: L reveal_type(x) # N: Revealed type is "builtins.list[Union[builtins.int, Any]]" -S = Type[S] # E: Type[...] can't contain another Type[...] -U = Type[Union[int, U]] # E: Type[...] can't contain another Type[...] +S = Type[S] # E: Type[...] can't contain "Type[...]" +U = Type[Union[int, U]] # E: Type[...] can't contain "Union[Type[...], Type[...]]" \ + # E: Type[...] can't contain "Type[...]" x: U -reveal_type(x) # N: Revealed type is "Type[Any]" +reveal_type(x) # N: Revealed type is "type[Any]" D = List[F[List[T]]] # E: Invalid recursive alias: type variable nesting on right hand side F = D[T] # Error reported above @@ -426,9 +427,9 @@ from typing import NamedTuple, Optional NT = NamedTuple("NT", [("x", Optional[NT]), ("y", int)]) nt: NT -reveal_type(nt) # N: Revealed type is "Tuple[Union[..., None], builtins.int, fallback=__main__.NT]" -reveal_type(nt.x) # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" -reveal_type(nt[0]) # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" +reveal_type(nt) # N: Revealed type is "tuple[Union[..., None], builtins.int, fallback=__main__.NT]" +reveal_type(nt.x) # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" +reveal_type(nt[0]) # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" y: str if nt.x is not None: y = nt.x[0] # E: Incompatible types in assignment (expression has type "Optional[NT]", variable has type "str") @@ -439,9 +440,9 @@ from typing import NamedTuple, TypeVar, Tuple NT = NamedTuple("NT", [("x", NT), ("y", int)]) nt: NT -reveal_type(nt) # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.NT]" -reveal_type(nt.x) # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.NT]" -reveal_type(nt[0]) # N: Revealed type is "Tuple[Tuple[..., builtins.int, fallback=__main__.NT], builtins.int, fallback=__main__.NT]" +reveal_type(nt) # N: Revealed type is "tuple[..., builtins.int, fallback=__main__.NT]" +reveal_type(nt.x) # N: Revealed type is "tuple[..., builtins.int, fallback=__main__.NT]" +reveal_type(nt[0]) # N: Revealed type is "tuple[tuple[..., builtins.int, fallback=__main__.NT], builtins.int, fallback=__main__.NT]" y: str if nt.x is not None: y = nt.x[0] # E: Incompatible types in assignment (expression has type "NT", variable has type "str") @@ -463,9 +464,9 @@ class NT(NamedTuple): y: int nt: NT -reveal_type(nt) # N: Revealed type is "Tuple[Union[..., None], builtins.int, fallback=__main__.NT]" -reveal_type(nt.x) # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" -reveal_type(nt[0]) # N: Revealed type is "Union[Tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" +reveal_type(nt) # N: Revealed type is "tuple[Union[..., None], builtins.int, fallback=__main__.NT]" +reveal_type(nt.x) # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" +reveal_type(nt[0]) # N: Revealed type is "Union[tuple[Union[..., None], builtins.int, fallback=__main__.NT], None]" y: str if nt.x is not None: y = nt.x[0] # E: Incompatible types in assignment (expression has type "Optional[NT]", variable has type "str") @@ -490,7 +491,7 @@ class B(Tuple[B, int]): x: int C = NewType("C", B) b, _ = x -reveal_type(b) # N: Revealed type is "Tuple[..., builtins.int, fallback=__main__.B]" +reveal_type(b) # N: Revealed type is "tuple[..., builtins.int, fallback=__main__.B]" reveal_type(b.x) # N: Revealed type is "builtins.int" y: CNT @@ -515,13 +516,13 @@ class B(NamedTuple): y: int n: A -reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Tuple[..., builtins.int, fallback=__main__.B], ...], fallback=__main__.A]" +reveal_type(n) # N: Revealed type is "tuple[builtins.str, builtins.tuple[tuple[..., builtins.int, fallback=__main__.B], ...], fallback=__main__.A]" T = TypeVar("T") S = TypeVar("S") def foo(arg: Tuple[T, S]) -> Union[T, S]: ... x = foo(n) -y: str = x # E: Incompatible types in assignment (expression has type "Union[str, Tuple[B, ...]]", variable has type "str") +y: str = x # E: Incompatible types in assignment (expression has type "Union[str, tuple[B, ...]]", variable has type "str") [builtins fixtures/tuple.pyi] [case testMutuallyRecursiveNamedTuplesJoin] @@ -534,7 +535,7 @@ class B(NamedTuple): A = NamedTuple('A', [('x', str), ('y', B)]) n: B m: A -s: str = n.x # E: Incompatible types in assignment (expression has type "Tuple[A, int]", variable has type "str") +s: str = n.x # E: Incompatible types in assignment (expression has type "tuple[A, int]", variable has type "str") reveal_type(m[0]) # N: Revealed type is "builtins.str" lst = [m, n] @@ -566,7 +567,7 @@ n = n.y.x t: Tuple[str, B] t = n -t = m # E: Incompatible types in assignment (expression has type "B", variable has type "Tuple[str, B]") +t = m # E: Incompatible types in assignment (expression has type "B", variable has type "tuple[str, B]") [builtins fixtures/tuple.pyi] [case testMutuallyRecursiveNamedTuplesCalls] @@ -577,8 +578,8 @@ B = NamedTuple('B', [('x', A), ('y', int)]) A = NamedTuple('A', [('x', str), ('y', 'B')]) n: A def f(m: B) -> None: pass -reveal_type(n) # N: Revealed type is "Tuple[builtins.str, Tuple[..., builtins.int, fallback=__main__.B], fallback=__main__.A]" -reveal_type(f) # N: Revealed type is "def (m: Tuple[Tuple[builtins.str, ..., fallback=__main__.A], builtins.int, fallback=__main__.B])" +reveal_type(n) # N: Revealed type is "tuple[builtins.str, tuple[..., builtins.int, fallback=__main__.B], fallback=__main__.A]" +reveal_type(f) # N: Revealed type is "def (m: tuple[tuple[builtins.str, ..., fallback=__main__.A], builtins.int, fallback=__main__.B])" f(n) # E: Argument 1 to "f" has incompatible type "A"; expected "B" [builtins fixtures/tuple.pyi] @@ -590,7 +591,7 @@ def foo() -> None: # N: Recursive types are not allowed at function scope y: int b: B - reveal_type(b) # N: Revealed type is "Tuple[Any, builtins.int, fallback=__main__.B@3]" + reveal_type(b) # N: Revealed type is "tuple[Any, builtins.int, fallback=__main__.B@3]" [builtins fixtures/tuple.pyi] [case testBasicRecursiveGenericNamedTuple] @@ -605,7 +606,7 @@ class A: ... class B(A): ... nti: NT[int] = NT(key=0, value=NT(key=1, value=A())) # E: Argument "value" to "NT" has incompatible type "A"; expected "Union[int, NT[int]]" -reveal_type(nti) # N: Revealed type is "Tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]" +reveal_type(nti) # N: Revealed type is "tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]" nta: NT[A] ntb: NT[B] @@ -689,10 +690,11 @@ class TD(TypedDict, total=False): y: TD td: TD +reveal_type(td.get("y")) # N: Revealed type is "Union[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: ...}), None]" td["y"] = {"x": 0, "y": {}} td["y"] = {"x": 0, "y": {"x": 0, "y": 42}} # E: Incompatible types (expression has type "int", TypedDict item "y" has type "TD") -reveal_type(td.get("y")) # N: Revealed type is "Union[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: ...})}), None]" +reveal_type(td.get("y")) # N: Revealed type is "Union[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: ...}), None]" s: str = td.get("y") # E: Incompatible types in assignment (expression has type "Optional[TD]", variable has type "str") td.update({"x": 0, "y": {"x": 1, "y": {}}}) @@ -806,11 +808,11 @@ Tree2 = Union[str, Tuple[Tree2, Tree2]] Tree3 = Union[str, Tuple[Tree3, Tree3, Tree3]] def test1() -> Tree1: - return 42 # E: Incompatible return value type (got "int", expected "Union[str, Tuple[Tree1]]") + return 42 # E: Incompatible return value type (got "int", expected "Union[str, tuple[Tree1]]") def test2() -> Tree2: - return 42 # E: Incompatible return value type (got "int", expected "Union[str, Tuple[Tree2, Tree2]]") + return 42 # E: Incompatible return value type (got "int", expected "Union[str, tuple[Tree2, Tree2]]") def test3() -> Tree3: - return 42 # E: Incompatible return value type (got "int", expected "Union[str, Tuple[Tree3, Tree3, Tree3]]") + return 42 # E: Incompatible return value type (got "int", expected "Union[str, tuple[Tree3, Tree3, Tree3]]") [builtins fixtures/tuple.pyi] [case testRecursiveDoubleUnionNoCrash] @@ -878,20 +880,20 @@ def list_thing(transforming: InList[T]) -> T: reveal_type(list_thing([5])) # N: Revealed type is "builtins.list[builtins.int]" [case testRecursiveTypedDictWithList] -from typing import List -from typing_extensions import TypedDict +from typing import List, TypedDict Example = TypedDict("Example", {"rec": List["Example"]}) e: Example reveal_type(e) # N: Revealed type is "TypedDict('__main__.Example', {'rec': builtins.list[...]})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testRecursiveNamedTupleWithList] from typing import List, NamedTuple Example = NamedTuple("Example", [("rec", List["Example"])]) e: Example -reveal_type(e) # N: Revealed type is "Tuple[builtins.list[...], fallback=__main__.Example]" +reveal_type(e) # N: Revealed type is "tuple[builtins.list[...], fallback=__main__.Example]" [builtins fixtures/tuple.pyi] [case testRecursiveBoundFunctionScopeNoCrash] @@ -931,12 +933,11 @@ x: A[int, str] *_, last = x if last is not None: - reveal_type(last) # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[Tuple[builtins.int, builtins.str, Union[..., None]], None]]" + reveal_type(last) # N: Revealed type is "tuple[builtins.int, builtins.str, Union[tuple[builtins.int, builtins.str, Union[..., None]], None]]" [builtins fixtures/tuple.pyi] [case testRecursiveAliasLiteral] -from typing import Tuple -from typing_extensions import Literal +from typing import Literal, Tuple NotFilter = Tuple[Literal["not"], "NotFilter"] n: NotFilter @@ -1006,3 +1007,36 @@ 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 + +[case testRecursiveAliasOnArgumentDetected] +from typing import TypeVar + +T = TypeVar("T") +L = list[T] + +A = L[A] +a: A = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "A") + +[case testRecursiveAliasInstanceOverlapCheck] +# flags: --warn-unreachable +from typing_extensions import TypeAlias + +OneClass: TypeAlias = 'list[OneClass]' + +class TwoClass(list['TwoClass']): + pass + +def f(obj: OneClass) -> None: + if isinstance(obj, TwoClass): + reveal_type(obj) # N: Revealed type is "__main__.TwoClass" + else: + reveal_type(obj) # N: Revealed type is "builtins.list[...]" +[builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index b7642d30efc8d..4bcbaf50298de 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -88,6 +88,7 @@ def h(a: Iterable[int]) -> None: [case testCannotRedefineLocalWithinTry] # flags: --allow-redefinition +def g(): pass def f() -> None: try: x = 0 @@ -102,7 +103,67 @@ def f() -> None: y y = '' -def g(): pass +[case testRedefineLocalWithinTryClauses] +# flags: --allow-redefinition +def fn_str(_: str) -> int: ... +def fn_int(_: int) -> None: ... + +def in_block() -> None: + try: + a = "" + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int" + except: + b = "" + b = fn_str(b) + fn_int(b) + else: + c = "" + c = fn_str(c) + fn_int(c) + finally: + d = "" + d = fn_str(d) + fn_int(d) + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(b) # N: Revealed type is "builtins.int" + reveal_type(c) # N: Revealed type is "builtins.int" + reveal_type(d) # N: Revealed type is "builtins.int" + +def across_blocks() -> None: + try: + a = "" + except: + pass + else: + a = fn_str(a) # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(a) # N: Revealed type is "builtins.str" + +[case testRedefineLocalExceptVar] +# flags: --allow-redefinition +def fn_exc(_: Exception) -> str: ... + +def exc_name() -> None: + try: + pass + except RuntimeError as e: + e = fn_exc(e) +[builtins fixtures/exception.pyi] + +[case testRedefineNestedInTry] +# flags: --allow-redefinition + +def fn_int(_: int) -> None: ... + +try: + try: + ... + finally: + a = "" + a = 5 # E: Incompatible types in assignment (expression has type "int", variable has type "str") + fn_int(a) # E: Argument 1 to "fn_int" has incompatible type "str"; expected "int" +except: + pass [case testRedefineLocalWithinWith] # flags: --allow-redefinition @@ -193,7 +254,8 @@ def f() -> None: _, _ = 1, '' if 1: _, _ = '', 1 - reveal_type(_) # N: Revealed type is "Any" + # This is unintentional but probably fine. No one is going to read _ value. + reveal_type(_) # N: Revealed type is "builtins.int" [case testRedefineWithBreakAndContinue] # flags: --allow-redefinition @@ -261,7 +323,7 @@ def f() -> None: def f() -> None: class x: pass x = 1 # E: Cannot assign to a type \ - # E: Incompatible types in assignment (expression has type "int", variable has type "Type[x]") + # E: Incompatible types in assignment (expression has type "int", variable has type "type[x]") y = 1 class y: pass # E: Name "y" already defined on line 5 @@ -274,7 +336,6 @@ def f() -> None: # E: Incompatible types in assignment (expression has type "int", variable has type "TypeVar") reveal_type(x) # N: Revealed type is "typing.TypeVar" y = 1 - # NOTE: '"int" not callable' is due to test stubs y = TypeVar('y') # E: Cannot redefine "y" as a type variable \ # E: Incompatible types in assignment (expression has type "TypeVar", variable has type "int") def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \ @@ -290,6 +351,7 @@ def f() -> None: n = 1 import typing as n # E: Incompatible import of "n" (imported name has type Module, local name has type "int") [builtins fixtures/module.pyi] +[typing fixtures/typing-full.pyi] [case testRedefineLocalWithTypeAnnotation] # flags: --allow-redefinition @@ -321,7 +383,7 @@ def f() -> None: x = 1 if int(): x = '' - reveal_type(x) # N: Revealed type is "builtins.object" + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" x = '' reveal_type(x) # N: Revealed type is "builtins.str" if int(): @@ -486,6 +548,7 @@ try: except Exception as typing: pass [builtins fixtures/exception.pyi] +[typing fixtures/typing-full.pyi] [case testRedefiningUnderscoreFunctionIsntAnError] def _(arg): diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test new file mode 100644 index 0000000000000..4d99aa17f804d --- /dev/null +++ b/test-data/unit/check-redefine2.test @@ -0,0 +1,1199 @@ +-- Test cases for the redefinition of variable with a different type (new version). + +[case testNewRedefineLocalWithDifferentType] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + x = '' + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineConditionalLocalWithDifferentType] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + else: + x = '' + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineMergeConditionalLocal1] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + else: + x = '' + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + if int(): + x = 0 + else: + x = None + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +[case testNewRedefineMergeConditionalLocal2] +# flags: --allow-redefinition-new --local-partial-types +def nested_ifs() -> None: + if int(): + if int(): + x = 0 + else: + x = '' + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + if int(): + x = None + else: + x = b"" + reveal_type(x) # N: Revealed type is "Union[None, builtins.bytes]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None, builtins.bytes]" + +[case testNewRedefineUninitializedCodePath1] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineUninitializedCodePath2] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + if int(): + x: Union[int, str] = 0 + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineUninitializedCodePath3] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + if int(): + x = 0 + elif int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineUninitializedCodePath4] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + if int(): + x: Union[int, str] = 0 + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineUninitializedCodePath5] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +def f1() -> None: + x = 0 + if int(): + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + x = None + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +[case testNewRedefineUninitializedCodePath6] +# flags: --allow-redefinition-new --local-partial-types +from typing import Union + +x: Union[str, None] + +def f1() -> None: + if x is not None: + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" + +[case testNewRedefineGlobalVariableSimple] +# flags: --allow-redefinition-new --local-partial-types +if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" +else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f1() -> None: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + global x + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineGlobalVariableNoneInit] +# flags: --allow-redefinition-new --local-partial-types +x = None + +def f() -> None: + global x + reveal_type(x) # N: Revealed type is "None" + x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "None") + reveal_type(x) # N: Revealed type is "None" + +reveal_type(x) # N: Revealed type is "None" + +[case testNewRedefineParameterTypes] +# flags: --allow-redefinition-new --local-partial-types +from typing import Optional + +def f1(x: Optional[str] = None) -> None: + reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" + if x is None: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2(*args: str, **kwargs: int) -> None: + reveal_type(args) # N: Revealed type is "builtins.tuple[builtins.str, ...]" + reveal_type(kwargs) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + +class C: + def m(self) -> None: + reveal_type(self) # N: Revealed type is "__main__.C" +[builtins fixtures/dict.pyi] + + +[case testNewRedefineClassBody] +# flags: --allow-redefinition-new --local-partial-types +class C: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +reveal_type(C.x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineNestedFunctionBasics] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + else: + x = "" + + def nested() -> None: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + if int(): + x = 0 + else: + x = "" + + def nested() -> None: + nonlocal x + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineLambdaBasics] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = 0 + if int(): + x = None + f = lambda: reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(f) # N: Revealed type is "def () -> Union[builtins.int, None]" + if x is None: + x = "" + f = lambda: reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(f) # N: Revealed type is "def () -> Union[builtins.int, builtins.str]" + +[case testNewRedefineAssignmentExpression] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if x := int(): + reveal_type(x) # N: Revealed type is "builtins.int" + elif x := str(): + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + if x := int(): + reveal_type(x) # N: Revealed type is "builtins.int" + elif x := str(): + reveal_type(x) # N: Revealed type is "builtins.str" + else: + pass + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f3() -> None: + if (x := int()) or (x := str()): + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineOperatorAssignment] +# flags: --allow-redefinition-new --local-partial-types +class D: pass +class C: + def __add__(self, x: C) -> D: ... + +c = C() +if int(): + c += C() + reveal_type(c) # N: Revealed type is "__main__.D" +reveal_type(c) # N: Revealed type is "Union[__main__.C, __main__.D]" + +[case testNewRedefineImportFrom-xfail] +# flags: --allow-redefinition-new --local-partial-types +if int(): + from m import x +else: + # TODO: This could be useful to allow + from m import y as x # E: Incompatible import of "x" (imported name has type "str", local name has type "int") +reveal_type(x) # N: Revealed type is "builtins.int" + +if int(): + from m import y +else: + y = 1 +reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.int]" + +[file m.py] +x = 1 +y = "" + +[case testNewRedefineImport] +# flags: --allow-redefinition-new --local-partial-types +if int(): + import m +else: + import m2 as m # E: Name "m" already defined (by an import) +m.x +m.y # E: Module has no attribute "y" + +[file m.py] +x = 1 + +[file m2.py] +y = "" +[builtins fixtures/module.pyi] + +[case testNewRedefineOptionalTypesSimple] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = None + if int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +def f2() -> None: + if int(): + x = None + elif int(): + x = "" + else: + x = 1 + reveal_type(x) # N: Revealed type is "Union[None, builtins.str, builtins.int]" + +def f3() -> None: + if int(): + x = None + else: + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +def f4() -> None: + x = None + reveal_type(x) # N: Revealed type is "None" + +y = None +if int(): + y = 1 +reveal_type(y) # N: Revealed type is "Union[None, builtins.int]" + +if int(): + z = None +elif int(): + z = 1 +else: + z = "" +reveal_type(z) # N: Revealed type is "Union[None, builtins.int, builtins.str]" + +[case testNewRedefinePartialTypeForInstanceVariable] +# flags: --allow-redefinition-new --local-partial-types +class C1: + def __init__(self) -> None: + self.x = None + if int(): + self.x = 1 + reveal_type(self.x) # N: Revealed type is "builtins.int" + reveal_type(self.x) # N: Revealed type is "Union[builtins.int, None]" + +reveal_type(C1().x) # N: Revealed type is "Union[builtins.int, None]" + +class C2: + def __init__(self) -> None: + self.x = [] + for i in [1, 2]: + self.x.append(i) + reveal_type(self.x) # N: Revealed type is "builtins.list[builtins.int]" + +reveal_type(C2().x) # N: Revealed type is "builtins.list[builtins.int]" + +class C3: + def __init__(self) -> None: + self.x = None + if int(): + self.x = 1 + else: + self.x = "" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[int]") + reveal_type(self.x) # N: Revealed type is "Union[builtins.int, None]" + +reveal_type(C3().x) # N: Revealed type is "Union[builtins.int, None]" + +class C4: + def __init__(self) -> None: + self.x = [] + if int(): + self.x = [""] + reveal_type(self.x) # N: Revealed type is "builtins.list[builtins.str]" + +reveal_type(C4().x) # N: Revealed type is "builtins.list[builtins.str]" + +class C5: + def __init__(self) -> None: + if int(): + self.x = None + return + self.x = [""] + reveal_type(self.x) # N: Revealed type is "builtins.list[builtins.str]" + +reveal_type(C5().x) # N: Revealed type is "Union[builtins.list[builtins.str], None]" +[builtins fixtures/list.pyi] + +[case testNewRedefinePartialGenericTypes] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +def f2() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + a = [""] + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" + +def f3() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + a = [] + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +def f4() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + # Partial types are currently not supported on reassignment + a = [] + a.append("x") # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + +def f5() -> None: + if int(): + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + else: + b = [""] + a = b + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]" + +def f6() -> None: + a = [] + a.append(1) + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + b = [""] + a = b + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/list.pyi] + +[case testNewRedefineFinalLiteral] +# flags: --allow-redefinition-new --local-partial-types +from typing import Final, Literal + +x: Final = "foo" +reveal_type(x) # N: Revealed type is "Literal['foo']?" +a: Literal["foo"] = x + +class B: + x: Final = "bar" + a: Literal["bar"] = x +reveal_type(B.x) # N: Revealed type is "Literal['bar']?" +[builtins fixtures/tuple.pyi] + +[case testNewRedefineAnnotatedVariable] +# flags: --allow-redefinition-new --local-partial-types +from typing import Optional + +def f1() -> None: + x: int = 0 + if int(): + x = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2(x: Optional[str]) -> None: + if x is not None: + reveal_type(x) # N: Revealed type is "builtins.str" + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f3() -> None: + a: list[Optional[str]] = [""] + reveal_type(a) # N: Revealed type is "builtins.list[Union[builtins.str, None]]" + a = [""] + reveal_type(a) # N: Revealed type is "builtins.list[Union[builtins.str, None]]" + +class C: + x: Optional[str] + + def f(self) -> None: + if self.x is not None: + reveal_type(self.x) # N: Revealed type is "builtins.str" + else: + self.x = "" + reveal_type(self.x) # N: Revealed type is "builtins.str" + +[case testNewRedefineAnyType1] +# flags: --allow-redefinition-new --local-partial-types +def a(): pass + +def f1() -> None: + if int(): + x = "" + else: + x = a() + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Union[builtins.str, Any]" + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2() -> None: + if int(): + x = a() + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[Any, builtins.str]" + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + +def f3() -> None: + x = 1 + x = a() + reveal_type(x) # N: Revealed type is "Any" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f4() -> None: + x = a() + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + x = a() + reveal_type(x) # N: Revealed type is "Any" + +def f5() -> None: + x = a() + if int(): + x = 1 + reveal_type(x) # N: Revealed type is "builtins.int" + elif int(): + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[Any, builtins.int, builtins.str]" + +def f6() -> None: + x = a() + if int(): + x = 1 + else: + x = "" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f7() -> None: + x: int + x = a() + reveal_type(x) # N: Revealed type is "builtins.int" + +[case testNewRedefineAnyType2] +# flags: --allow-redefinition-new --local-partial-types +from typing import Any + +def f1() -> None: + x: Any + x = int() + reveal_type(x) # N: Revealed type is "Any" + +def f2() -> None: + x: Any + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "Any" + else: + x = "" + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Any" + +def f3(x) -> None: + if int(): + x = 0 + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Any" + +[case tetNewRedefineDel] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + del x + reveal_type(x) # N: Revealed type is "" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2() -> None: + if int(): + x = 0 + del x + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f3() -> None: + if int(): + x = 0 + else: + x = "" + del x + reveal_type(x) # N: Revealed type is "builtins.int" + +def f4() -> None: + while int(): + if int(): + x: int = 0 + else: + del x + reveal_type(x) # N: Revealed type is "builtins.int" + +def f5() -> None: + while int(): + if int(): + x = 0 + else: + del x + continue + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" +[case testNewRedefineWhileLoopSimple] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + while int(): + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + x = 0 + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "builtins.int" + while int(): + x = None + reveal_type(x) # N: Revealed type is "None" + x = b"" + reveal_type(x) # N: Revealed type is "builtins.bytes" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.bytes]" + x = [1] + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" + +[case testNewRedefineWhileLoopOptional] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = None + while int(): + if int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +def f2() -> None: + x = None + while int(): + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + if int(): + x = "" + reveal_type(x) # N: Revealed type is "Union[None, builtins.str]" + +[case testNewRedefineWhileLoopPartialType] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x = [] + while int(): + x.append(1) + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testNewRedefineWhileLoopComplex1] +# flags: --allow-redefinition-new --local-partial-types + +def f1() -> None: + while True: + try: + pass + except Exception as e: + continue +[builtins fixtures/exception.pyi] + +[case testNewRedefineWhileLoopComplex2] +# flags: --allow-redefinition-new --local-partial-types + +class C: + def __enter__(self) -> str: ... + def __exit__(self, *args) -> str: ... + +def f1() -> None: + while True: + with C() as x: + continue + +def f2() -> None: + while True: + from m import y + if int(): + continue + +[file m.py] +y = "" +[builtins fixtures/tuple.pyi] + +[case testNewRedefineReturn] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = 0 + return + else: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + if int(): + x = "" + else: + x = 0 + return + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineBreakAndContinue] +# flags: --allow-redefinition-new --local-partial-types +def b() -> None: + while int(): + x = "" + if int(): + x = 1 + break + reveal_type(x) # N: Revealed type is "builtins.str" + x = None + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + +def c() -> None: + x = 0 + while int(): + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" + if int(): + x = "" + continue + else: + x = None + reveal_type(x) # N: Revealed type is "None" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" + +[case testNewRedefineUnderscore] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + if int(): + _ = 0 + reveal_type(_) # N: Revealed type is "builtins.int" + else: + _ = "" + reveal_type(_) # N: Revealed type is "builtins.str" + reveal_type(_) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineWithStatement] +# flags: --allow-redefinition-new --local-partial-types +class C: + def __enter__(self) -> int: ... + def __exit__(self, x, y, z): ... +class D: + def __enter__(self) -> str: ... + def __exit__(self, x, y, z): ... + +def f1() -> None: + with C() as x: + reveal_type(x) # N: Revealed type is "builtins.int" + with D() as x: + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + if int(): + with C() as x: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + with D() as x: + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[case testNewRedefineTryStatement] +# flags: --allow-redefinition-new --local-partial-types +class E(Exception): pass + +def g(): ... + +def f1() -> None: + try: + x = 1 + g() + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + except RuntimeError as e: + reveal_type(e) # N: Revealed type is "builtins.RuntimeError" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + except E as e: + reveal_type(e) # N: Revealed type is "__main__.E" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(e) # N: Revealed type is "" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f2() -> None: + try: + x = 1 + if int(): + x = "" + return + except Exception: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + return + reveal_type(x) # N: Revealed type is "builtins.int" + +def f3() -> None: + try: + x = 1 + if int(): + x = "" + return + finally: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(x) # N: Revealed type is "builtins.int" + +def f4() -> None: + while int(): + try: + x = 1 + if int(): + x = "" + break + if int(): + while int(): + if int(): + x = None + break + finally: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/exception.pyi] + +[case testNewRedefineRaiseStatement] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + if int(): + x = "" + elif int(): + x = None + raise Exception() + else: + x = 1 + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + +def f2() -> None: + try: + x = 1 + if int(): + x = "" + raise Exception() + reveal_type(x) # N: Revealed type is "builtins.int" + except Exception: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/exception.pyi] + + +[case testNewRedefineMultipleAssignment] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + x, y = 1, "" + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" + x, y = None, 2 + reveal_type(x) # N: Revealed type is "None" + reveal_type(y) # N: Revealed type is "builtins.int" + +def f2() -> None: + if int(): + x, y = 1, "" + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" + else: + x, y = None, 2 + reveal_type(x) # N: Revealed type is "None" + reveal_type(y) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.int]" + +[case testNewRedefineForLoopBasics] +# flags: --allow-redefinition-new --local-partial-types +def f1() -> None: + for x in [1]: + reveal_type(x) # N: Revealed type is "builtins.int" + for x in [""]: + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + if int(): + for x, y in [(1, "x")]: + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.str" + else: + for x, y in [(None, 1)]: + reveal_type(x) # N: Revealed type is "None" + reveal_type(y) # N: Revealed type is "builtins.int" + + reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(y) # N: Revealed type is "Union[builtins.str, builtins.int]" +[builtins fixtures/for.pyi] + +[case testNewRedefineForLoop1] +# flags: --allow-redefinition-new --local-partial-types +def l() -> list[int]: + return [] + +def f1() -> None: + x = "" + for x in l(): + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + +def f2() -> None: + for x in [1, 2]: + x = [x] + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" + +def f3() -> None: + for x in [1, 2]: + if int(): + x = "x" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/for.pyi] + +[case testNewRedefineForLoop2] +# flags: --allow-redefinition-new --local-partial-types +from typing import Any + +def f(a: Any) -> None: + for d in a: + if isinstance(d["x"], str): + return +[builtins fixtures/isinstance.pyi] + +[case testNewRedefineForStatementIndexNarrowing] +# flags: --allow-redefinition-new --local-partial-types +from typing 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 + reveal_type(a) # N: Revealed type is "builtins.int" +reveal_type(a) # N: Revealed type is "builtins.int" + +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] +[typing fixtures/typing-full.pyi] + +[case testNewRedefineForLoopIndexWidening] +# flags: --allow-redefinition-new --local-partial-types + +def f1() -> None: + for x in [1]: + reveal_type(x) # N: Revealed type is "builtins.int" + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2() -> None: + for x in [1]: + reveal_type(x) # N: Revealed type is "builtins.int" + if int(): + break + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +def f3() -> None: + if int(): + for x in [1]: + x = "" + reveal_type(x) # N: Revealed type is "builtins.str" + +[case testNewRedefineVariableAnnotatedInLoop] +# flags: --allow-redefinition-new --local-partial-types --enable-error-code=redundant-expr +from typing import Optional + +def f1() -> None: + e: Optional[str] = None + for x in ["a"]: + if e is None and int(): + e = x + continue + elif e is not None and int(): + break + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + +def f2(e: Optional[str]) -> None: + for x in ["a"]: + if e is None and int(): + e = x + continue + elif e is not None and int(): + break + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + reveal_type(e) # N: Revealed type is "Union[builtins.str, None]" + +[case testNewRedefineLoopAndPartialTypesSpecialCase] +# flags: --allow-redefinition-new --local-partial-types +def f() -> list[str]: + a = [] # type: ignore + o = [] + for line in ["x"]: + if int(): + continue + if int(): + a = [] + if int(): + a.append(line) + else: + o.append(line) + return o +[builtins fixtures/list.pyi] + +[case testNewRedefineFinalVariable] +# flags: --allow-redefinition-new --local-partial-types +from typing import Final + +x: Final = "foo" +x = 1 # E: Cannot assign to final name "x" \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + +class C: + y: Final = "foo" + y = 1 # E: Cannot assign to final name "y" \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + +[case testNewRedefineEnableUsingComment] +# flags: --local-partial-types +import a +import b + +[file a.py] +# mypy: allow-redefinition-new +if int(): + x = 0 +else: + x = "" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + +[file b.py] +if int(): + x = 0 +else: + x = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int") +reveal_type(x) # N: Revealed type is "builtins.int" + +[case testNewRedefineWithoutLocalPartialTypes] +import a +import b + +[file a.py] +# mypy: local-partial-types, allow-redefinition-new +x = 0 +if int(): + x = "" + +[file b.py] +# mypy: allow-redefinition-new +x = 0 +if int(): + x = "" + +[out] +tmp/b.py:1: error: --local-partial-types must be enabled if using --allow-redefinition-new + +[case testNewRedefineNestedLoopInfiniteExpansion] +# flags: --allow-redefinition-new --local-partial-types +def a(): ... + +def f() -> None: + while int(): + x = a() + + while int(): + x = [x] + + reveal_type(x) # N: Revealed type is "Union[Any, builtins.list[Any]]" + +[case testNewRedefinePartialNoneEmptyList] +# flags: --allow-redefinition-new --local-partial-types +def func() -> None: + l = None + + if int(): + l = [] # E: Need type annotation for "l" + l.append(1) + reveal_type(l) # N: Revealed type is "Union[None, builtins.list[Any]]" +[builtins fixtures/list.pyi] + +[case testNewRedefineNarrowingSpecialCase] +# flags: --allow-redefinition-new --local-partial-types --warn-unreachable +from typing import Any, Union + +def get() -> Union[tuple[Any, Any], tuple[None, None]]: ... + +def f() -> None: + x, _ = get() + reveal_type(x) # N: Revealed type is "Union[Any, None]" + if x and int(): + reveal_type(x) # N: Revealed type is "Any" + reveal_type(x) # N: Revealed type is "Union[Any, None]" + if x and int(): + reveal_type(x) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testNewRedefinePartialTypeForUnderscore] +# flags: --allow-redefinition-new --local-partial-types + +def t() -> tuple[int]: + return (42,) + +def f1() -> None: + # Underscore is slightly special to preserve backward compatibility + x, *_ = t() + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2() -> None: + x, *y = t() # E: Need type annotation for "y" (hint: "y: list[] = ...") + +def f3() -> None: + x, _ = 1, [] + +def f4() -> None: + a, b = 1, [] # E: Need type annotation for "b" (hint: "b: list[] = ...") +[builtins fixtures/tuple.pyi] + +[case testNewRedefineUseInferredTypedDictTypeForContext] +# flags: --allow-redefinition-new --local-partial-types +from typing import TypedDict + +class TD(TypedDict): + x: int + +def f() -> None: + td = TD(x=1) + if int(): + td = {"x": 5} + reveal_type(td) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.int})" +[typing fixtures/typing-typeddict.pyi] + +[case testNewRedefineEmptyGeneratorUsingUnderscore] +# flags: --allow-redefinition-new --local-partial-types +def f() -> None: + gen = (_ for _ in ()) + reveal_type(gen) # N: Revealed type is "typing.Generator[Any, None, None]" +[builtins fixtures/tuple.pyi] + +[case testNewRedefineCannotWidenImportedVariable] +# flags: --allow-redefinition-new --local-partial-types +import a +import b +reveal_type(a.x) # N: Revealed type is "builtins.str" + +[file a.py] +from b import x +if int(): + x = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") + +[file b.py] +x = "a" + +[case testNewRedefineCannotWidenGlobalOrClassVariableWithMemberRef] +# flags: --allow-redefinition-new --local-partial-types +from typing import ClassVar +import a + +a.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") +reveal_type(a.x) # N: Revealed type is "builtins.str" + +class C: + x = "" + y: ClassVar[str] = "" + +C.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") +reveal_type(C.x) # N: Revealed type is "builtins.str" +C.y = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") +reveal_type(C.y) # N: Revealed type is "builtins.str" + +[file a.py] +x = "a" + +[case testNewRedefineWidenGlobalInInitModule] +# flags: --allow-redefinition-new --local-partial-types +import pkg + +[file pkg/__init__.py] +x = 0 +if int(): + x = "" +reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 9601852ef823b..89603efafddd6 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -55,7 +55,7 @@ class A: return A() # E: Incompatible return value type (got "A", expected "T") elif A(): return B() # E: Incompatible return value type (got "B", expected "T") - reveal_type(_type(self)) # N: Revealed type is "Type[T`-1]" + reveal_type(_type(self)) # N: Revealed type is "type[T`-1]" return reveal_type(_type(self)()) # N: Revealed type is "T`-1" class B(A): @@ -160,12 +160,7 @@ class C(A[int]): def f(self) -> int: ... class D(A[str]): - def f(self) -> int: ... # E: Signature of "f" incompatible with supertype "A" \ - # N: Superclass: \ - # N: @overload \ - # N: def f(self) -> str \ - # N: Subclass: \ - # N: def f(self) -> int + def f(self) -> int: ... # E: Return type "int" of "f" incompatible with return type "str" in supertype "A" class E(A[T]): def f(self) -> int: ... # E: Signature of "f" incompatible with supertype "A" \ @@ -201,7 +196,6 @@ class I(A[int]): class J(A[int]): def f(self, arg) -> int: ... # E: Signature of "f" incompatible with supertype "A" \ # N: Superclass: \ - # N: @overload \ # N: def f(self) -> int \ # N: Subclass: \ # N: def f(self, arg: Any) -> int @@ -224,12 +218,10 @@ class B(A[int]): def f(self, s: int) -> int: ... class C(A[None]): - def f(self, s: int) -> int: ... # E: Signature of "f" incompatible with supertype "A" \ - # N: Superclass: \ - # N: @overload \ - # N: def f(self, s: None) -> None \ - # N: Subclass: \ - # N: def f(self, s: int) -> int + def f(self, s: int) -> int: ... # E: Return type "int" of "f" incompatible with return type "None" in supertype "A" \ + # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "None" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides [builtins fixtures/tuple.pyi] [case testSelfTypeOverrideCompatibilityTypeVar] @@ -314,7 +306,7 @@ class A: @classmethod def new(cls: Type[T], factory: Callable[[T], T]) -> T: - reveal_type(cls) # N: Revealed type is "Type[T`-1]" + reveal_type(cls) # N: Revealed type is "type[T`-1]" reveal_type(cls()) # N: Revealed type is "T`-1" cls(2) # E: Too many arguments for "A" return cls() @@ -421,7 +413,7 @@ class A: return self @classmethod - def cfoo(cls: Type[T]) -> T: # E: The erased type of self "Type[builtins.str]" is not a supertype of its class "Type[__main__.A]" + def cfoo(cls: Type[T]) -> T: # E: The erased type of self "type[builtins.str]" is not a supertype of its class "type[__main__.A]" return cls() Q = TypeVar('Q', bound='B') @@ -449,7 +441,7 @@ class D: return self @classmethod - def cfoo(cls: Type[Q]) -> Q: # E: The erased type of self "Type[__main__.B]" is not a supertype of its class "Type[__main__.D]" + def cfoo(cls: Type[Q]) -> Q: # E: The erased type of self "type[__main__.B]" is not a supertype of its class "type[__main__.D]" return cls() [builtins fixtures/classmethod.pyi] @@ -481,10 +473,10 @@ class A: pass class B: - def __new__(cls: Type[T]) -> T: # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]" + def __new__(cls: Type[T]) -> T: # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]" return cls() - def __init_subclass__(cls: Type[T]) -> None: # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]" + def __init_subclass__(cls: Type[T]) -> None: # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]" pass class C: @@ -495,19 +487,19 @@ class C: pass class D: - def __new__(cls: D) -> D: # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]" + def __new__(cls: D) -> D: # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]" return cls - def __init_subclass__(cls: D) -> None: # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]" + def __init_subclass__(cls: D) -> None: # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]" pass class E: def __new__(cls) -> E: - reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + reveal_type(cls) # N: Revealed type is "type[__main__.E]" return cls() def __init_subclass__(cls) -> None: - reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + reveal_type(cls) # N: Revealed type is "type[__main__.E]" [case testSelfTypeNew_explicit] from typing import TypeVar, Type @@ -524,11 +516,11 @@ class A: class B: @staticmethod - def __new__(cls: Type[T]) -> T: # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]" + def __new__(cls: Type[T]) -> T: # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]" return cls() @classmethod - def __init_subclass__(cls: Type[T]) -> None: # E: The erased type of self "Type[__main__.A]" is not a supertype of its class "Type[__main__.B]" + def __init_subclass__(cls: Type[T]) -> None: # E: The erased type of self "type[__main__.A]" is not a supertype of its class "type[__main__.B]" pass class C: @@ -542,22 +534,22 @@ class C: class D: @staticmethod - def __new__(cls: D) -> D: # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]" + def __new__(cls: D) -> D: # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]" return cls @classmethod - def __init_subclass__(cls: D) -> None: # E: The erased type of self "__main__.D" is not a supertype of its class "Type[__main__.D]" + def __init_subclass__(cls: D) -> None: # E: The erased type of self "__main__.D" is not a supertype of its class "type[__main__.D]" pass class E: @staticmethod def __new__(cls) -> E: - reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + reveal_type(cls) # N: Revealed type is "type[__main__.E]" return cls() @classmethod def __init_subclass__(cls) -> None: - reveal_type(cls) # N: Revealed type is "Type[__main__.E]" + reveal_type(cls) # N: Revealed type is "type[__main__.E]" [builtins fixtures/classmethod.pyi] @@ -616,13 +608,13 @@ class B(A): pass reveal_type(A().g) # N: Revealed type is "builtins.int" -reveal_type(A().gt) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.A]" +reveal_type(A().gt) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.A]" reveal_type(A().f()) # N: Revealed type is "builtins.int" -reveal_type(A().ft()) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.A]" +reveal_type(A().ft()) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.A]" reveal_type(B().g) # N: Revealed type is "builtins.int" -reveal_type(B().gt) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.B]" +reveal_type(B().gt) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.B]" reveal_type(B().f()) # N: Revealed type is "builtins.int" -reveal_type(B().ft()) # N: Revealed type is "Tuple[builtins.int, builtins.int, fallback=__main__.B]" +reveal_type(B().ft()) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.B]" [builtins fixtures/property.pyi] @@ -653,9 +645,9 @@ reveal_type(Y.gt) # N: Revealed type is "def (x: builtins.int) -> __main__.Y" reveal_type(Y.f()) # N: Revealed type is "builtins.int" reveal_type(Y.ft()) # N: Revealed type is "def (x: builtins.int) -> __main__.Y" reveal_type(X1.g) # N: Revealed type is "builtins.int" -reveal_type(X1.gt) # N: Revealed type is "Type[__main__.X]" +reveal_type(X1.gt) # N: Revealed type is "type[__main__.X]" reveal_type(X1.f()) # N: Revealed type is "builtins.int" -reveal_type(X1.ft()) # N: Revealed type is "Type[__main__.X]" +reveal_type(X1.ft()) # N: Revealed type is "type[__main__.X]" [builtins fixtures/property.pyi] @@ -711,9 +703,9 @@ class C(Generic[T]): class DI(C[int]): ... class DS(C[str]): ... -DI().from_item() # E: Invalid self argument "Type[DI]" to class attribute function "from_item" with type "Callable[[Type[C[str]]], None]" +DI().from_item() # E: Invalid self argument "type[DI]" to class attribute function "from_item" with type "Callable[[type[C[str]]], None]" DS().from_item() -DI.from_item() # E: Invalid self argument "Type[DI]" to attribute function "from_item" with type "Callable[[Type[C[str]]], None]" +DI.from_item() # E: Invalid self argument "type[DI]" to attribute function "from_item" with type "Callable[[type[C[str]]], None]" DS.from_item() [builtins fixtures/classmethod.pyi] @@ -731,7 +723,7 @@ class C(Generic[T]): ci: C[int] cs: C[str] -reveal_type(ci.from_item) # N: Revealed type is "def (item: Tuple[builtins.int])" +reveal_type(ci.from_item) # N: Revealed type is "def (item: tuple[builtins.int])" reveal_type(cs.from_item) # N: Revealed type is "def (item: builtins.str)" [builtins fixtures/tuple.pyi] @@ -784,8 +776,7 @@ reveal_type(x) # N: Revealed type is "__main__.SubP[Any]" y: SubP[str] = SubP(use_str=True) [file lib.pyi] -from typing import TypeVar, Generic, overload, Tuple -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic, overload, Tuple T = TypeVar('T') class P(Generic[T]): @@ -809,8 +800,7 @@ xx = PFallBack(t) # E: Need type annotation for "xx" yy = PFallBackAny(t) # OK [file lib.pyi] -from typing import TypeVar, Generic, overload, Tuple, Any -from typing_extensions import Literal +from typing import Literal, TypeVar, Generic, overload, Tuple, Any class PFallBack(Generic[T]): @overload @@ -854,12 +844,11 @@ class Sub(Base[List[int]]): ... class BadSub(Base[int]): ... reveal_type(Sub().get_item()) # N: Revealed type is "builtins.int" -BadSub().get_item() # E: Invalid self argument "BadSub" to attribute function "get_item" with type "Callable[[Base[List[S]]], S]" +BadSub().get_item() # E: Invalid self argument "BadSub" to attribute function "get_item" with type "Callable[[Base[list[S]]], S]" [builtins fixtures/list.pyi] [case testMixinAllowedWithProtocol] -from typing import TypeVar -from typing_extensions import Protocol +from typing import Protocol, TypeVar class Resource(Protocol): def close(self) -> int: ... @@ -910,8 +899,7 @@ class Bad: class CC(TweakFunc, Bad): pass # E: Definition of "func" in base class "TweakFunc" is incompatible with definition in base class "Bad" [case testBadClassLevelDecoratorHack] -from typing_extensions import Protocol -from typing import TypeVar, Any +from typing import Protocol, TypeVar, Any class FuncLike(Protocol): __call__: Any @@ -975,7 +963,7 @@ c: Lnk[int, float] = Lnk() d: Lnk[str, float] = b >> c # OK e: Lnk[str, Tuple[int, float]] = a >> (b, c) # OK -f: Lnk[str, Tuple[float, int]] = a >> (c, b) # E: Unsupported operand types for >> ("Lnk[str, Tuple[str, int]]" and "Tuple[Lnk[int, float], Lnk[str, int]]") +f: Lnk[str, Tuple[float, int]] = a >> (c, b) # E: Unsupported operand types for >> ("Lnk[str, tuple[str, int]]" and "tuple[Lnk[int, float], Lnk[str, int]]") [builtins fixtures/tuple.pyi] [case testSelfTypeMutuallyExclusiveRestrictions] @@ -1031,7 +1019,7 @@ class Bad(metaclass=Meta): pass Good.do_x() -Bad.do_x() # E: Invalid self argument "Type[Bad]" to attribute function "do_x" with type "Callable[[Type[T]], T]" +Bad.do_x() # E: Invalid self argument "type[Bad]" to attribute function "do_x" with type "Callable[[type[T]], T]" [case testSelfTypeProtocolClassmethodMatch] from typing import Type, TypeVar, Protocol @@ -1132,7 +1120,7 @@ class C(Generic[T]): class D(Generic[V]): def f(self) -> None: - reveal_type(C[Tuple[V, str]]().magic()) # N: Revealed type is "Tuple[Tuple[V`1, builtins.str], V`1, builtins.str]" + reveal_type(C[Tuple[V, str]]().magic()) # N: Revealed type is "tuple[tuple[V`1, builtins.str], V`1, builtins.str]" [builtins fixtures/tuple.pyi] [case testSelfTypeOnUnion] @@ -1426,7 +1414,7 @@ class C(Generic[T]): def f(self) -> None: for x, y in Z(self.a, self.b): - reveal_type((x, y)) # N: Revealed type is "Tuple[T`1, builtins.str]" + reveal_type((x, y)) # N: Revealed type is "tuple[T`1, builtins.str]" [builtins fixtures/tuple.pyi] [case testEnumerateReturningSelfFromIter] @@ -1520,7 +1508,7 @@ from typing import Self, TypeVar, Tuple T = TypeVar("T") class C: def meth(self: T) -> Tuple[Self, T]: ... # E: Method cannot have explicit self annotation and Self type -reveal_type(C().meth()) # N: Revealed type is "Tuple[Never, __main__.C]" +reveal_type(C().meth()) # N: Revealed type is "tuple[Never, __main__.C]" [builtins fixtures/property.pyi] [case testTypingSelfProperty] @@ -1583,7 +1571,7 @@ Pairs = List[Tuple[T, T]] class C(Generic[T]): def pairs(self) -> Pairs[Self]: ... class D(C[T]): ... -reveal_type(D[int]().pairs()) # N: Revealed type is "builtins.list[Tuple[__main__.D[builtins.int], __main__.D[builtins.int]]]" +reveal_type(D[int]().pairs()) # N: Revealed type is "builtins.list[tuple[__main__.D[builtins.int], __main__.D[builtins.int]]]" [builtins fixtures/tuple.pyi] [case testTypingSelfOverrideVar] @@ -1621,11 +1609,11 @@ class C(Generic[T]): def __init__(self, val: T) -> None: ... @classmethod def pair(cls, val: T) -> Tuple[Self, Self]: - return (cls(val), C(val)) # E: Incompatible return value type (got "Tuple[Self, C[T]]", expected "Tuple[Self, Self]") + return (cls(val), C(val)) # E: Incompatible return value type (got "tuple[Self, C[T]]", expected "tuple[Self, Self]") class D(C[int]): pass -reveal_type(C.pair(42)) # N: Revealed type is "Tuple[__main__.C[builtins.int], __main__.C[builtins.int]]" -reveal_type(D.pair("no")) # N: Revealed type is "Tuple[__main__.D, __main__.D]" \ +reveal_type(C.pair(42)) # N: Revealed type is "tuple[__main__.C[builtins.int], __main__.C[builtins.int]]" +reveal_type(D.pair("no")) # N: Revealed type is "tuple[__main__.D, __main__.D]" \ # E: Argument 1 to "pair" of "C" has incompatible type "str"; expected "int" [builtins fixtures/classmethod.pyi] @@ -1642,8 +1630,8 @@ class D(C[int]): ... c: C[int] d: D -reveal_type(c.meth("test")) # N: Revealed type is "Tuple[__main__.C[builtins.int], builtins.str, builtins.int]" -reveal_type(d.meth("test")) # N: Revealed type is "Tuple[__main__.D, builtins.str, builtins.int]" +reveal_type(c.meth("test")) # N: Revealed type is "tuple[__main__.C[builtins.int], builtins.str, builtins.int]" +reveal_type(d.meth("test")) # N: Revealed type is "tuple[__main__.D, builtins.str, builtins.int]" [builtins fixtures/tuple.pyi] [case testTypingSelfRecursiveInit] @@ -1719,7 +1707,6 @@ class C: [builtins fixtures/classmethod.pyi] [case testTypingSelfRedundantAllowed_pep585] -# flags: --python-version 3.9 from typing import Self class C: @@ -1754,7 +1741,6 @@ class C: [builtins fixtures/classmethod.pyi] [case testTypingSelfRedundantWarning_pep585] -# flags: --python-version 3.9 # mypy: enable-error-code="redundant-self" from typing import Self @@ -1793,8 +1779,8 @@ class C: def bar(self) -> Self: ... def foo(self, x: S) -> Tuple[Self, S]: ... -reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> Tuple[Self`1, S`2]" -reveal_type(C().foo(42)) # N: Revealed type is "Tuple[__main__.C, builtins.int]" +reveal_type(C.foo) # N: Revealed type is "def [Self <: __main__.C, S] (self: Self`1, x: S`2) -> tuple[Self`1, S`2]" +reveal_type(C().foo(42)) # N: Revealed type is "tuple[__main__.C, builtins.int]" [builtins fixtures/tuple.pyi] [case testTypingSelfTypeVarClashAttr] @@ -1807,8 +1793,8 @@ class C: def bar(self) -> Self: ... foo: Callable[[S, Self], Tuple[Self, S]] -reveal_type(C().foo) # N: Revealed type is "def [S] (S`1, __main__.C) -> Tuple[__main__.C, S`1]" -reveal_type(C().foo(42, C())) # N: Revealed type is "Tuple[__main__.C, builtins.int]" +reveal_type(C().foo) # N: Revealed type is "def [S] (S`2, __main__.C) -> tuple[__main__.C, S`2]" +reveal_type(C().foo(42, C())) # N: Revealed type is "tuple[__main__.C, builtins.int]" class This: ... [builtins fixtures/tuple.pyi] @@ -1903,7 +1889,7 @@ class C: class D(C): ... -reveal_type(D.f) # N: Revealed type is "def [T] (T`1) -> T`1" +reveal_type(D.f) # N: Revealed type is "def [T] (T`3) -> T`3" reveal_type(D().f) # N: Revealed type is "def () -> __main__.D" [case testTypingSelfOnSuperTypeVarValues] @@ -2032,7 +2018,7 @@ class Ben(Object): } @classmethod def doit(cls) -> Foo: - reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`4) -> Self`4]" + reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`1) -> Self`1]" foo_method = cls.MY_MAP["foo"] return foo_method(Foo()) [builtins fixtures/isinstancelist.pyi] @@ -2117,9 +2103,9 @@ class C(Tuple[int, str]): return reveal_type(self.y) # N: Revealed type is "Self`0" c: C -reveal_type(c.x) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]" -reveal_type(c.y) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]" -reveal_type(C.y) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.C]" +reveal_type(c.x) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C]" +reveal_type(c.y) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C]" +reveal_type(C.y) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.C]" C.x # E: Access to generic instance variables via class is ambiguous [builtins fixtures/classmethod.pyi] @@ -2160,3 +2146,235 @@ class MyProtocol(Protocol): 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] + +[case testSelfInMultipleInheritance] +from typing_extensions import Self + +class A: + foo: int + def method(self: Self, other: Self) -> None: + self.foo + other.foo + +class B: + bar: str + def method(self: Self, other: Self) -> None: + self.bar + other.bar + +class C(A, B): # OK: both methods take Self + pass +[builtins fixtures/tuple.pyi] + +[case testSelfTypeClassMethodNotSilentlyErased] +from typing import Self, Optional + +class X: + _inst: Optional[Self] = None + @classmethod + def default(cls) -> Self: + reveal_type(cls._inst) # N: Revealed type is "Union[Self`0, None]" + if cls._inst is None: + cls._inst = cls() + return cls._inst + +reveal_type(X._inst) # E: Access to generic instance variables via class is ambiguous \ + # N: Revealed type is "Union[__main__.X, None]" +reveal_type(X()._inst) # N: Revealed type is "Union[__main__.X, None]" + +class Y(X): ... +reveal_type(Y._inst) # E: Access to generic instance variables via class is ambiguous \ + # N: Revealed type is "Union[__main__.Y, None]" +reveal_type(Y()._inst) # N: Revealed type is "Union[__main__.Y, None]" +[builtins fixtures/tuple.pyi] + +[case testSelfInFuncDecoratedClassmethod] +from collections.abc import Callable +from typing import Self, TypeVar + +T = TypeVar("T") + +def debug(make: Callable[[type[T]], T]) -> Callable[[type[T]], T]: + return make + +class Foo: + @classmethod + @debug + def make(cls) -> Self: + return cls() + +class Bar(Foo): ... + +reveal_type(Foo.make()) # N: Revealed type is "__main__.Foo" +reveal_type(Foo().make()) # N: Revealed type is "__main__.Foo" +reveal_type(Bar.make()) # N: Revealed type is "__main__.Bar" +reveal_type(Bar().make()) # N: Revealed type is "__main__.Bar" +[builtins fixtures/tuple.pyi] + +[case testSelfInClassDecoratedClassmethod] +from typing import Callable, Generic, TypeVar, Self + +T = TypeVar("T") + +class W(Generic[T]): + def __init__(self, fn: Callable[..., T]) -> None: ... + def __call__(self) -> T: ... + +class Check: + @W + def foo(self) -> Self: + ... + +reveal_type(Check.foo()) # N: Revealed type is "def () -> __main__.Check" +reveal_type(Check().foo()) # N: Revealed type is "__main__.Check" +[builtins fixtures/tuple.pyi] + +[case testSelfInClassmethodWithOtherSelfMethod] +from typing import Any, Callable, Self, TypeVar + +_C = TypeVar("_C", bound=Callable[..., Any]) + +def identity(func: _C, /) -> _C: + return func + +class A: + def meth(self) -> Self: ... + + @classmethod + def other_meth(cls) -> Self: + reveal_type(cls.meth) # N: Revealed type is "def [Self <: __main__.A] (self: Self`1) -> Self`1" + reveal_type(A.meth) # N: Revealed type is "def [Self <: __main__.A] (self: Self`2) -> Self`2" + return cls().meth() + +class B: + @identity + def meth(self) -> Self: ... + + @classmethod + def other_meth(cls) -> Self: + reveal_type(cls.meth) # N: Revealed type is "def [Self <: __main__.B] (self: Self`5) -> Self`5" + reveal_type(B.meth) # N: Revealed type is "def [Self <: __main__.B] (self: Self`6) -> Self`6" + return cls().meth() + +class C: + @classmethod + def other_meth(cls) -> Self: ... + + def meth(self) -> Self: + reveal_type(self.other_meth) # N: Revealed type is "def () -> Self`0" + reveal_type(type(self).other_meth) # N: Revealed type is "def () -> Self`0" + return self.other_meth() +[builtins fixtures/tuple.pyi] + +[case testSelfTypeUpperBoundFiler] +from typing import Generic, TypeVar, overload, Sequence + +class B: ... +class C(B): ... + +TB = TypeVar("TB", bound=B) +TC = TypeVar("TC", bound=C) + +class G(Generic[TB]): + @overload + def test(self: G[TC]) -> list[TC]: ... + @overload + def test(self: G[TB]) -> Sequence[TB]: ... + def test(self): + ... + +class D1(B): ... +class D2(C): ... + +gb: G[D1] +gc: G[D2] + +reveal_type(gb.test()) # N: Revealed type is "typing.Sequence[__main__.D1]" +reveal_type(gc.test()) # N: Revealed type is "builtins.list[__main__.D2]" +[builtins fixtures/list.pyi] + +[case testEnumImplicitlyFinalForSelfType] +from enum import Enum +from typing import Self + +# This enum has members and so is implicitly final. +# Foo and Self are interchangeable within the class. +class Foo(Enum): + A = 1 + + @classmethod + def foo(cls) -> Self: + return Foo.A + + @classmethod + def foo2(cls) -> Self: + return cls.bar() + + @classmethod + def bar(cls) -> Foo: + ... + +# This enum is empty and should not be assignable to Self +class Bar(Enum): + @classmethod + def foo(cls) -> Self: + return cls.bar() # E: Incompatible return value type (got "Bar", expected "Self") + + @classmethod + def bar(cls) -> Bar: + ... +[builtins fixtures/classmethod.pyi] diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index d7ab272aed6cf..52abbf09f1e53 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -137,8 +137,7 @@ def f() -> None: ... # E: Name "f" already defined (possibly by an import) [out] [case testRuntimeProtoTwoBases] -from typing_extensions import Protocol, runtime_checkable -from typing import TypeVar, Generic +from typing import TypeVar, Generic, Protocol, runtime_checkable T = TypeVar('T') @@ -151,6 +150,7 @@ class C: x: P[int] = C() [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testSemanalDoesNotLeakSyntheticTypes] # flags: --cache-fine-grained diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index 81da94c0591c6..1498c8d82826c 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -31,7 +31,7 @@ x = '' -- We only do the following two sections once here to avoid repetition. -- Most other test cases are similar. [rechecked a] -[stale] +[stale a] [out2] tmp/a.py:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -158,6 +158,7 @@ def f(__x: int) -> None: pass [out2] tmp/a.py:3: error: Argument 1 to "f" has incompatible type "str"; expected "int" tmp/a.py:4: error: Unexpected keyword argument "__x" for "f" +tmp/b.py: note: "f" defined here [case testSerializeArgumentKindsErrors] import a @@ -223,6 +224,7 @@ def f(x: int) -> int: pass [out2] tmp/a.py:2: note: Revealed type is "builtins.str" tmp/a.py:3: error: Unexpected keyword argument "x" for "f" +tmp/b.py: note: "f" defined here [case testSerializeTypeGuardFunction] import a @@ -547,7 +549,7 @@ class A(Tuple[int, str]): [builtins fixtures/tuple.pyi] [out2] tmp/a.py:3: error: Too many arguments for "f" of "A" -tmp/a.py:4: note: Revealed type is "Tuple[builtins.int, builtins.str]" +tmp/a.py:4: note: Revealed type is "tuple[builtins.int, builtins.str]" [case testSerializeVariableLengthTupleBaseClass] import a @@ -565,7 +567,7 @@ class A(Tuple[int, ...]): [builtins fixtures/tuple.pyi] [out2] tmp/a.py:3: error: Too many arguments for "f" of "A" -tmp/a.py:4: note: Revealed type is "Tuple[builtins.int, builtins.int]" +tmp/a.py:4: note: Revealed type is "tuple[builtins.int, builtins.int]" [case testSerializePlainTupleBaseClass] import a @@ -583,7 +585,7 @@ class A(tuple): [builtins fixtures/tuple.pyi] [out2] tmp/a.py:3: error: Too many arguments for "f" of "A" -tmp/a.py:4: note: Revealed type is "Tuple[Any, Any]" +tmp/a.py:4: note: Revealed type is "tuple[Any, Any]" [case testSerializeNamedTupleBaseClass] import a @@ -602,8 +604,8 @@ class A(NamedTuple('N', [('x', int), ('y', str)])): [builtins fixtures/tuple.pyi] [out2] tmp/a.py:3: error: Too many arguments for "f" of "A" -tmp/a.py:4: note: Revealed type is "Tuple[builtins.int, builtins.str]" -tmp/a.py:5: note: Revealed type is "Tuple[builtins.int, builtins.str]" +tmp/a.py:4: note: Revealed type is "tuple[builtins.int, builtins.str]" +tmp/a.py:5: note: Revealed type is "tuple[builtins.int, builtins.str]" [case testSerializeAnyBaseClass] import a @@ -727,13 +729,13 @@ class C: self.c = A [builtins fixtures/tuple.pyi] [out1] -main:2: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:3: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:4: note: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.C.A@4]" [out2] -main:2: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:3: note: Revealed type is "Tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:4: note: Revealed type is "def (x: builtins.int) -> Tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.C.A@4]" -- -- Strict optional @@ -941,9 +943,9 @@ N = NamedTuple('N', [('x', int)]) x: N [builtins fixtures/tuple.pyi] [out2] -tmp/a.py:5: error: Incompatible types in assignment (expression has type "Tuple[int]", variable has type "N") -tmp/a.py:6: error: Incompatible types in assignment (expression has type "Tuple[int]", variable has type "N") -tmp/a.py:9: note: Revealed type is "Tuple[builtins.int, fallback=b.N]" +tmp/a.py:5: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "N") +tmp/a.py:6: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "N") +tmp/a.py:9: note: Revealed type is "tuple[builtins.int, fallback=b.N]" tmp/a.py:10: note: Revealed type is "builtins.int" tmp/a.py:11: error: Argument "x" to "N" has incompatible type "str"; expected "int" @@ -993,9 +995,9 @@ tmp/a.py:9: note: Revealed type is "b.DD" tmp/a.py:10: note: Revealed type is "Any" tmp/a.py:11: note: Revealed type is "Union[builtins.int, builtins.str]" tmp/a.py:12: note: Revealed type is "builtins.list[builtins.int]" -tmp/a.py:13: note: Revealed type is "Tuple[builtins.int, builtins.str]" +tmp/a.py:13: note: Revealed type is "tuple[builtins.int, builtins.str]" tmp/a.py:14: note: Revealed type is "def (builtins.int) -> builtins.str" -tmp/a.py:15: note: Revealed type is "Type[builtins.int]" +tmp/a.py:15: note: Revealed type is "type[builtins.int]" tmp/a.py:17: note: Revealed type is "def (*Any, **Any) -> builtins.str" tmp/a.py:19: note: Revealed type is "builtins.type" @@ -1010,9 +1012,9 @@ X = TypeVar('X') Y = Tuple[X, str] [builtins fixtures/tuple.pyi] [out1] -main:4: note: Revealed type is "Tuple[builtins.int, builtins.str]" +main:4: note: Revealed type is "tuple[builtins.int, builtins.str]" [out2] -main:4: note: Revealed type is "Tuple[builtins.int, builtins.str]" +main:4: note: Revealed type is "tuple[builtins.int, builtins.str]" [case testSerializeTuple] # Don't repreat types tested by testSerializeTypeAliases here. @@ -1054,7 +1056,7 @@ reveal_type(C().a) reveal_type(C().b) reveal_type(C().c) [file ntcrash.py] -from mypy_extensions import TypedDict +from typing import TypedDict class C: def __init__(self) -> None: A = TypedDict('A', {'x': int}) @@ -1062,6 +1064,7 @@ class C: self.b = A(x=0) # type: A self.c = A [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:2: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" main:3: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" @@ -1075,10 +1078,11 @@ main:4: note: Revealed type is "def (*, x: builtins.int) -> TypedDict('ntcrash.C from m import d reveal_type(d) [file m.py] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) d: D [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out1] main:2: note: Revealed type is "TypedDict('m.D', {'x'?: builtins.int, 'y'?: builtins.str})" [out2] diff --git a/test-data/unit/check-slots.test b/test-data/unit/check-slots.test index b7ce5e596101a..25dd630e1cbed 100644 --- a/test-data/unit/check-slots.test +++ b/test-data/unit/check-slots.test @@ -180,6 +180,7 @@ b.m = 2 b.b = 2 b._two = 2 [out] +main:5: error: Class "B" has incompatible disjoint bases main:11: error: Trying to assign name "_one" that is not in "__slots__" of type "__main__.B" main:16: error: "B" has no attribute "b" main:17: error: "B" has no attribute "_two" @@ -496,6 +497,29 @@ class A: self.missing = 3 [builtins fixtures/dict.pyi] +[case testSlotsNotInClass] +# Shouldn't be triggered +__slots__ = [1, 2] +reveal_type(__slots__) # N: Revealed type is "builtins.list[builtins.int]" + +def foo() -> None: + __slots__ = 1 + reveal_type(__slots__) # N: Revealed type is "builtins.int" + +[case testSlotsEmptyList] +class A: + __slots__ = [] + reveal_type(__slots__) # N: Revealed type is "builtins.list[builtins.str]" + +reveal_type(A.__slots__) # N: Revealed type is "builtins.list[builtins.str]" + +[case testSlotsEmptySet] +class A: + __slots__ = set() + reveal_type(__slots__) # N: Revealed type is "builtins.set[builtins.str]" + +reveal_type(A.__slots__) # N: Revealed type is "builtins.set[builtins.str]" +[builtins fixtures/set.pyi] [case testSlotsWithAny] from typing import Any @@ -521,3 +545,19 @@ x = X() X.a # E: "a" in __slots__ conflicts with class variable access x.a [builtins fixtures/tuple.pyi] + +[case testSlotsOnSelfType] +from typing_extensions import Self + +class X: + __slots__ = ("foo",) + foo: int + + def method1(self: Self) -> Self: + self.bar = 0 # E: Trying to assign name "bar" that is not in "__slots__" of type "__main__.X" + return self + + def method2(self) -> Self: + self.bar = 0 # E: Trying to assign name "bar" that is not in "__slots__" of type "__main__.X" + return self +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index d1464423e90f0..87d015f3de0f8 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: Exception must be derived from BaseException; did you mean "NotImplementedError"? +[builtins fixtures/notimplemented.pyi] + [case testTryFinallyStatement] import typing try: @@ -1298,13 +1305,13 @@ def g() -> Iterator[List[int]]: yield [2, 3, 4] def f() -> Iterator[List[int]]: yield from g() - yield from [1, 2, 3] # E: Incompatible types in "yield from" (actual type "int", expected type "List[int]") + yield from [1, 2, 3] # E: Incompatible types in "yield from" (actual type "int", expected type "list[int]") [builtins fixtures/for.pyi] [out] [case testYieldFromNotAppliedToNothing] def h(): - yield from # E: invalid syntax + yield from # E: Invalid syntax [out] [case testYieldFromAndYieldTogether] @@ -1469,7 +1476,7 @@ with A(): with A() as a: # type: Tuple[int, int] pass -with A() as b: # type: Tuple[int, str] # E: Incompatible types in assignment (expression has type "Tuple[int, int]", variable has type "Tuple[int, str]") +with A() as b: # type: Tuple[int, str] # E: Incompatible types in assignment (expression has type "tuple[int, int]", variable has type "tuple[int, str]") pass with A() as (c, d): # type: int, int @@ -1520,13 +1527,13 @@ from typing import Optional class InvalidReturn1: def __exit__(self, x, y, z) -> bool: # E: "bool" is invalid as return type for "__exit__" that always returns False \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions return False class InvalidReturn2: def __exit__(self, x, y, z) -> Optional[bool]: # E: "bool" is invalid as return type for "__exit__" that always returns False \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions if int(): return False @@ -1535,7 +1542,7 @@ class InvalidReturn2: class InvalidReturn3: def __exit__(self, x, y, z) -> bool: # E: "bool" is invalid as return type for "__exit__" that always returns False \ -# N: Use "typing_extensions.Literal[False]" as the return type or change it to "None" \ +# N: Use "typing.Literal[False]" as the return type or change it to "None" \ # N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions def nested() -> bool: return True @@ -1543,7 +1550,7 @@ class InvalidReturn3: [builtins fixtures/bool.pyi] [case testWithStmtBoolExitReturnOkay] -from typing_extensions import Literal +from typing import Literal class GoodReturn1: def __exit__(self, x, y, z) -> bool: @@ -2022,7 +2029,7 @@ cs: List[B] if int(): *bs, b = bs if int(): - *bs, c = cs # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") + *bs, c = cs # E: Incompatible types in assignment (expression has type "list[B]", variable has type "list[A]") if int(): *ns, c = cs if int(): @@ -2175,8 +2182,7 @@ class M(N): pass [out] [case testForwardRefsInWithStatementImplicit] -from typing import ContextManager, Any -from mypy_extensions import TypedDict +from typing import ContextManager, Any, TypedDict cm: ContextManager[N] with cm as g: @@ -2184,12 +2190,11 @@ with cm as g: N = TypedDict('N', {'x': int}) [builtins fixtures/dict.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-full.pyi] [out] [case testForwardRefsInWithStatement] -from typing import ContextManager, Any -from mypy_extensions import TypedDict +from typing import ContextManager, Any, TypedDict cm: ContextManager[Any] with cm as g: # type: N @@ -2197,7 +2202,7 @@ with cm as g: # type: N N = TypedDict('N', {'x': int}) [builtins fixtures/dict.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-full.pyi] [out] [case testGlobalWithoutInitialization] @@ -2276,6 +2281,20 @@ def get_strings(foo: bool) -> Iterator[str]: yield "bar2" [builtins fixtures/tuple.pyi] +[case testYieldFromInvalidType] +from collections.abc import Iterator + +class A: + def list(self) -> None: ... + + def foo(self) -> list[int]: # E: Function "__main__.A.list" is not valid as a type \ + # N: Perhaps you need "Callable[...]" or a callback protocol? + return [] + +def fn() -> Iterator[int]: + yield from A().foo() # E: "list?[builtins.int]" has no attribute "__iter__" (not iterable) +[builtins fixtures/tuple.pyi] + [case testNoCrashOnStarRightHandSide] x = *(1, 2, 3) # E: can't use starred expression here [builtins fixtures/tuple.pyi] @@ -2339,6 +2358,6 @@ describe(CAny()) describe(C()) describe(CNone()) describe(CWrong()) # E: Argument 1 to "describe" has incompatible type "CWrong"; expected "Callable[[], None]" \ - # N: "CWrong.__call__" has type "Callable[[Arg(int, 'x')], None]" + # N: "CWrong.__call__" has type "def __call__(self, x: int) -> None" describe(f) [builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 972bccf8c24b1..cfdd2aacc4d25 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -11,15 +11,15 @@ t4: Tuple[A, B] t5: Tuple[B, A] if int(): - t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[B]", variable has type "Tuple[A]") + t1 = t2 # E: Incompatible types in assignment (expression has type "tuple[B]", variable has type "tuple[A]") if int(): - t1 = t3 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") + t1 = t3 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A]") if int(): - t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A]", variable has type "Tuple[A, A]") + t3 = t1 # E: Incompatible types in assignment (expression has type "tuple[A]", variable has type "tuple[A, A]") if int(): - t3 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[A, A]") + t3 = t4 # E: Incompatible types in assignment (expression has type "tuple[A, B]", variable has type "tuple[A, A]") if int(): - t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, A]") + t3 = t5 # E: Incompatible types in assignment (expression has type "tuple[B, A]", variable has type "tuple[A, A]") # Ok if int(): @@ -44,10 +44,10 @@ t2: Tuple[A, B] t3: Tuple[B, A] if int(): - t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") - t2 = t3 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, B]") - t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[B, A]") - t3 = t2 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[B, A]") + t2 = t1 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A, B]") + t2 = t3 # E: Incompatible types in assignment (expression has type "tuple[B, A]", variable has type "tuple[A, B]") + t3 = t1 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[B, A]") + t3 = t2 # E: Incompatible types in assignment (expression has type "tuple[A, B]", variable has type "tuple[B, A]") t1 = t2 t1 = t3 @@ -63,11 +63,11 @@ a, o = None, None # type: (A, object) t = None # type: Tuple[A, A] if int(): - a = t # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "A") + a = t # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "A") if int(): - t = o # E: Incompatible types in assignment (expression has type "object", variable has type "Tuple[A, A]") + t = o # E: Incompatible types in assignment (expression has type "object", variable has type "tuple[A, A]") if int(): - t = a # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, A]") + t = a # E: Incompatible types in assignment (expression has type "A", variable has type "tuple[A, A]") # TODO: callable types + tuples # Ok @@ -85,7 +85,7 @@ t1: Tuple[A, Tuple[A, A]] t2: Tuple[B, Tuple[B, B]] if int(): - t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") + t2 = t1 # E: Incompatible types in assignment (expression has type "tuple[A, tuple[A, A]]", variable has type "tuple[B, tuple[B, B]]") if int(): t1 = t2 @@ -99,7 +99,7 @@ t1: Tuple[A, Tuple[A, A]] t2: Tuple[B, Tuple[B, B]] if int(): - t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") + t2 = t1 # E: Incompatible types in assignment (expression has type "tuple[A, tuple[A, A]]", variable has type "tuple[B, tuple[B, B]]") if int(): t1 = t2 @@ -139,18 +139,18 @@ def takes_tuple_aa(t: tuple[A, A]): ... takes_tuple_aa(tuple_aa) takes_tuple_aa(Tuple_aa) -takes_tuple_aa(tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]" -takes_tuple_aa(Tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]" -takes_tuple_aa(tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]" -takes_tuple_aa(Tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]" -takes_tuple_aa(tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]" -takes_tuple_aa(Tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]" +takes_tuple_aa(tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, ...]"; expected "tuple[A, A]" +takes_tuple_aa(Tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, ...]"; expected "tuple[A, A]" +takes_tuple_aa(tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object]"; expected "tuple[A, A]" +takes_tuple_aa(Tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object]"; expected "tuple[A, A]" +takes_tuple_aa(tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, object]"; expected "tuple[A, A]" +takes_tuple_aa(Tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[object, object]"; expected "tuple[A, A]" takes_tuple_aa(tuple_any_implicit) takes_tuple_aa(Tuple_any_implicit) takes_tuple_aa(tuple_any) takes_tuple_aa(Tuple_any) -takes_tuple_aa(tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]" -takes_tuple_aa(Tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]" +takes_tuple_aa(tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[Any]"; expected "tuple[A, A]" +takes_tuple_aa(Tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple[Any]"; expected "tuple[A, A]" takes_tuple_aa(tuple_any_two) takes_tuple_aa(Tuple_any_two) @@ -175,22 +175,22 @@ takes_tuple_any_implicit(Tuple_any_two) def takes_tuple_any_one(t: tuple[Any]): ... -takes_tuple_any_one(tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]" -takes_tuple_any_one(Tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]" -takes_tuple_any_one(tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]" -takes_tuple_any_one(Tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]" +takes_tuple_any_one(tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[A, A]"; expected "tuple[Any]" +takes_tuple_any_one(Tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[A, A]"; expected "tuple[Any]" +takes_tuple_any_one(tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, ...]"; expected "tuple[Any]" +takes_tuple_any_one(Tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, ...]"; expected "tuple[Any]" takes_tuple_any_one(tuple_obj_one) takes_tuple_any_one(Tuple_obj_one) -takes_tuple_any_one(tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]" -takes_tuple_any_one(Tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]" +takes_tuple_any_one(tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, object]"; expected "tuple[Any]" +takes_tuple_any_one(Tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[object, object]"; expected "tuple[Any]" takes_tuple_any_one(tuple_any_implicit) takes_tuple_any_one(Tuple_any_implicit) takes_tuple_any_one(tuple_any) takes_tuple_any_one(Tuple_any) takes_tuple_any_one(tuple_any_one) takes_tuple_any_one(Tuple_any_one) -takes_tuple_any_one(tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]" -takes_tuple_any_one(Tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]" +takes_tuple_any_one(tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[Any, Any]"; expected "tuple[Any]" +takes_tuple_any_one(Tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "tuple[Any, Any]"; expected "tuple[Any]" class A: pass [builtins fixtures/tuple.pyi] @@ -229,15 +229,15 @@ def takes_tuple_aa(t: Tuple[A, A]): ... takes_tuple_aa(inst_tuple_aa) takes_tuple_aa(inst_tuple_aa_subclass) takes_tuple_aa(inst_tuple_any_subclass) -takes_tuple_aa(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_one_subclass"; expected "Tuple[A, A]" +takes_tuple_aa(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_one_subclass"; expected "tuple[A, A]" takes_tuple_aa(inst_tuple_any_two_subclass) -takes_tuple_aa(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "Tuple[A, A]" -takes_tuple_aa(inst_tuple_obj_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_one_subclass"; expected "Tuple[A, A]" -takes_tuple_aa(inst_tuple_obj_two_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_two_subclass"; expected "Tuple[A, A]" +takes_tuple_aa(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "tuple[A, A]" +takes_tuple_aa(inst_tuple_obj_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_one_subclass"; expected "tuple[A, A]" +takes_tuple_aa(inst_tuple_obj_two_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_two_subclass"; expected "tuple[A, A]" def takes_tuple_aa_subclass(t: tuple_aa_subclass): ... -takes_tuple_aa_subclass(inst_tuple_aa) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "Tuple[A, A]"; expected "tuple_aa_subclass" +takes_tuple_aa_subclass(inst_tuple_aa) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple[A, A]"; expected "tuple_aa_subclass" takes_tuple_aa_subclass(inst_tuple_aa_subclass) takes_tuple_aa_subclass(inst_tuple_any_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_subclass"; expected "tuple_aa_subclass" takes_tuple_aa_subclass(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_one_subclass"; expected "tuple_aa_subclass" @@ -271,15 +271,15 @@ t3 = None # type: Tuple[A, B] a, b, c = None, None, None # type: (A, B, C) if int(): - t2 = () # E: Incompatible types in assignment (expression has type "Tuple[()]", variable has type "Tuple[A]") + t2 = () # E: Incompatible types in assignment (expression has type "tuple[()]", variable has type "tuple[A]") if int(): - t2 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") + t2 = (a, a) # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A]") if int(): - t3 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") + t3 = (a, a) # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A, B]") if int(): - t3 = (b, b) # E: Incompatible types in assignment (expression has type "Tuple[B, B]", variable has type "Tuple[A, B]") + t3 = (b, b) # E: Incompatible types in assignment (expression has type "tuple[B, B]", variable has type "tuple[A, B]") if int(): - t3 = (a, b, a) # E: Incompatible types in assignment (expression has type "Tuple[A, B, A]", variable has type "Tuple[A, B]") + t3 = (a, b, a) # E: Incompatible types in assignment (expression has type "tuple[A, B, A]", variable has type "tuple[A, B]") t1 = () t1 = (a,) @@ -389,9 +389,9 @@ class B: pass t: Tuple[A, B] n = 0 -t[0] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]") -t[2] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]") -t[n] = A() # E: Unsupported target for indexed assignment ("Tuple[A, B]") +t[0] = A() # E: Unsupported target for indexed assignment ("tuple[A, B]") +t[2] = A() # E: Unsupported target for indexed assignment ("tuple[A, B]") +t[n] = A() # E: Unsupported target for indexed assignment ("tuple[A, B]") [builtins fixtures/tuple.pyi] @@ -618,6 +618,15 @@ u, v, w = r, s = 1, 1 # E: Need more than 2 values to unpack (3 expected) d, e = f, g, h = 1, 1 # E: Need more than 2 values to unpack (3 expected) [builtins fixtures/tuple.pyi] +[case testUnpackAssignmentWithStarExpr] +a: A +b: list[B] +if int(): + (a,) = (*b,) # E: Incompatible types in assignment (expression has type "B", variable has type "A") + +class A: pass +class B: pass + -- Assignment to starred expressions -- --------------------------------- @@ -626,8 +635,8 @@ d, e = f, g, h = 1, 1 # E: Need more than 2 values to unpack (3 expected) [case testAssignmentToStarMissingAnnotation] from typing import List t = 1, 2 -a, b, *c = 1, 2 # E: Need type annotation for "c" (hint: "c: List[] = ...") -aa, bb, *cc = t # E: Need type annotation for "cc" (hint: "cc: List[] = ...") +a, b, *c = 1, 2 # E: Need type annotation for "c" (hint: "c: list[] = ...") +aa, bb, *cc = t # E: Need type annotation for "cc" (hint: "cc: list[] = ...") [builtins fixtures/list.pyi] [case testAssignmentToStarAnnotation] @@ -636,7 +645,7 @@ from typing import List li, lo = None, None # type: List[int], List[object] a, b, *c = 1, 2 # type: int, int, List[int] if int(): - c = lo # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[int]") + c = lo # E: Incompatible types in assignment (expression has type "list[object]", variable has type "list[int]") if int(): c = li [builtins fixtures/list.pyi] @@ -707,7 +716,7 @@ if int(): a, *na = ta if int(): na = la - na = a # E: Incompatible types in assignment (expression has type "A", variable has type "List[A]") + na = a # E: Incompatible types in assignment (expression has type "A", variable has type "list[A]") class A: pass [builtins fixtures/list.pyi] @@ -719,7 +728,7 @@ li: List[int] la: List[A] a, *l = A(), A() if int(): - l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") + l = li # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]") if int(): l = la [builtins fixtures/list.pyi] @@ -734,7 +743,7 @@ li: List[int] la: List[A] a, *l = [A(), A()] if int(): - l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") + l = li # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]") if int(): l = la [builtins fixtures/list.pyi] @@ -747,7 +756,7 @@ la: List[A] ta: Tuple[A, A, A] a, *l = ta if int(): - l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") + l = li # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]") if int(): l = la @@ -761,7 +770,7 @@ li: List[int] la: List[A] a, *l = la if int(): - l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") + l = li # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[A]") if int(): l = la @@ -835,17 +844,17 @@ if int(): if int(): t, c2 = (a2, b2), c2 if int(): - t, c2 = (a2, a2), c2 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") + t, c2 = (a2, a2), c2 # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple[A, B]") if int(): - t = a1, a1, a1 # E: Incompatible types in assignment (expression has type "Tuple[A, A, A]", variable has type "Tuple[A, B]") + t = a1, a1, a1 # E: Incompatible types in assignment (expression has type "tuple[A, A, A]", variable has type "tuple[A, B]") if int(): - t = a1 # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, B]") + t = a1 # E: Incompatible types in assignment (expression has type "A", variable has type "tuple[A, B]") if int(): a2, a2, a2 = t # E: Need more than 2 values to unpack (3 expected) if int(): a2, = t # E: Too many values to unpack (1 expected, 2 provided) if int(): - a2 = t # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "A") + a2 = t # E: Incompatible types in assignment (expression has type "tuple[A, B]", variable has type "A") class A: pass class B: pass @@ -864,10 +873,10 @@ def f(x: 'A') -> None: pass a: A -(a, a) + a # E: Unsupported operand types for + ("Tuple[A, A]" and "A") -a + (a, a) # E: Unsupported operand types for + ("A" and "Tuple[A, A]") -f((a, a)) # E: Argument 1 to "f" has incompatible type "Tuple[A, A]"; expected "A" -(a, a).foo # E: "Tuple[A, A]" has no attribute "foo" +(a, a) + a # E: Unsupported operand types for + ("tuple[A, A]" and "A") +a + (a, a) # E: Unsupported operand types for + ("A" and "tuple[A, A]") +f((a, a)) # E: Argument 1 to "f" has incompatible type "tuple[A, A]"; expected "A" +(a, a).foo # E: "tuple[A, A]" has no attribute "foo" [builtins fixtures/tuple.pyi] [case testLargeTuplesInErrorMessages] @@ -879,7 +888,7 @@ class LongTypeName: def __add__(self, x: 'LongTypeName') -> 'LongTypeName': pass [builtins fixtures/tuple.pyi] [out] -main:3: error: Unsupported operand types for + ("LongTypeName" and "Tuple[LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName]") +main:3: error: Unsupported operand types for + ("LongTypeName" and "tuple[LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName, LongTypeName]") -- Tuple methods @@ -899,7 +908,7 @@ if int(): i = t.__str__() # E: Incompatible types in assignment (expression has type "str", variable has type "int") if int(): i = s in t # E: Incompatible types in assignment (expression has type "bool", variable has type "int") -t.foo # E: "Tuple[int, str]" has no attribute "foo" +t.foo # E: "tuple[int, str]" has no attribute "foo" if int(): i = t.__len__() @@ -1036,7 +1045,7 @@ from typing import TypeVar, Generic, Tuple T = TypeVar('T') class Test(Generic[T], Tuple[T]): pass x = Test() # type: Test[int] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.Test[builtins.int]]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.Test[builtins.int]]" [builtins fixtures/tuple.pyi] [out] @@ -1064,7 +1073,7 @@ tb = () # type: Tuple[B, ...] fa(ta) fa(tb) fb(tb) -fb(ta) # E: Argument 1 to "fb" has incompatible type "Tuple[A, ...]"; expected "Tuple[B, ...]" +fb(ta) # E: Argument 1 to "fb" has incompatible type "tuple[A, ...]"; expected "tuple[B, ...]" [builtins fixtures/tuple.pyi] [case testSubtypingFixedAndVariableLengthTuples] @@ -1080,8 +1089,8 @@ fa(aa) fa(ab) fa(bb) fb(bb) -fb(ab) # E: Argument 1 to "fb" has incompatible type "Tuple[A, B]"; expected "Tuple[B, ...]" -fb(aa) # E: Argument 1 to "fb" has incompatible type "Tuple[A, A]"; expected "Tuple[B, ...]" +fb(ab) # E: Argument 1 to "fb" has incompatible type "tuple[A, B]"; expected "tuple[B, ...]" +fb(aa) # E: Argument 1 to "fb" has incompatible type "tuple[A, A]"; expected "tuple[B, ...]" [builtins fixtures/tuple.pyi] [case testSubtypingTupleIsContainer] @@ -1102,7 +1111,7 @@ a = () a = (1, 2) b = (*a, '') -reveal_type(b) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]" +reveal_type(b) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr2] @@ -1115,7 +1124,7 @@ reveal_type(b) # N: Revealed type is "builtins.tuple[builtins.int, ...]" # flags: --enable-incomplete-feature=PreciseTupleTypes a = [1] b = (0, *a) -reveal_type(b) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(b) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr3] @@ -1130,9 +1139,9 @@ reveal_type(c) # N: Revealed type is "builtins.tuple[builtins.str, ...]" # flags: --enable-incomplete-feature=PreciseTupleTypes a = [''] b = (0, *a) -reveal_type(b) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]" +reveal_type(b) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]" c = (*a, '') -reveal_type(c) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.str]" +reveal_type(c) # N: Revealed type is "tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.str]" [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr4] @@ -1159,13 +1168,13 @@ class B: pass def f(x: Union[B, Tuple[A, A]]) -> None: if isinstance(x, tuple): - reveal_type(x) # N: Revealed type is "Tuple[__main__.A, __main__.A]" + reveal_type(x) # N: Revealed type is "tuple[__main__.A, __main__.A]" else: reveal_type(x) # N: Revealed type is "__main__.B" def g(x: Union[str, Tuple[str, str]]) -> None: if isinstance(x, tuple): - reveal_type(x) # N: Revealed type is "Tuple[builtins.str, builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.str, builtins.str]" else: reveal_type(x) # N: Revealed type is "builtins.str" @@ -1178,19 +1187,19 @@ from typing import Tuple, Union Pair = Tuple[int, int] Variant = Union[int, Pair] def tuplify(v: Variant) -> None: - reveal_type(v) # N: Revealed type is "Union[builtins.int, Tuple[builtins.int, builtins.int]]" + reveal_type(v) # N: Revealed type is "Union[builtins.int, tuple[builtins.int, builtins.int]]" if not isinstance(v, tuple): reveal_type(v) # N: Revealed type is "builtins.int" v = (v, v) - reveal_type(v) # N: Revealed type is "Tuple[builtins.int, builtins.int]" - reveal_type(v) # N: Revealed type is "Tuple[builtins.int, builtins.int]" + reveal_type(v) # N: Revealed type is "tuple[builtins.int, builtins.int]" + reveal_type(v) # N: Revealed type is "tuple[builtins.int, builtins.int]" reveal_type(v[0]) # N: Revealed type is "builtins.int" Pair2 = Tuple[int, str] Variant2 = Union[int, Pair2] def tuplify2(v: Variant2) -> None: if isinstance(v, tuple): - reveal_type(v) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + reveal_type(v) # N: Revealed type is "tuple[builtins.int, builtins.str]" else: reveal_type(v) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] @@ -1200,10 +1209,10 @@ def tuplify2(v: Variant2) -> None: from typing import Tuple, Union def good(blah: Union[Tuple[int, int], int]) -> None: - reveal_type(blah) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.int]" + reveal_type(blah) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.int]" if isinstance(blah, tuple): - reveal_type(blah) # N: Revealed type is "Tuple[builtins.int, builtins.int]" - reveal_type(blah) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], builtins.int]" + reveal_type(blah) # N: Revealed type is "tuple[builtins.int, builtins.int]" + reveal_type(blah) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], builtins.int]" [builtins fixtures/tuple.pyi] [out] @@ -1223,7 +1232,7 @@ def g(x: T) -> Tuple[T, T]: return (x, x) z = 1 -x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[B1, B2]" +x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "tuple[B1, B2]" [builtins fixtures/tuple.pyi] [out] @@ -1374,13 +1383,13 @@ reveal_type(join(subtup, tup2)) # N: Revealed type is "builtins.tuple[builtins. [case testTupleWithUndersizedContext] a = ([1], 'x') if int(): - a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[Never], str, int]", variable has type "Tuple[List[int], str]") + a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "tuple[list[Never], str, int]", variable has type "tuple[list[int], str]") [builtins fixtures/tuple.pyi] [case testTupleWithOversizedContext] a = (1, [1], 'x') if int(): - a = (1, []) # E: Incompatible types in assignment (expression has type "Tuple[int, List[int]]", variable has type "Tuple[int, List[int], str]") + a = (1, []) # E: Incompatible types in assignment (expression has type "tuple[int, list[int]]", variable has type "tuple[int, list[int], str]") [builtins fixtures/tuple.pyi] [case testTupleWithoutContext] @@ -1405,7 +1414,7 @@ def f(a: Tuple) -> None: pass f(()) f((1,)) f(('', '')) -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Any, ...]" +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[Any, ...]" [builtins fixtures/tuple.pyi] [case testTupleSingleton] @@ -1413,9 +1422,9 @@ f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Any, . from typing import Tuple def f(a: Tuple[()]) -> None: pass f(()) -f((1,)) # E: Argument 1 to "f" has incompatible type "Tuple[int]"; expected "Tuple[()]" -f(('', '')) # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[()]" -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[()]" +f((1,)) # E: Argument 1 to "f" has incompatible type "tuple[int]"; expected "tuple[()]" +f(('', '')) # E: Argument 1 to "f" has incompatible type "tuple[str, str]"; expected "tuple[()]" +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[()]" [builtins fixtures/tuple.pyi] [case testNonliteralTupleIndex] @@ -1426,7 +1435,7 @@ reveal_type(t[x]) # N: Revealed type is "Union[builtins.int, builtins.str]" t[y] # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> Union[int, str] \ - # N: def __getitem__(self, slice, /) -> Tuple[Union[int, str], ...] + # N: def __getitem__(self, slice, /) -> tuple[Union[int, str], ...] [builtins fixtures/tuple.pyi] @@ -1438,6 +1447,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') @@ -1461,7 +1476,7 @@ class C(Tuple[int, str]): def f(cls) -> None: pass t: Type[C] -t.g() # E: "Type[C]" has no attribute "g" +t.g() # E: "type[C]" has no attribute "g" t.f() [builtins fixtures/classmethod.pyi] @@ -1469,7 +1484,7 @@ t.f() from typing import Tuple def foo(o: CallableTuple) -> int: - reveal_type(o) # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple]" + reveal_type(o) # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple]" return o(1, 2) class CallableTuple(Tuple[str, int]): @@ -1483,7 +1498,7 @@ from typing import Generic, Tuple, TypeVar T = TypeVar('T') def foo(o: CallableTuple[int]) -> int: - reveal_type(o) # N: Revealed type is "Tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple[builtins.int]]" + reveal_type(o) # N: Revealed type is "tuple[builtins.str, builtins.int, fallback=__main__.CallableTuple[builtins.int]]" reveal_type(o.count(3)) # N: Revealed type is "builtins.int" return o(1, 2) @@ -1514,7 +1529,7 @@ from typing import Iterable, Tuple x: Iterable[int] = () y: Tuple[int, int] = (1, 2) x = y -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" [builtins fixtures/tuple.pyi] [case testTupleOverlapDifferentTuples] @@ -1526,9 +1541,9 @@ possibles: Tuple[int, Tuple[A]] x: Optional[Tuple[B]] if x in possibles: - reveal_type(x) # N: Revealed type is "Tuple[__main__.B]" + reveal_type(x) # N: Revealed type is "tuple[__main__.B]" else: - reveal_type(x) # N: Revealed type is "Union[Tuple[__main__.B], None]" + reveal_type(x) # N: Revealed type is "Union[tuple[__main__.B], None]" [builtins fixtures/tuple.pyi] @@ -1540,7 +1555,7 @@ reveal_type(tup[0]) # N: Revealed type is "builtins.int" reveal_type(tup[1]) # N: Revealed type is "Union[builtins.str, builtins.int]" reveal_type(tup[2]) # E: Tuple index out of range \ # N: Revealed type is "Union[Any, builtins.str]" -reveal_type(tup[:]) # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], Tuple[builtins.int, builtins.int, builtins.str]]" +reveal_type(tup[:]) # N: Revealed type is "Union[tuple[builtins.int, builtins.str], tuple[builtins.int, builtins.int, builtins.str]]" [builtins fixtures/tuple.pyi] @@ -1552,7 +1567,7 @@ reveal_type(tup[0]) # N: Revealed type is "builtins.int" reveal_type(tup[1]) # N: Revealed type is "Union[builtins.str, builtins.int]" reveal_type(tup[2]) # E: Tuple index out of range \ # N: Revealed type is "Union[Any, builtins.int]" -reveal_type(tup[:]) # N: Revealed type is "Union[Tuple[builtins.int, builtins.str], builtins.list[builtins.int]]" +reveal_type(tup[:]) # N: Revealed type is "Union[tuple[builtins.int, builtins.str], builtins.list[builtins.int]]" [builtins fixtures/tuple.pyi] @@ -1560,7 +1575,7 @@ reveal_type(tup[:]) # N: Revealed type is "Union[Tuple[builtins.int, builtins.s a = (1, "foo", 3) b = ("bar", 7) -reveal_type(a + b) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.int, builtins.str, builtins.int]" +reveal_type(a + b) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.int, builtins.str, builtins.int]" [builtins fixtures/tuple.pyi] @@ -1580,7 +1595,7 @@ t1: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") # E: # N: Expression tuple item 10 has type "str"; "int" expected; # short tuple initializer assignment -t2: Tuple[int, ...] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, ...]") +t2: Tuple[int, ...] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "tuple[int, int, str, int]", variable has type "tuple[int, ...]") # long initializer assignment with few mismatches, no ellipsis t3: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "str", "str") # E: Incompatible types in assignment (2 tuple items are incompatible) \ @@ -1594,13 +1609,90 @@ t4: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3 # N: Expression tuple item 10 has type "str"; "int" expected; # short tuple initializer assignment, no ellipsis -t5: Tuple[int, int] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, int]") +t5: Tuple[int, int] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "tuple[int, int, str, int]", variable has type "tuple[int, int]") # long initializer assignment with mismatched pairs -t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>]) +t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) # E: Incompatible types in assignment (expression has type "tuple[int, int, ... <15 more items>]", variable has type "tuple[int, int, ... <10 more items>]") [builtins fixtures/tuple.pyi] +[case testPropertyLongTupleReturnTypeMismatchUnion] +from typing import Tuple, Union +class A: + a: str + b: str + c: str + d: str + e: str + f: str + g: Union[str, int] + h: Union[str, float] + i: Union[str, None] + j: Union[str, None] + k: Union[str, None] + l: Union[str, None] + + @property + def x(self) -> Tuple[str, str, str, str, str, str, str, str, str, str, str, str]: + return ( + self.a, + self.b, + self.c, + self.d, + self.e, + self.f, + self.g, + self.h, + self.i, + self.j, + self.k, + self.l, + ) +[out] +main:18: error: Incompatible return value type (6 tuple items are incompatible; 3 items are omitted) +main:18: note: Expression tuple item 6 has type "Union[str, int]"; "str" expected; +main:18: note: Expression tuple item 7 has type "Union[str, float]"; "str" expected; +main:18: note: Expression tuple item 8 has type "Optional[str]"; "str" expected; +[builtins fixtures/property.pyi] + +[case testPropertyLongTupleReturnTypeMismatchUnionWiderExpected] +from typing import Tuple, Union +class A: + a: str + b: str + c: str + d: str + e: str + f: str + g: str + h: str + i: str + j: str + k: str + l: Union[float, int] + + @property + def x(self) -> Tuple[Union[str, int], Union[str, float], int, Union[str, None], Union[str, None], Union[str, None], str, str, str, str, str, str]: + return ( + self.a, + self.b, + self.c, + self.d, + self.e, + self.f, + self.g, + self.h, + self.i, + self.j, + self.k, + self.l, + ) +[out] +main:18: error: Incompatible return value type (2 tuple items are incompatible) +main:18: note: Expression tuple item 2 has type "str"; "int" expected; +main:18: note: Expression tuple item 11 has type "Union[float, int]"; "str" expected; +[builtins fixtures/property.pyi] + [case testTupleWithStarExpr] from typing import Tuple, List points = (1, "test") # type: Tuple[int, str] @@ -1648,11 +1740,11 @@ x9, y9, x10, y10, z5 = *points2, 1, *points2 # E: Contiguous iterable with same [case testMultiplyTupleByIntegerLiteral] from typing import Tuple t = ('',) * 2 -reveal_type(t) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(t) # N: Revealed type is "tuple[builtins.str, builtins.str]" t2 = ('',) * -1 -reveal_type(t2) # N: Revealed type is "Tuple[()]" +reveal_type(t2) # N: Revealed type is "tuple[()]" t3 = ('', 1) * 2 -reveal_type(t3) # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.str, builtins.int]" +reveal_type(t3) # N: Revealed type is "tuple[builtins.str, builtins.int, builtins.str, builtins.int]" def f() -> Tuple[str, ...]: return ('', ) reveal_type(f() * 2) # N: Revealed type is "builtins.tuple[builtins.str, ...]" @@ -1663,18 +1755,18 @@ from typing import Tuple def f() -> Tuple[()]: ... -reveal_type(f) # N: Revealed type is "def () -> Tuple[()]" -reveal_type(f()) # N: Revealed type is "Tuple[()]" +reveal_type(f) # N: Revealed type is "def () -> tuple[()]" +reveal_type(f()) # N: Revealed type is "tuple[()]" [builtins fixtures/tuple.pyi] [case testMultiplyTupleByIntegerLiteralReverse] from typing import Tuple t = 2 * ('',) -reveal_type(t) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(t) # N: Revealed type is "tuple[builtins.str, builtins.str]" t2 = -1 * ('',) -reveal_type(t2) # N: Revealed type is "Tuple[()]" +reveal_type(t2) # N: Revealed type is "tuple[()]" t3 = 2 * ('', 1) -reveal_type(t3) # N: Revealed type is "Tuple[builtins.str, builtins.int, builtins.str, builtins.int]" +reveal_type(t3) # N: Revealed type is "tuple[builtins.str, builtins.int, builtins.str, builtins.int]" def f() -> Tuple[str, ...]: return ('', ) reveal_type(2 * f()) # N: Revealed type is "builtins.tuple[builtins.str, ...]" @@ -1720,7 +1812,7 @@ def zip(i): ... def g(t: Tuple): reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[builtins.tuple[Any, ...]]" - reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Tuple[Any]]" + reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[tuple[Any]]" [builtins fixtures/tuple.pyi] [case testTupleSubclassSlice] @@ -1730,5 +1822,5 @@ class A: ... class tuple_aa_subclass(Tuple[A, A]): ... -inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:] # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "tuple_aa_subclass") +inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:] # E: Incompatible types in assignment (expression has type "tuple[A, A]", variable has type "tuple_aa_subclass") [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index c7b9694a91887..1fb2b038a1a18 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -12,7 +12,7 @@ U = Union[int, str] def f(x: U) -> None: pass f(1) f('') -f(()) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Union[int, str]" +f(()) # E: Argument 1 to "f" has incompatible type "tuple[()]"; expected "Union[int, str]" [targets __main__, __main__.f] [builtins fixtures/tuple.pyi] @@ -21,7 +21,7 @@ from typing import Tuple T = Tuple[int, str] def f(x: T) -> None: pass f((1, 'x')) -f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[int, str]" +f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[int, str]" [targets __main__, __main__.f] [builtins fixtures/tuple.pyi] @@ -64,7 +64,7 @@ from _m import U def f(x: U) -> None: pass f(1) f('x') -f(()) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Union[int, str]" +f(()) # E: Argument 1 to "f" has incompatible type "tuple[()]"; expected "Union[int, str]" [file _m.py] from typing import Union U = Union[int, str] @@ -73,7 +73,7 @@ U = Union[int, str] [case testProhibitReassigningAliases] A = float if int(): - A = int # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation + A = int # E: Cannot assign multiple types to name "A" without an explicit "type[...]" annotation [out] [case testProhibitReassigningSubscriptedAliases] @@ -81,7 +81,7 @@ from typing import Callable A = Callable[[], float] if int(): A = Callable[[], int] \ - # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ + # E: Cannot assign multiple types to name "A" without an explicit "type[...]" annotation \ # E: Value of type "int" is not indexable # the second error is because of `Callable = 0` in lib-stub/typing.pyi [builtins fixtures/list.pyi] @@ -93,7 +93,7 @@ T = TypeVar('T') A = Tuple[T, T] if int(): - A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation + A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "type[...]" annotation [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] @@ -168,11 +168,11 @@ f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "str" from typing import Tuple, Callable EmptyTuple = Tuple[()] x: EmptyTuple -reveal_type(x) # N: Revealed type is "Tuple[()]" +reveal_type(x) # N: Revealed type is "tuple[()]" EmptyTupleCallable = Callable[[Tuple[()]], None] f: EmptyTupleCallable -reveal_type(f) # N: Revealed type is "def (Tuple[()])" +reveal_type(f) # N: Revealed type is "def (tuple[()])" [builtins fixtures/list.pyi] [case testForwardTypeAlias] @@ -188,7 +188,7 @@ from typing import TypeVar, Tuple def f(p: 'Alias[str]') -> None: pass -reveal_type(f) # N: Revealed type is "def (p: Tuple[builtins.int, builtins.str])" +reveal_type(f) # N: Revealed type is "def (p: tuple[builtins.int, builtins.str])" T = TypeVar('T') Alias = Tuple[int, T] [builtins fixtures/tuple.pyi] @@ -375,25 +375,25 @@ class Cls: A1('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" a1 = A1(1) -reveal_type(a1) # N: Revealed type is "Tuple[builtins.int, fallback=nt.C]" +reveal_type(a1) # N: Revealed type is "tuple[builtins.int, fallback=nt.C]" A2(0) # E: Argument 1 to "D" has incompatible type "int"; expected "str" a2 = A2('yes') -reveal_type(a2) # N: Revealed type is "Tuple[builtins.str, fallback=nt.D]" +reveal_type(a2) # N: Revealed type is "tuple[builtins.str, fallback=nt.D]" a3 = A3() -reveal_type(a3) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=nt.E]" +reveal_type(a3) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=nt.E]" Cls.A1('no') # E: Argument 1 has incompatible type "str"; expected "int" ca1 = Cls.A1(1) -reveal_type(ca1) # N: Revealed type is "Tuple[builtins.int, fallback=nt.C]" +reveal_type(ca1) # N: Revealed type is "tuple[builtins.int, fallback=nt.C]" Cls.A2(0) # E: Argument 1 has incompatible type "int"; expected "str" ca2 = Cls.A2('yes') -reveal_type(ca2) # N: Revealed type is "Tuple[builtins.str, fallback=nt.D]" +reveal_type(ca2) # N: Revealed type is "tuple[builtins.str, fallback=nt.D]" ca3 = Cls.A3() -reveal_type(ca3) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=nt.E]" +reveal_type(ca3) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=nt.E]" [file nt.pyi] from typing import NamedTuple, Tuple @@ -926,30 +926,30 @@ class Child(Parent): pass p = Parent() c = Child() -NormalImplicit = 4 # E: Cannot assign multiple types to name "NormalImplicit" without an explicit "Type[...]" annotation \ - # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") -NormalExplicit = 4 # E: Cannot assign multiple types to name "NormalExplicit" without an explicit "Type[...]" annotation \ - # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") -SpecialImplicit = 4 # E: Cannot assign multiple types to name "SpecialImplicit" without an explicit "Type[...]" annotation -SpecialExplicit = 4 # E: Cannot assign multiple types to name "SpecialExplicit" without an explicit "Type[...]" annotation +NormalImplicit = 4 # E: Cannot assign multiple types to name "NormalImplicit" without an explicit "type[...]" annotation \ + # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") +NormalExplicit = 4 # E: Cannot assign multiple types to name "NormalExplicit" without an explicit "type[...]" annotation \ + # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") +SpecialImplicit = 4 # E: Cannot assign multiple types to name "SpecialImplicit" without an explicit "type[...]" annotation +SpecialExplicit = 4 # E: Cannot assign multiple types to name "SpecialExplicit" without an explicit "type[...]" annotation -Parent.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") -Parent.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") +Parent.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") +Parent.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") Parent.SpecialImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "") Parent.SpecialExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "") -Child.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") -Child.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") +Child.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") +Child.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") Child.SpecialImplicit = 4 Child.SpecialExplicit = 4 -p.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") -p.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") +p.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") +p.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") p.SpecialImplicit = 4 p.SpecialExplicit = 4 -c.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") -c.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]") +c.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") +c.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "type[Foo]") c.SpecialImplicit = 4 c.SpecialExplicit = 4 [builtins fixtures/tuple.pyi] @@ -1002,14 +1002,11 @@ B = List[C[U]] y: B[int] y_bad: B[str] # E: Type argument "str" of "B" must be a subtype of "int" -[case testTupleWithDifferentArgsPy38] -# flags: --python-version 3.8 -NotYet1 = tuple[float] # E: "tuple" is not subscriptable -NotYet2 = tuple[float, float] # E: "tuple" is not subscriptable -NotYet3 = tuple[float, ...] # E: Unexpected "..." \ - # E: "tuple" is not subscriptable -NotYet4 = tuple[float, float, ...] # E: Unexpected "..." \ - # E: "tuple" is not subscriptable +[case testTupleWithDifferentArgs] +Alias1 = tuple[float] +Alias2 = tuple[float, float] +Alias3 = tuple[float, ...] +Alias4 = tuple[float, float, ...] # E: Unexpected "..." [builtins fixtures/tuple.pyi] [case testTupleWithDifferentArgsStub] @@ -1050,8 +1047,8 @@ class C(Generic[T]): [builtins fixtures/classmethod.pyi] [case testRecursiveAliasTuple] -from typing_extensions import Literal, TypeAlias -from typing import Tuple, Union +from typing_extensions import TypeAlias +from typing import Literal, Tuple, Union Expr: TypeAlias = Union[ Tuple[Literal[123], int], @@ -1105,6 +1102,10 @@ t1: T1 # E: Variable "__main__.T1" is not valid as a type \ T3 = TypeAliasType("T3", -1) # E: Invalid type: try using Literal[-1] instead? t3: T3 reveal_type(t3) # N: Revealed type is "Any" + +T4 = TypeAliasType("T4") # E: Missing positional argument "value" in call to "TypeAliasType" +T5 = TypeAliasType("T5", int, str) # E: Too many positional arguments for "TypeAliasType" \ + # E: Argument 3 to "TypeAliasType" has incompatible type "type[str]"; expected "tuple[Union[TypeVar?, ParamSpec?, TypeVarTuple?], ...]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] @@ -1135,7 +1136,7 @@ VariadicAlias1 = TypeAliasType("VariadicAlias1", Tuple[Unpack[Ts]], type_params= VariadicAlias2 = TypeAliasType("VariadicAlias2", Tuple[Unpack[Ts], K], type_params=(Ts, K)) VariadicAlias3 = TypeAliasType("VariadicAlias3", Callable[[Unpack[Ts]], int], type_params=(Ts,)) xv: VariadicAlias1[int, str] = (1, 'a') -yv: VariadicAlias1[str, int] = (1, 'a') # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "Tuple[str, int]") +yv: VariadicAlias1[str, int] = (1, 'a') # E: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "tuple[str, int]") zv: VariadicAlias2[int, str] = (1, 'a') def int_in_int_out(x: int) -> int: return x wv: VariadicAlias3[int] = int_in_int_out @@ -1238,3 +1239,205 @@ from typing import List, Union A = Union[int, List[A]] def func(x: A) -> int: ... [builtins fixtures/tuple.pyi] + +[case testAliasNonGeneric] +from typing_extensions import TypeAlias +class Foo: ... + +ImplicitFoo = Foo +ExplicitFoo: TypeAlias = Foo + +x1: ImplicitFoo[str] # E: "Foo" expects no type arguments, but 1 given +x2: ExplicitFoo[str] # E: "Foo" expects no type arguments, but 1 given + +def is_foo(x: object): + if isinstance(x, ImplicitFoo): + pass + if isinstance(x, ExplicitFoo): + pass + +[builtins fixtures/tuple.pyi] + +[case testAliasExplicitNoArgsTuple] +from typing import Any, Tuple, assert_type +from typing_extensions import TypeAlias + +Implicit = Tuple +Explicit: TypeAlias = Tuple + +x1: Implicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +assert_type(x1, Tuple[Any, ...]) +assert_type(x2, Tuple[Any, ...]) +[builtins fixtures/tuple.pyi] + +[case testAliasExplicitNoArgsCallable] +from typing import Any, Callable, assert_type +from typing_extensions import TypeAlias + +Implicit = Callable +Explicit: TypeAlias = Callable + +x1: Implicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +x2: Explicit[str] # E: Bad number of arguments for type alias, expected 0, given 1 +assert_type(x1, Callable[..., Any]) +assert_type(x2, Callable[..., Any]) +[builtins fixtures/tuple.pyi] + +[case testExplicitTypeAliasToSameNameOuterProhibited] +from typing import TypeVar, Generic +from typing_extensions import TypeAlias + +T = TypeVar("T") +class Foo(Generic[T]): + bar: Bar[T] + +class Bar(Generic[T]): + Foo: TypeAlias = Foo[T] # E: Can't use bound type variable "T" to define generic alias +[builtins fixtures/tuple.pyi] + +[case testExplicitTypeAliasToSameNameOuterAllowed] +from typing import TypeVar, Generic +from typing_extensions import TypeAlias + +T = TypeVar("T") +class Foo(Generic[T]): + bar: Bar[T] + +U = TypeVar("U") +class Bar(Generic[T]): + Foo: TypeAlias = Foo[U] + var: Foo[T] +x: Bar[int] +reveal_type(x.var.bar) # N: Revealed type is "__main__.Bar[builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testExplicitTypeAliasClassVarProhibited] +from typing import ClassVar +from typing_extensions import TypeAlias + +Foo: TypeAlias = ClassVar[int] # E: ClassVar[...] can't be used inside a type alias +[builtins fixtures/tuple.pyi] + +[case testAnnotatedWithCallableAsParameterTypeAlias] +from typing_extensions import Annotated, TypeAlias + +def something() -> None: ... + +A: TypeAlias = list[Annotated[str, something()]] +a: A +reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testAnnotatedWithCallableAsParameterTypeAliasDeeper] +from typing_extensions import Annotated, TypeAlias + +def something() -> None: ... + +A: TypeAlias = list[Annotated[Annotated[str, something()], something()]] +a: A +reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeAliasDict] +D = dict[str, int] +d = D() +reveal_type(d) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type(D()) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type(D(x=1)) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +reveal_type(D(x="asdf")) # E: No overload variant of "dict" matches argument type "str" \ + # N: Possible overload variants: \ + # N: def __init__(self, **kwargs: int) -> dict[str, int] \ + # N: def __init__(self, arg: Iterable[tuple[str, int]], **kwargs: int) -> dict[str, int] \ + # N: Revealed type is "Any" +[builtins fixtures/dict.pyi] + +[case testTypeAliasesInCyclicImport1] +import p.aliases + +[file p/__init__.py] +[file p/aliases.py] +from typing_extensions import TypeAlias +from .defs import C, Alias1 + +Alias2: TypeAlias = Alias1[C] + +[file p/defs.py] +from typing import TypeVar +from typing_extensions import TypeAlias +import p.aliases + +C = TypeVar("C", bound="SomeClass") +Alias1: TypeAlias = C + +class SomeClass: + pass +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeAliasesInCyclicImport2] +import p.aliases + +[file p/__init__.py] +[file p/aliases.py] +from typing_extensions import TypeAlias +from .defs import C, Alias1 + +Alias2: TypeAlias = Alias1[C] + +[file p/defs.py] +from typing import TypeVar, Union +from typing_extensions import TypeAlias +import p.aliases + +C = TypeVar("C", bound="SomeClass") +Alias1: TypeAlias = Union[C, int] + +class SomeClass: + pass +[builtins fixtures/tuple.pyi] + +[case testTypeAliasesInCyclicImport3] +import p.aliases + +[file p/__init__.py] +[file p/aliases.py] +from typing_extensions import TypeAlias +from .defs import C, Alias1 + +Alias2: TypeAlias = Alias1[C] + +[file p/defs.py] +from typing import TypeVar +from typing_extensions import TypeAlias +import p.aliases + +C = TypeVar("C", bound="list[SomeClass]") +Alias1: TypeAlias = C + +class SomeClass: + pass +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeAliasesInCyclicImport4] +import p.aliases + +[file p/__init__.py] +[file p/aliases.py] +from typing_extensions import TypeAlias +from .defs import C, Alias1 + +Alias2: TypeAlias = Alias1[C] + +[file p/defs.py] +from typing import TypeVar, Union +from typing_extensions import TypeAlias +import p.aliases + +C = TypeVar("C", bound="list[SomeClass]") +Alias1: TypeAlias = Union[C, int] + +class SomeClass: + pass +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-type-object-type-inference.test b/test-data/unit/check-type-object-type-inference.test index 5a4afa0c92481..b410815664d1a 100644 --- a/test-data/unit/check-type-object-type-inference.test +++ b/test-data/unit/check-type-object-type-inference.test @@ -1,5 +1,4 @@ [case testInferTupleType] -# flags: --python-version 3.9 from typing import TypeVar, Generic, Type from abc import abstractmethod import types # Explicitly bring in stubs for 'types' @@ -18,25 +17,25 @@ class F: def g(f: F): f.f(int).e(7) f.f(tuple[int,str]) - f.f(tuple[int,str]).e('x') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[int, str]" - f.f(tuple[int,str]).e( (7,8) ) # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, int]"; expected "Tuple[int, str]" + f.f(tuple[int,str]).e('x') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "tuple[int, str]" + f.f(tuple[int,str]).e( (7,8) ) # E: Argument 1 to "e" of "E" has incompatible type "tuple[int, int]"; expected "tuple[int, str]" f.f(tuple[int,str]).e( (7,'x') ) # OK - reveal_type(f.f(tuple[int,str]).e) # N: Revealed type is "def (t: Tuple[builtins.int, builtins.str]) -> builtins.str" + reveal_type(f.f(tuple[int,str]).e) # N: Revealed type is "def (t: tuple[builtins.int, builtins.str]) -> builtins.str" def h(f: F): f.f(int).e(7) f.f(tuple) - f.f(tuple).e('y') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[Any, ...]" + f.f(tuple).e('y') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "tuple[Any, ...]" f.f(tuple).e( (8,'y') ) # OK reveal_type(f.f(tuple).e) # N: Revealed type is "def (t: builtins.tuple[Any, ...]) -> builtins.str" def i(f: F): f.f(tuple[int,tuple[int,str]]) - f.f(tuple[int,tuple[int,str]]).e('z') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "Tuple[int, Tuple[int, str]]" - f.f(tuple[int,tuple[int,str]]).e( (8,9) ) # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, int]"; expected "Tuple[int, Tuple[int, str]]" - f.f(tuple[int,tuple[int,str]]).e( (17, (28, 29)) ) # E: Argument 1 to "e" of "E" has incompatible type "Tuple[int, Tuple[int, int]]"; expected "Tuple[int, Tuple[int, str]]" + f.f(tuple[int,tuple[int,str]]).e('z') # E: Argument 1 to "e" of "E" has incompatible type "str"; expected "tuple[int, tuple[int, str]]" + f.f(tuple[int,tuple[int,str]]).e( (8,9) ) # E: Argument 1 to "e" of "E" has incompatible type "tuple[int, int]"; expected "tuple[int, tuple[int, str]]" + f.f(tuple[int,tuple[int,str]]).e( (17, (28, 29)) ) # E: Argument 1 to "e" of "E" has incompatible type "tuple[int, tuple[int, int]]"; expected "tuple[int, tuple[int, str]]" f.f(tuple[int,tuple[int,str]]).e( (27,(28,'z')) ) # OK - reveal_type(f.f(tuple[int,tuple[int,str]]).e) # N: Revealed type is "def (t: Tuple[builtins.int, Tuple[builtins.int, builtins.str]]) -> builtins.str" + reveal_type(f.f(tuple[int,tuple[int,str]]).e) # N: Revealed type is "def (t: tuple[builtins.int, tuple[builtins.int, builtins.str]]) -> builtins.str" x = tuple[int,str][str] # False negative [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-type-promotion.test b/test-data/unit/check-type-promotion.test index e66153726e7d1..1b69174a45454 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] @@ -187,3 +187,19 @@ if isinstance(x, (float, complex)): else: reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/primitives.pyi] + +[case testRejectPromotionsForProtocols] +from typing import Protocol + +class H(Protocol): + def hex(self, /) -> str: ... + +f: H = 1.0 +o: H = object() # E: Incompatible types in assignment (expression has type "object", variable has type "H") +c: H = 1j # E: Incompatible types in assignment (expression has type "complex", variable has type "H") +i: H = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "H") +b: H = False # E: Incompatible types in assignment (expression has type "bool", variable has type "H") + +class N(float): ... +n: H = N() +[builtins fixtures/primitives.pyi] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index e1797421636e8..e1a70efe9316d 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1,7 +1,7 @@ -- Create Instance [case testCanCreateTypedDictInstanceWithKeywordArguments] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=42, y=1337) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" @@ -12,7 +12,7 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" [targets __main__] [case testCanCreateTypedDictInstanceWithDictCall] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" @@ -22,7 +22,7 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictInstanceWithDictLiteral] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point({'x': 42, 'y': 1337}) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" @@ -32,8 +32,7 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictInstanceWithNoArguments] -from typing import TypeVar, Union -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union EmptyDict = TypedDict('EmptyDict', {}) p = EmptyDict() reveal_type(p) # N: Revealed type is "TypedDict('__main__.EmptyDict', {})" @@ -45,49 +44,55 @@ reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]" -- Create Instance (Errors) [case testCannotCreateTypedDictInstanceWithUnknownArgumentPattern] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(42, 1337) # E: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceNonLiteralItemName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) x = 'x' p = Point({x: 42, 'y': 1337}) # E: Expected TypedDict key to be string literal [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithExtraItems] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=42, y=1337, z=666) # E: Extra key "z" for TypedDict "Point" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithMissingItems] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=42) # E: Missing key "y" for TypedDict "Point" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithIncompatibleItemType] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x='meaning_of_life', y=1337) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictInstanceWithInlineTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', { 'x': TypedDict('E', { # E: Use dict literal for nested TypedDict 'y': int }) }) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Define TypedDict (Class syntax) [case testCanCreateTypedDictWithClass] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int @@ -96,9 +101,10 @@ class Point(TypedDict): p = Point(x=42, y=1337) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithSubclass] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1D(TypedDict): x: int @@ -109,9 +115,10 @@ p: Point2D reveal_type(r) # N: Revealed type is "TypedDict('__main__.Point1D', {'x': builtins.int})" reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithSubclass2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1D(TypedDict): x: int @@ -121,9 +128,10 @@ class Point2D(TypedDict, Point1D): # We also allow to include TypedDict in bases p: Point2D reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictClassEmpty] -from mypy_extensions import TypedDict +from typing import TypedDict class EmptyDict(TypedDict): pass @@ -131,12 +139,12 @@ class EmptyDict(TypedDict): p = EmptyDict() reveal_type(p) # N: Revealed type is "TypedDict('__main__.EmptyDict', {})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOldVersion] # Test that we can use class-syntax to merge function-based TypedDicts - -from mypy_extensions import TypedDict +from typing import TypedDict MovieBase1 = TypedDict( 'MovieBase1', {'name': str, 'year': int}) @@ -152,13 +160,13 @@ def foo(x): foo({}) # E: Missing keys ("name", "year") for TypedDict "Movie" foo({'name': 'lol', 'year': 2009, 'based_on': 0}) # E: Incompatible types (expression has type "int", TypedDict item "based_on" has type "str") - [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Define TypedDict (Class syntax errors) [case testCannotCreateTypedDictWithClassOtherBases] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass @@ -170,6 +178,7 @@ class Point2D(Point1D, A): # E: All bases of a new TypedDict must be TypedDict t p: Point2D reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point2D', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithDuplicateBases] # https://github.com/python/mypy/issues/3673 @@ -187,7 +196,7 @@ class C(TypedDict, TypedDict): # E: Duplicate base class "TypedDict" [typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithClassWithOtherStuff] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int @@ -198,6 +207,7 @@ class Point(TypedDict): p = Point(x=42, y=1337, z='whatever') reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int, 'z': Any})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithClassWithFunctionUsedToCrash] # https://github.com/python/mypy/issues/11079 @@ -237,12 +247,13 @@ class Foo(TypedDict): [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictTypeWithUnderscoreItemName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int, '_fallback': object}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassUnderscores] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int @@ -251,9 +262,10 @@ class Point(TypedDict): p: Point reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, '_y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithDuplicateKey1] -from mypy_extensions import TypedDict +from typing import TypedDict class Bad(TypedDict): x: int @@ -262,6 +274,7 @@ class Bad(TypedDict): b: Bad reveal_type(b) # N: Revealed type is "TypedDict('__main__.Bad', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictWithDuplicateKey2] from typing import TypedDict @@ -280,7 +293,7 @@ reveal_type(d2) # N: Revealed type is "TypedDict('__main__.D2', {'x': builtins.s [typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOverwriting] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1(TypedDict): x: int @@ -292,9 +305,10 @@ class Bad(Point1, Point2): # E: Overwriting TypedDict field "x" while merging b: Bad reveal_type(b) # N: Revealed type is "TypedDict('__main__.Bad', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithClassOverwriting2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point1(TypedDict): x: int @@ -304,104 +318,111 @@ class Point2(Point1): p2: Point2 reveal_type(p2) # N: Revealed type is "TypedDict('__main__.Point2', {'x': builtins.float})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Subtyping [case testCanConvertTypedDictToItself] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def identity(p: Point) -> Point: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanConvertTypedDictToEquivalentTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict PointA = TypedDict('PointA', {'x': int, 'y': int}) PointB = TypedDict('PointB', {'x': int, 'y': int}) def identity(p: PointA) -> PointB: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToSimilarTypedDictWithNarrowerItemTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) ObjectPoint = TypedDict('ObjectPoint', {'x': object, 'y': object}) def convert(op: ObjectPoint) -> Point: return op # E: Incompatible return value type (got "ObjectPoint", expected "Point") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToSimilarTypedDictWithWiderItemTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) ObjectPoint = TypedDict('ObjectPoint', {'x': object, 'y': object}) def convert(p: Point) -> ObjectPoint: return p # E: Incompatible return value type (got "Point", expected "ObjectPoint") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToSimilarTypedDictWithIncompatibleItemTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Chameleon = TypedDict('Chameleon', {'x': str, 'y': str}) def convert(p: Point) -> Chameleon: return p # E: Incompatible return value type (got "Point", expected "Chameleon") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanConvertTypedDictToNarrowerTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Point1D = TypedDict('Point1D', {'x': int}) def narrow(p: Point) -> Point1D: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToWiderTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Point3D = TypedDict('Point3D', {'x': int, 'y': int, 'z': int}) def widen(p: Point) -> Point3D: return p # E: Incompatible return value type (got "Point", expected "Point3D") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanConvertTypedDictToCompatibleMapping] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def as_mapping(p: Point) -> Mapping[str, object]: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToIncompatibleMapping] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def as_mapping(p: Point) -> Mapping[str, int]: return p # E: Incompatible return value type (got "Point", expected "Mapping[str, int]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAcceptsIntForFloatDuckTypes] -from mypy_extensions import TypedDict -from typing import Any, Mapping +from typing import Any, Mapping, TypedDict Point = TypedDict('Point', {'x': float, 'y': float}) def create_point() -> Point: return Point(x=1, y=2) reveal_type(Point(x=1, y=2)) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.float, 'y': builtins.float})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictDoesNotAcceptsFloatForInt] -from mypy_extensions import TypedDict -from typing import Any, Mapping +from typing import Any, Mapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def create_point() -> Point: return Point(x=1.2, y=2.5) [out] -main:5: error: Incompatible types (expression has type "float", TypedDict item "x" has type "int") -main:5: error: Incompatible types (expression has type "float", TypedDict item "y" has type "int") +main:4: error: Incompatible types (expression has type "float", TypedDict item "x" has type "int") +main:4: error: Incompatible types (expression has type "float", TypedDict item "y" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAcceptsAnyType] -from mypy_extensions import TypedDict -from typing import Any, Mapping +from typing import Any, Mapping, TypedDict Point = TypedDict('Point', {'x': float, 'y': float}) def create_point(something: Any) -> Point: return Point({ @@ -409,35 +430,35 @@ def create_point(something: Any) -> Point: 'y': something.y }) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictValueTypeContext] -from mypy_extensions import TypedDict -from typing import List +from typing import List, TypedDict D = TypedDict('D', {'x': List[int]}) reveal_type(D(x=[])) # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.list[builtins.int]})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotConvertTypedDictToDictOrMutableMapping] -from mypy_extensions import TypedDict -from typing import Dict, MutableMapping +from typing import Dict, MutableMapping, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def as_dict(p: Point) -> Dict[str, int]: - return p # E: Incompatible return value type (got "Point", expected "Dict[str, int]") + return p # E: Incompatible return value type (got "Point", expected "dict[str, int]") def as_mutable_mapping(p: Point) -> MutableMapping[str, object]: return p # E: Incompatible return value type (got "Point", expected "MutableMapping[str, object]") [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] [case testCanConvertTypedDictToAny] -from mypy_extensions import TypedDict -from typing import Any +from typing import Any, TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def unprotect(p: Point) -> Any: return p [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testAnonymousTypedDictInErrorMessages] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'x': int, 'z': str, 'a': int}) @@ -449,13 +470,14 @@ c: C def f(a: A) -> None: pass l = [a, b] # Join generates an anonymous TypedDict -f(l) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int})]"; expected "A" +f(l) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x': int})]"; expected "A" ll = [b, c] -f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x': int, 'z': str})]"; expected "A" +f(ll) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x': int, 'z': str})]"; expected "A" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithSimpleProtocol] -from typing_extensions import Protocol, TypedDict +from typing import Protocol, TypedDict class StrObjectMap(Protocol): def __getitem__(self, key: str) -> object: ... @@ -483,8 +505,7 @@ main:17: note: Got: main:17: note: def __getitem__(self, str, /) -> object [case testTypedDictWithSimpleProtocolInference] -from typing_extensions import Protocol, TypedDict -from typing import TypeVar +from typing import Protocol, TypedDict, TypeVar T_co = TypeVar('T_co', covariant=True) T = TypeVar('T') @@ -507,7 +528,7 @@ reveal_type(fun(b)) # N: Revealed type is "builtins.object" -- Join [case testJoinOfTypedDictHasOnlyCommonKeysAndNewFallback] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) Point3D = TypedDict('Point3D', {'x': int, 'y': int, 'z': int}) p1 = TaggedPoint(type='2d', x=0, y=0) @@ -520,7 +541,7 @@ reveal_type(joined_points) # N: Revealed type is "TypedDict({'x': builtins.int, [typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictRemovesNonequivalentKeys] -from mypy_extensions import TypedDict +from typing import TypedDict CellWithInt = TypedDict('CellWithInt', {'value': object, 'meta': int}) CellWithObject = TypedDict('CellWithObject', {'value': object, 'meta': object}) c1 = CellWithInt(value=1, meta=42) @@ -530,9 +551,10 @@ reveal_type(c1) # N: Revealed type is "TypedDict('__main__.CellWithI reveal_type(c2) # N: Revealed type is "TypedDict('__main__.CellWithObject', {'value': builtins.object, 'meta': builtins.object})" reveal_type(joined_cells) # N: Revealed type is "builtins.list[TypedDict({'value': builtins.object})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testJoinOfDisjointTypedDictsIsEmptyTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) Cell = TypedDict('Cell', {'value': object}) d1 = Point(x=0, y=0) @@ -542,10 +564,10 @@ reveal_type(d1) # N: Revealed type is "TypedDict('__main__.Point', { reveal_type(d2) # N: Revealed type is "TypedDict('__main__.Cell', {'value': builtins.object})" reveal_type(joined_dicts) # N: Revealed type is "builtins.list[TypedDict({})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictWithCompatibleMappingIsMapping] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Cell = TypedDict('Cell', {'value': int}) left = Cell(value=42) right = {'score': 999} # type: Mapping[str, int] @@ -554,10 +576,10 @@ joined2 = [right, left] reveal_type(joined1) # N: Revealed type is "builtins.list[typing.Mapping[builtins.str, builtins.object]]" reveal_type(joined2) # N: Revealed type is "builtins.list[typing.Mapping[builtins.str, builtins.object]]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictWithCompatibleMappingSupertypeIsSupertype] -from mypy_extensions import TypedDict -from typing import Sized +from typing import Sized, TypedDict Cell = TypedDict('Cell', {'value': int}) left = Cell(value=42) right = {'score': 999} # type: Sized @@ -569,8 +591,7 @@ reveal_type(joined2) # N: Revealed type is "builtins.list[typing.Sized]" [typing fixtures/typing-typeddict.pyi] [case testJoinOfTypedDictWithIncompatibleTypeIsObject] -from mypy_extensions import TypedDict -from typing import Mapping +from typing import Mapping, TypedDict Cell = TypedDict('Cell', {'value': int}) left = Cell(value=42) right = 42 @@ -579,13 +600,13 @@ joined2 = [right, left] reveal_type(joined1) # N: Revealed type is "builtins.list[builtins.object]" reveal_type(joined2) # N: Revealed type is "builtins.list[builtins.object]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Meet [case testMeetOfTypedDictsWithCompatibleCommonKeysHasAllKeysAndNewFallback] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}) YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') @@ -593,10 +614,10 @@ 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 testMeetOfTypedDictsWithIncompatibleCommonKeysIsUninhabited] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XYa = TypedDict('XYa', {'x': int, 'y': int}) YbZ = TypedDict('YbZ', {'y': object, 'z': int}) T = TypeVar('T') @@ -604,10 +625,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: XYa, y: YbZ) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithNoCommonKeysHasAllKeysAndNewFallback] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable X = TypedDict('X', {'x': int}) Z = TypedDict('Z', {'z': int}) T = TypeVar('T') @@ -615,11 +636,11 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: Z) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict({'x': builtins.int, 'z': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] # TODO: It would be more accurate for the meet to be TypedDict instead. [case testMeetOfTypedDictWithCompatibleMappingIsUninhabitedForNow] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable, Mapping +from typing import TypedDict, TypeVar, Callable, Mapping X = TypedDict('X', {'x': int}) M = Mapping[str, int] T = TypeVar('T') @@ -627,10 +648,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: M) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictWithIncompatibleMappingIsUninhabited] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable, Mapping +from typing import TypedDict, TypeVar, Callable, Mapping X = TypedDict('X', {'x': int}) M = Mapping[str, str] T = TypeVar('T') @@ -638,10 +659,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: M) -> None: pass reveal_type(f(g)) # N: Revealed type is "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictWithCompatibleMappingSuperclassIsUninhabitedForNow] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable, Iterable +from typing import TypedDict, TypeVar, Callable, Iterable X = TypedDict('X', {'x': int}) I = Iterable[str] T = TypeVar('T') @@ -649,10 +670,10 @@ def f(x: Callable[[T, T], None]) -> T: pass def g(x: X, y: I) -> None: pass reveal_type(f(g)) # N: Revealed type is "TypedDict('__main__.X', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testMeetOfTypedDictsWithNonTotal] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}, total=False) YZ = TypedDict('YZ', {'y': int, 'z': int}, total=False) T = TypeVar('T') @@ -660,10 +681,10 @@ 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 testMeetOfTypedDictsWithNonTotalAndTotal] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int}, total=False) YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') @@ -671,10 +692,10 @@ 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 testMeetOfTypedDictsWithIncompatibleNonTotalAndTotal] -from mypy_extensions import TypedDict -from typing import TypeVar, Callable +from typing import TypedDict, TypeVar, Callable XY = TypedDict('XY', {'x': int, 'y': int}, total=False) YZ = TypedDict('YZ', {'y': int, 'z': int}) T = TypeVar('T') @@ -682,13 +703,13 @@ 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 "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Constraint Solver [case testTypedDictConstraintsAgainstIterable] -from typing import TypeVar, Iterable -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Iterable T = TypeVar('T') def f(x: Iterable[T]) -> T: pass A = TypedDict('A', {'x': int}) @@ -703,25 +724,26 @@ reveal_type(f(a)) # N: Revealed type is "builtins.str" -- Special Method: __getitem__ [case testCanGetItemOfTypedDictWithValidStringLiteralKey] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) reveal_type(p['type']) # N: Revealed type is "builtins.str" reveal_type(p['x']) # N: Revealed type is "builtins.int" reveal_type(p['y']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotGetItemOfTypedDictWithInvalidStringLiteralKey] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p: TaggedPoint p['typ'] # E: TypedDict "TaggedPoint" has no key "typ" \ # N: Did you mean "type"? [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotGetItemOfAnonymousTypedDictWithInvalidStringLiteralKey] -from typing import TypeVar -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': str, 'y': int, 'z': str}) B = TypedDict('B', {'x': str, 'z': int}) C = TypedDict('C', {'x': str, 'y': int, 'z': int}) @@ -732,89 +754,95 @@ ac = join(A(x='', y=1, z=''), C(x='', y=0, z=1)) ab['y'] # E: "y" is not a valid TypedDict key; expected one of ("x") ac['a'] # E: "a" is not a valid TypedDict key; expected one of ("x", "y") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotGetItemOfTypedDictWithNonLiteralKey] -from mypy_extensions import TypedDict -from typing import Union +from typing import TypedDict, Union TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) def get_coordinate(p: TaggedPoint, key: str) -> Union[str, int]: return p[key] # E: TypedDict key must be a string literal; expected one of ("type", "x", "y") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Special Method: __setitem__ [case testCanSetItemOfTypedDictWithValidStringLiteralKeyAndCompatibleValueType] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p['type'] = 'two_d' p['x'] = 1 [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotSetItemOfTypedDictWithIncompatibleValueType] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p['x'] = 'y' # E: Value of "x" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotSetItemOfTypedDictWithInvalidStringLiteralKey] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p['z'] = 1 # E: TypedDict "TaggedPoint" has no key "z" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotSetItemOfTypedDictWithNonLiteralKey] -from mypy_extensions import TypedDict -from typing import Union +from typing import TypedDict, Union TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) def set_coordinate(p: TaggedPoint, key: str, value: int) -> None: p[key] = value # E: TypedDict key must be a string literal; expected one of ("type", "x", "y") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- isinstance [case testTypedDictWithIsInstanceAndIsSubclass] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int}) d: object if isinstance(d, D): # E: Cannot use isinstance() with TypedDict type - reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.int})" + reveal_type(d) # N: Revealed type is "__main__.D" issubclass(object, D) # E: Cannot use issubclass() with TypedDict type [builtins fixtures/isinstancelist.pyi] +[typing fixtures/typing-typeddict.pyi] -- Scoping [case testTypedDictInClassNamespace] # https://github.com/python/mypy/pull/2553#issuecomment-266474341 -from mypy_extensions import TypedDict +from typing import TypedDict class C: def f(self): A = TypedDict('A', {'x': int}) def g(self): A = TypedDict('A', {'y': int}) -C.A # E: "Type[C]" has no attribute "A" +C.A # E: "type[C]" has no attribute "A" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictInFunction] -from mypy_extensions import TypedDict +from typing import TypedDict def f() -> None: A = TypedDict('A', {'x': int}) A # E: Name "A" is not defined [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Union simplification / proper subtype checks [case testTypedDictUnionSimplification] -from typing import TypeVar, Union, Any, cast -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Any, cast T = TypeVar('T') S = TypeVar('S') @@ -842,10 +870,10 @@ reveal_type(u(f, c)) # N: Revealed type is "Union[TypedDict('__main__.C', {'a': reveal_type(u(c, g)) # N: Revealed type is "Union[TypedDict('__main__.G', {'a': Any}), TypedDict('__main__.C', {'a': builtins.int})]" reveal_type(u(g, c)) # N: Revealed type is "Union[TypedDict('__main__.C', {'a': builtins.int}), TypedDict('__main__.G', {'a': Any})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionSimplification2] -from typing import TypeVar, Union, Mapping, Any -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Mapping, Any T = TypeVar('T') S = TypeVar('S') @@ -865,10 +893,10 @@ reveal_type(u(c, m_s_s)) # N: Revealed type is "Union[typing.Mapping[builtins.st reveal_type(u(c, m_i_i)) # N: Revealed type is "Union[typing.Mapping[builtins.int, builtins.int], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]" reveal_type(u(c, m_s_a)) # N: Revealed type is "Union[typing.Mapping[builtins.str, Any], TypedDict('__main__.C', {'a': builtins.int, 'b': builtins.int})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionUnambiguousCase] -from typing import Union, Mapping, Any, cast -from typing_extensions import TypedDict, Literal +from typing import Union, Literal, Mapping, TypedDict, Any, cast A = TypedDict('A', {'@type': Literal['a-type'], 'a': str}) B = TypedDict('B', {'@type': Literal['b-type'], 'b': int}) @@ -876,32 +904,33 @@ B = TypedDict('B', {'@type': Literal['b-type'], 'b': int}) c: Union[A, B] = {'@type': 'a-type', 'a': 'Test'} reveal_type(c) # N: Revealed type is "Union[TypedDict('__main__.A', {'@type': Literal['a-type'], 'a': builtins.str}), TypedDict('__main__.B', {'@type': Literal['b-type'], 'b': builtins.int})]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionAmbiguousCaseBothMatch] -from typing import Union, Mapping, Any, cast -from typing_extensions import TypedDict, Literal +from typing import Union, Literal, Mapping, TypedDict, Any, cast A = TypedDict('A', {'@type': Literal['a-type'], 'value': str}) B = TypedDict('B', {'@type': Literal['b-type'], 'value': str}) c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnionAmbiguousCaseNoMatch] -from typing import Union, Mapping, Any, cast -from typing_extensions import TypedDict, Literal +from typing import Union, Literal, Mapping, TypedDict, Any, cast A = TypedDict('A', {'@type': Literal['a-type'], 'value': int}) B = TypedDict('B', {'@type': Literal['b-type'], 'value': int}) c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \ - # E: Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "Union[A, B]") + # E: Incompatible types in assignment (expression has type "dict[str, str]", variable has type "Union[A, B]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Use dict literals [case testTypedDictDictLiterals] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) @@ -919,9 +948,10 @@ f(dict(x=1, y=3, z=4)) # E: Extra key "z" for TypedDict "Point" f(dict(x=1, y=3, z=4, a=5)) # E: Extra keys ("z", "a") for TypedDict "Point" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictExplicitTypes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) @@ -938,10 +968,10 @@ if int(): p4: Point = {'x': 1, 'y': 2} [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateAnonymousTypedDictInstanceUsingDictLiteralWithExtraItems] -from mypy_extensions import TypedDict -from typing import TypeVar +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': int, 'y': int}) B = TypedDict('B', {'x': int, 'y': str}) T = TypeVar('T') @@ -950,10 +980,10 @@ ab = join(A(x=1, y=1), B(x=1, y='')) if int(): ab = {'x': 1, 'z': 1} # E: Expected TypedDict key "x" but found keys ("x", "z") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateAnonymousTypedDictInstanceUsingDictLiteralWithMissingItems] -from mypy_extensions import TypedDict -from typing import TypeVar +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': int, 'y': int, 'z': int}) B = TypedDict('B', {'x': int, 'y': int, 'z': str}) T = TypeVar('T') @@ -962,48 +992,173 @@ ab = join(A(x=1, y=1, z=1), B(x=1, y=1, z='')) if int(): ab = {} # E: Expected TypedDict keys ("x", "y") but found no keys [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Other TypedDict methods -[case testTypedDictGetMethod] -from mypy_extensions import TypedDict -class A: pass -D = TypedDict('D', {'x': int, 'y': str}) + +[case testTypedDictGetMethodOverloads] +from typing import TypedDict +from typing_extensions import Required, NotRequired + +class D(TypedDict): + a: int + b: NotRequired[str] + +def test(d: D) -> None: + reveal_type(d.get) # N: Revealed type is "Overload(def (k: builtins.str) -> builtins.object, def (builtins.str, builtins.object) -> builtins.object, def [V] (builtins.str, V`4) -> builtins.object)" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictGetMethodTotalFalse] +from typing import TypedDict, Literal +class Unrelated: pass +D = TypedDict('D', {'x': int, 'y': str}, total=False) d: D +u: Unrelated +x: Literal['x'] +y: Literal['y'] +z: Literal['z'] +x_or_y: Literal['x', 'y'] +x_or_z: Literal['x', 'z'] +x_or_y_or_z: Literal['x', 'y', 'z'] + +# test with literal expression reveal_type(d.get('x')) # N: Revealed type is "Union[builtins.int, None]" reveal_type(d.get('y')) # N: Revealed type is "Union[builtins.str, None]" -reveal_type(d.get('x', A())) # N: Revealed type is "Union[builtins.int, __main__.A]" +reveal_type(d.get('z')) # N: Revealed type is "builtins.object" +reveal_type(d.get('x', u)) # N: Revealed type is "Union[builtins.int, __main__.Unrelated]" +reveal_type(d.get('x', 1)) # N: Revealed type is "builtins.int" +reveal_type(d.get('y', None)) # N: Revealed type is "Union[builtins.str, None]" + +# test with literal type / union of literal types with implicit default +reveal_type(d.get(x)) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(d.get(y)) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(d.get(z)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y)) # N: Revealed type is "Union[builtins.int, builtins.str, None]" +reveal_type(d.get(x_or_z)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y_or_z)) # N: Revealed type is "builtins.object" + +# test with literal type / union of literal types with explicit default +reveal_type(d.get(x, u)) # N: Revealed type is "Union[builtins.int, __main__.Unrelated]" +reveal_type(d.get(y, u)) # N: Revealed type is "Union[builtins.str, __main__.Unrelated]" +reveal_type(d.get(z, u)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y, u)) # N: Revealed type is "Union[builtins.int, builtins.str, __main__.Unrelated]" +reveal_type(d.get(x_or_z, u)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y_or_z, u)) # N: Revealed type is "builtins.object" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictGetMethodTotalTrue] +from typing import TypedDict, Literal +class Unrelated: pass +D = TypedDict('D', {'x': int, 'y': str}, total=True) +d: D +u: Unrelated +x: Literal['x'] +y: Literal['y'] +z: Literal['z'] +x_or_y: Literal['x', 'y'] +x_or_z: Literal['x', 'z'] +x_or_y_or_z: Literal['x', 'y', 'z'] + +# test with literal expression +reveal_type(d.get('x')) # N: Revealed type is "builtins.int" +reveal_type(d.get('y')) # N: Revealed type is "builtins.str" +reveal_type(d.get('z')) # N: Revealed type is "builtins.object" +reveal_type(d.get('x', u)) # N: Revealed type is "builtins.int" +reveal_type(d.get('x', 1)) # N: Revealed type is "builtins.int" +reveal_type(d.get('y', None)) # N: Revealed type is "builtins.str" + +# test with literal type / union of literal types with implicit default +reveal_type(d.get(x)) # N: Revealed type is "builtins.int" +reveal_type(d.get(y)) # N: Revealed type is "builtins.str" +reveal_type(d.get(z)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y)) # N: Revealed type is "Union[builtins.int, builtins.str]" +reveal_type(d.get(x_or_z)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y_or_z)) # N: Revealed type is "builtins.object" + +# test with literal type / union of literal types with explicit default +reveal_type(d.get(x, u)) # N: Revealed type is "builtins.int" +reveal_type(d.get(y, u)) # N: Revealed type is "builtins.str" +reveal_type(d.get(z, u)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y, u)) # N: Revealed type is "Union[builtins.int, builtins.str]" +reveal_type(d.get(x_or_z, u)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y_or_z, u)) # N: Revealed type is "builtins.object" + +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictGetMethodTotalMixed] +from typing import TypedDict, Literal +from typing_extensions import Required, NotRequired +class Unrelated: pass +D = TypedDict('D', {'x': Required[int], 'y': NotRequired[str]}) +d: D +u: Unrelated +x: Literal['x'] +y: Literal['y'] +z: Literal['z'] +x_or_y: Literal['x', 'y'] +x_or_z: Literal['x', 'z'] +x_or_y_or_z: Literal['x', 'y', 'z'] + +# test with literal expression +reveal_type(d.get('x')) # N: Revealed type is "builtins.int" +reveal_type(d.get('y')) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(d.get('z')) # N: Revealed type is "builtins.object" +reveal_type(d.get('x', u)) # N: Revealed type is "builtins.int" reveal_type(d.get('x', 1)) # N: Revealed type is "builtins.int" reveal_type(d.get('y', None)) # N: Revealed type is "Union[builtins.str, None]" + +# test with literal type / union of literal types with implicit default +reveal_type(d.get(x)) # N: Revealed type is "builtins.int" +reveal_type(d.get(y)) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(d.get(z)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y)) # N: Revealed type is "Union[builtins.int, builtins.str, None]" +reveal_type(d.get(x_or_z)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y_or_z)) # N: Revealed type is "builtins.object" + +# test with literal type / union of literal types with explicit default +reveal_type(d.get(x, u)) # N: Revealed type is "builtins.int" +reveal_type(d.get(y, u)) # N: Revealed type is "Union[builtins.str, __main__.Unrelated]" +reveal_type(d.get(z, u)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y, u)) # N: Revealed type is "Union[builtins.int, builtins.str, __main__.Unrelated]" +reveal_type(d.get(x_or_z, u)) # N: Revealed type is "builtins.object" +reveal_type(d.get(x_or_y_or_z, u)) # N: Revealed type is "builtins.object" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] + [case testTypedDictGetMethodTypeContext] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict class A: pass -D = TypedDict('D', {'x': List[int], 'y': int}) +D = TypedDict('D', {'x': List[int], 'y': int}, total=False) d: D reveal_type(d.get('x', [])) # N: Revealed type is "builtins.list[builtins.int]" -d.get('x', ['x']) # E: List item 0 has incompatible type "str"; expected "int" +reveal_type(d.get('x', ['x'])) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]" a = [''] reveal_type(d.get('x', a)) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] [case testTypedDictGetMethodInvalidArgs] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}) d: D d.get() # E: All overload variants of "get" of "Mapping" require at least one argument \ # N: Possible overload variants: \ # N: def get(self, k: str) -> object \ - # N: def [V] get(self, k: str, default: object) -> object + # N: def get(self, str, object, /) -> object \ + # N: def [V] get(self, str, V, /) -> object d.get('x', 1, 2) # E: No overload variant of "get" of "Mapping" matches argument types "str", "int", "int" \ # N: Possible overload variants: \ # N: def get(self, k: str) -> object \ - # N: def [V] get(self, k: str, default: Union[int, V]) -> object + # N: def get(self, str, object, /) -> object \ + # N: def [V] get(self, str, Union[int, V], /) -> object x = d.get('z') reveal_type(x) # N: Revealed type is "builtins.object" s = '' @@ -1013,14 +1168,15 @@ reveal_type(y) # N: Revealed type is "builtins.object" [typing fixtures/typing-typeddict.pyi] [case testTypedDictMissingMethod] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}) d: D d.bad(1) # E: "D" has no attribute "bad" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictChainedGetMethodWithDictFallback] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}) E = TypedDict('E', {'d': D}) p = E(d=D(x=0, y='')) @@ -1029,7 +1185,7 @@ reveal_type(p.get('d', {'x': 1, 'y': ''})) # N: Revealed type is "TypedDict('__m [typing fixtures/typing-typeddict.pyi] [case testTypedDictGetDefaultParameterStillTypeChecked] -from mypy_extensions import TypedDict +from typing import TypedDict TaggedPoint = TypedDict('TaggedPoint', {'type': str, 'x': int, 'y': int}) p = TaggedPoint(type='2d', x=42, y=1337) p.get('x', 1 + 'y') # E: Unsupported operand types for + ("int" and "str") @@ -1037,40 +1193,157 @@ p.get('x', 1 + 'y') # E: Unsupported operand types for + ("int" and "str") [typing fixtures/typing-typeddict.pyi] [case testTypedDictChainedGetWithEmptyDictDefault] -from mypy_extensions import TypedDict -C = TypedDict('C', {'a': int}) -D = TypedDict('D', {'x': C, 'y': str}) +from typing import TypedDict +C = TypedDict('C', {'a': int}, total=True) +D = TypedDict('D', {'x': C, 'y': str}, total=False) +d: D +reveal_type(d.get('x', {})) # N: Revealed type is "TypedDict('__main__.C', {'a'?: builtins.int})" +reveal_type(d.get('x', None)) # N: Revealed type is "Union[TypedDict('__main__.C', {'a': builtins.int}), None]" +reveal_type(d.get('x', {}).get('a')) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(d.get('x', {})['a']) # N: Revealed type is "builtins.int" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictChainedGetWithEmptyDictDefault2] +from typing import TypedDict +C = TypedDict('C', {'a': int}, total=False) +D = TypedDict('D', {'x': C, 'y': str}, total=True) +d: D +reveal_type(d.get('x', {})) # N: Revealed type is "TypedDict('__main__.C', {'a'?: builtins.int})" +reveal_type(d.get('x', None)) # N: Revealed type is "TypedDict('__main__.C', {'a'?: builtins.int})" +reveal_type(d.get('x', {}).get('a')) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(d.get('x', {})['a']) # N: Revealed type is "builtins.int" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictChainedGetWithEmptyDictDefault3] +from typing import TypedDict +C = TypedDict('C', {'a': int}, total=True) +D = TypedDict('D', {'x': C, 'y': str}, total=True) +d: D +reveal_type(d.get('x', {})) # N: Revealed type is "TypedDict('__main__.C', {'a': builtins.int})" +reveal_type(d.get('x', None)) # N: Revealed type is "TypedDict('__main__.C', {'a': builtins.int})" +reveal_type(d.get('x', {}).get('a')) # N: Revealed type is "builtins.int" +reveal_type(d.get('x', {})['a']) # N: Revealed type is "builtins.int" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictChainedGetWithEmptyDictDefault4] +from typing import TypedDict +C = TypedDict('C', {'a': int}, total=False) +D = TypedDict('D', {'x': C, 'y': str}, total=False) d: D -reveal_type(d.get('x', {})) \ - # N: Revealed type is "TypedDict('__main__.C', {'a'?: builtins.int})" -reveal_type(d.get('x', None)) \ - # N: Revealed type is "Union[TypedDict('__main__.C', {'a': builtins.int}), None]" +reveal_type(d.get('x', {})) # N: Revealed type is "TypedDict('__main__.C', {'a'?: builtins.int})" +reveal_type(d.get('x', None)) # N: Revealed type is "Union[TypedDict('__main__.C', {'a'?: builtins.int}), None]" reveal_type(d.get('x', {}).get('a')) # N: Revealed type is "Union[builtins.int, None]" reveal_type(d.get('x', {})['a']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictGetMethodChained] +# check that chaining with get like ``.get(key, {}).get(subkey, {})`` works. +from typing import TypedDict, Mapping +from typing_extensions import Required, NotRequired, Never + +class Total(TypedDict, total=True): # no keys optional + key_one: int + key_two: str + +class Maybe(TypedDict, total=False): # all keys are optional + key_one: int + key_two: str + +class Mixed(TypedDict): # some keys optional + key_one: Required[int] + key_two: NotRequired[str] + +class Config(TypedDict): + required_total: Required[Total] + optional_total: NotRequired[Total] + required_mixed: Required[Mixed] + optional_mixed: NotRequired[Mixed] + required_maybe: Required[Maybe] + optional_maybe: NotRequired[Maybe] + +def test_chaining(d: Config) -> None: + reveal_type( d.get("required_total", {}) ) # N: Revealed type is "TypedDict('__main__.Total', {'key_one': builtins.int, 'key_two': builtins.str})" + reveal_type( d.get("optional_total", {}) ) # N: Revealed type is "TypedDict('__main__.Total', {'key_one'?: builtins.int, 'key_two'?: builtins.str})" + reveal_type( d.get("required_maybe", {}) ) # N: Revealed type is "TypedDict('__main__.Maybe', {'key_one'?: builtins.int, 'key_two'?: builtins.str})" + reveal_type( d.get("optional_maybe", {}) ) # N: Revealed type is "TypedDict('__main__.Maybe', {'key_one'?: builtins.int, 'key_two'?: builtins.str})" + reveal_type( d.get("required_mixed", {}) ) # N: Revealed type is "TypedDict('__main__.Mixed', {'key_one': builtins.int, 'key_two'?: builtins.str})" + reveal_type( d.get("optional_mixed", {}) ) # N: Revealed type is "TypedDict('__main__.Mixed', {'key_one'?: builtins.int, 'key_two'?: builtins.str})" + + reveal_type( d.get("required_total", {}).get("key_one") ) # N: Revealed type is "builtins.int" + reveal_type( d.get("required_total", {}).get("key_two") ) # N: Revealed type is "builtins.str" + reveal_type( d.get("required_total", {}).get("bad_key") ) # N: Revealed type is "builtins.object" + reveal_type( d.get("optional_total", {}).get("key_one") ) # N: Revealed type is "Union[builtins.int, None]" + reveal_type( d.get("optional_total", {}).get("key_two") ) # N: Revealed type is "Union[builtins.str, None]" + reveal_type( d.get("optional_total", {}).get("bad_key") ) # N: Revealed type is "builtins.object" + + reveal_type( d.get("required_maybe", {}).get("key_one") ) # N: Revealed type is "Union[builtins.int, None]" + reveal_type( d.get("required_maybe", {}).get("key_two") ) # N: Revealed type is "Union[builtins.str, None]" + reveal_type( d.get("required_maybe", {}).get("bad_key") ) # N: Revealed type is "builtins.object" + reveal_type( d.get("optional_maybe", {}).get("key_one") ) # N: Revealed type is "Union[builtins.int, None]" + reveal_type( d.get("optional_maybe", {}).get("key_two") ) # N: Revealed type is "Union[builtins.str, None]" + reveal_type( d.get("optional_maybe", {}).get("bad_key") ) # N: Revealed type is "builtins.object" + + reveal_type( d.get("required_mixed", {}).get("key_one") ) # N: Revealed type is "builtins.int" + reveal_type( d.get("required_mixed", {}).get("key_two") ) # N: Revealed type is "Union[builtins.str, None]" + reveal_type( d.get("required_mixed", {}).get("bad_key") ) # N: Revealed type is "builtins.object" + reveal_type( d.get("optional_mixed", {}).get("key_one") ) # N: Revealed type is "Union[builtins.int, None]" + reveal_type( d.get("optional_mixed", {}).get("key_two") ) # N: Revealed type is "Union[builtins.str, None]" + reveal_type( d.get("optional_mixed", {}).get("bad_key") ) # N: Revealed type is "builtins.object" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + + +[case testTypedDictGetWithNestedUnionOfTypedDicts] +# https://github.com/python/mypy/issues/19902 +from typing import TypedDict, Union +from typing_extensions import TypeAlias, NotRequired +class A(TypedDict): + key: NotRequired[int] + +class B(TypedDict): + key: NotRequired[int] + +class C(TypedDict): + key: NotRequired[int] + +A_or_B: TypeAlias = Union[A, B] +A_or_B_or_C: TypeAlias = Union[A_or_B, C] + +def test(d: A_or_B_or_C) -> None: + reveal_type(d.get("key")) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + -- Totality (the "total" keyword argument) [case testTypedDictWithTotalTrue] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=True) d: D reveal_type(d) \ # N: Revealed type is "TypedDict('__main__.D', {'x': builtins.int, 'y': builtins.str})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithInvalidTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int}, total=0) # E: "total" argument must be a True or False literal B = TypedDict('B', {'x': int}, total=bool) # E: "total" argument must be a True or False literal C = TypedDict('C', {'x': int}, x=False) # E: Unexpected keyword argument "x" for "TypedDict" D = TypedDict('D', {'x': int}, False) # E: Unexpected arguments to TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithTotalFalse] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) def f(d: D) -> None: reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'x'?: builtins.int, 'y'?: builtins.str})" @@ -1081,9 +1354,10 @@ f({'x': 1, 'y': ''}) f({'x': 1, 'z': ''}) # E: Extra key "z" for TypedDict "D" f({'x': ''}) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictConstructorWithTotalFalse] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) def f(d: D) -> None: pass reveal_type(D()) # N: Revealed type is "TypedDict('__main__.D', {'x'?: builtins.int, 'y'?: builtins.str})" @@ -1093,9 +1367,10 @@ f(D(x=1, y='')) f(D(x=1, z='')) # E: Extra key "z" for TypedDict "D" f(D(x='')) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictIndexingWithNonRequiredKey] -from mypy_extensions import TypedDict +from typing import TypedDict D = TypedDict('D', {'x': int, 'y': str}, total=False) d: D reveal_type(d['x']) # N: Revealed type is "builtins.int" @@ -1106,7 +1381,7 @@ reveal_type(d.get('y')) # N: Revealed type is "Union[builtins.str, None]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictSubtypingWithTotalFalse] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int}) B = TypedDict('B', {'x': int}, total=False) C = TypedDict('C', {'x': int, 'y': str}, total=False) @@ -1123,10 +1398,10 @@ fb(a) # E: Argument 1 to "fb" has incompatible type "A"; expected "B" fa(b) # E: Argument 1 to "fa" has incompatible type "B"; expected "A" fc(b) # E: Argument 1 to "fc" has incompatible type "B"; expected "C" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictJoinWithTotalFalse] -from typing import TypeVar -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar A = TypedDict('A', {'x': int}) B = TypedDict('B', {'x': int}, total=False) C = TypedDict('C', {'x': int, 'y': str}, total=False) @@ -1146,18 +1421,20 @@ reveal_type(j(b, c)) \ reveal_type(j(c, b)) \ # N: Revealed type is "TypedDict({'x'?: builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictClassWithTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict class D(TypedDict, total=False): x: int y: str d: D reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'x'?: builtins.int, 'y'?: builtins.str})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictClassWithInvalidTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict class D(TypedDict, total=1): # E: "total" argument must be a True or False literal x: int class E(TypedDict, total=bool): # E: "total" argument must be a True or False literal @@ -1166,9 +1443,10 @@ class F(TypedDict, total=xyz): # E: Name "xyz" is not defined \ # E: "total" argument must be a True or False literal x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictClassInheritanceWithTotalArgument] -from mypy_extensions import TypedDict +from typing import TypedDict class A(TypedDict): x: int class B(TypedDict, A, total=False): @@ -1178,9 +1456,10 @@ class C(TypedDict, B, total=True): c: C reveal_type(c) # N: Revealed type is "TypedDict('__main__.C', {'x': builtins.int, 'y'?: builtins.int, 'z': builtins.str})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNonTotalTypedDictInErrorMessages] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}, total=False) B = TypedDict('B', {'x': int, 'z': str, 'a': int}, total=False) @@ -1192,14 +1471,15 @@ c: C def f(a: A) -> None: pass l = [a, b] # Join generates an anonymous TypedDict -f(l) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x'?: int})]"; expected "A" +f(l) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x'?: int})]"; expected "A" ll = [b, c] -f(ll) # E: Argument 1 to "f" has incompatible type "List[TypedDict({'x'?: int, 'z'?: str})]"; expected "A" +f(ll) # E: Argument 1 to "f" has incompatible type "list[TypedDict({'x'?: int, 'z'?: str})]"; expected "A" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testNonTotalTypedDictCanBeEmpty] # flags: --warn-unreachable -from mypy_extensions import TypedDict +from typing import TypedDict class A(TypedDict): ... @@ -1216,70 +1496,80 @@ if not a: if not b: reveal_type(b) # N: Revealed type is "TypedDict('__main__.B', {'x'?: builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Create Type (Errors) [case testCannotCreateTypedDictTypeWithTooFewArguments] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point') # E: Too few arguments for TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithTooManyArguments] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}, dict) # E: Unexpected arguments to TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict(dict, {'x': int, 'y': int}) # E: TypedDict() expects a string literal as the first argument [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidItems] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x'}) # E: TypedDict() expects a dictionary literal as the second argument [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithKwargs] -from mypy_extensions import TypedDict +from typing import TypedDict d = {'x': int, 'y': int} Point = TypedDict('Point', {**d}) # E: Invalid TypedDict() field name [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithBytes] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict(b'Point', {'x': int, 'y': int}) # E: TypedDict() expects a string literal as the first argument # This technically works at runtime but doesn't make sense. Point2 = TypedDict('Point2', {b'x': int}) # E: Invalid TypedDict() field name [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- NOTE: The following code works at runtime but is not yet supported by mypy. -- Keyword arguments may potentially be supported in the future. [case testCannotCreateTypedDictTypeWithNonpositionalArgs] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict(typename='Point', fields={'x': int, 'y': int}) # E: Unexpected arguments to TypedDict() [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidItemName] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {int: int, int: int}) # E: Invalid TypedDict() field name [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidItemType] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': 1, 'y': 1}) # E: Invalid type: try using Literal[1] instead? [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCannotCreateTypedDictTypeWithInvalidName2] -from mypy_extensions import TypedDict +from typing import TypedDict X = TypedDict('Y', {'x': int}) # E: First argument "Y" to TypedDict() does not match variable name "X" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] -- Overloading [case testTypedDictOverloading] -from typing import overload, Iterable -from mypy_extensions import TypedDict +from typing import overload, Iterable, TypedDict A = TypedDict('A', {'x': int}) @@ -1296,8 +1586,7 @@ reveal_type(f(1)) # N: Revealed type is "builtins.int" [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading2] -from typing import overload, Iterable -from mypy_extensions import TypedDict +from typing import overload, Iterable, TypedDict A = TypedDict('A', {'x': int}) @@ -1312,16 +1601,15 @@ f(a) [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] [out] -main:13: error: Argument 1 to "f" has incompatible type "A"; expected "Iterable[int]" -main:13: note: Following member(s) of "A" have conflicts: -main:13: note: Expected: -main:13: note: def __iter__(self) -> Iterator[int] -main:13: note: Got: -main:13: note: def __iter__(self) -> Iterator[str] +main:12: error: Argument 1 to "f" has incompatible type "A"; expected "Iterable[int]" +main:12: note: Following member(s) of "A" have conflicts: +main:12: note: Expected: +main:12: note: def __iter__(self) -> Iterator[int] +main:12: note: Got: +main:12: note: def __iter__(self) -> Iterator[str] [case testTypedDictOverloading3] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) @@ -1340,8 +1628,7 @@ f(a) # E: No overload variant of "f" matches argument type "A" \ [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading4] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) B = TypedDict('B', {'x': str}) @@ -1361,8 +1648,7 @@ f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "A" [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading5] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) B = TypedDict('B', {'y': str}) @@ -1384,8 +1670,7 @@ f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "A" [typing fixtures/typing-typeddict.pyi] [case testTypedDictOverloading6] -from typing import overload -from mypy_extensions import TypedDict +from typing import TypedDict, overload A = TypedDict('A', {'x': int}) B = TypedDict('B', {'y': str}) @@ -1407,8 +1692,7 @@ reveal_type(f(b)) # N: Revealed type is "builtins.str" -- Special cases [case testForwardReferenceInTypedDict] -from typing import Mapping -from mypy_extensions import TypedDict +from typing import TypedDict, Mapping X = TypedDict('X', {'b': 'B', 'c': 'C'}) class B: pass class C(B): pass @@ -1417,10 +1701,10 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'b': __main__.B, m1: Mapping[str, object] = x m2: Mapping[str, B] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, B]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testForwardReferenceInClassTypedDict] -from typing import Mapping -from mypy_extensions import TypedDict +from typing import TypedDict, Mapping class X(TypedDict): b: 'B' c: 'C' @@ -1431,19 +1715,20 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'b': __main__.B, m1: Mapping[str, object] = x m2: Mapping[str, B] = x # E: Incompatible types in assignment (expression has type "X", variable has type "Mapping[str, B]") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testForwardReferenceToTypedDictInTypedDict] -from typing import Mapping -from mypy_extensions import TypedDict +from typing import TypedDict, Mapping X = TypedDict('X', {'a': 'A'}) A = TypedDict('A', {'b': int}) x: X reveal_type(x) # N: Revealed type is "TypedDict('__main__.X', {'a': TypedDict('__main__.A', {'b': builtins.int})})" reveal_type(x['a']['b']) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testSelfRecursiveTypedDictInheriting] -from mypy_extensions import TypedDict +from typing import TypedDict def test() -> None: class MovieBase(TypedDict): @@ -1456,10 +1741,10 @@ def test() -> None: m: Movie reveal_type(m['director']['name']) # N: Revealed type is "Any" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testSubclassOfRecursiveTypedDict] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict def test() -> None: class Command(TypedDict): @@ -1470,13 +1755,13 @@ def test() -> None: pass hc = HelpCommand(subcommands=[]) - reveal_type(hc) # N: Revealed type is "TypedDict('__main__.HelpCommand@8', {'subcommands': builtins.list[Any]})" + reveal_type(hc) # N: Revealed type is "TypedDict('__main__.HelpCommand@7', {'subcommands': builtins.list[Any]})" [builtins fixtures/list.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testTypedDictForwardAsUpperBound] -from typing import TypeVar, Generic -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Generic T = TypeVar('T', bound='M') class G(Generic[T]): x: T @@ -1488,12 +1773,13 @@ z: int = G[M]().x['x'] # type: ignore[used-before-def] class M(TypedDict): x: int [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testTypedDictWithImportCycleForward] import a [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict from b import f N = TypedDict('N', {'a': str}) @@ -1504,6 +1790,7 @@ def f(x: a.N) -> None: reveal_type(x) reveal_type(x['a']) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] tmp/b.py:4: note: Revealed type is "TypedDict('a.N', {'a': builtins.str})" tmp/b.py:5: note: Revealed type is "builtins.str" @@ -1524,14 +1811,15 @@ tp(x='no') # E: Incompatible types (expression has type "str", TypedDict item " [file b.py] from a import C -from mypy_extensions import TypedDict +from typing import TypedDict tp = TypedDict('tp', {'x': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] [case testTypedDictAsStarStarArg] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) class B: pass @@ -1551,11 +1839,11 @@ f4(**a) # E: Extra argument "y" from **args for "f4" f5(**a) # E: Missing positional arguments "y", "z" in call to "f5" f6(**a) # E: Extra argument "y" from **args for "f6" f1(1, **a) # E: "f1" gets multiple values for keyword argument "x" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarArgConstraints] -from typing import TypeVar, Union -from mypy_extensions import TypedDict +from typing import TypedDict, TypeVar, Union T = TypeVar('T') S = TypeVar('S') @@ -1564,10 +1852,11 @@ def f1(x: T, y: S) -> Union[T, S]: ... A = TypedDict('A', {'y': int, 'x': str}) a: A reveal_type(f1(**a)) # N: Revealed type is "Union[builtins.str, builtins.int]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarArgCalleeKwargs] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'x': str, 'y': str}) @@ -1585,9 +1874,10 @@ g(1, **a) # E: "g" gets multiple values for keyword argument "x" g(1, **b) # E: "g" gets multiple values for keyword argument "x" \ # E: Argument "x" to "g" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarTwice] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict('A', {'x': int, 'y': str}) B = TypedDict('B', {'z': bytes}) @@ -1609,11 +1899,11 @@ f1(**a, **c) # E: "f1" gets multiple values for keyword argument "x" \ # E: Argument "x" to "f1" has incompatible type "str"; expected "int" f1(**c, **a) # E: "f1" gets multiple values for keyword argument "x" \ # E: Argument "x" to "f1" has incompatible type "str"; expected "int" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictAsStarStarAndDictAsStarStar] -from mypy_extensions import TypedDict -from typing import Any, Dict +from typing import Any, Dict, TypedDict TD = TypedDict('TD', {'x': int, 'y': str}) @@ -1628,10 +1918,10 @@ f1(**d, **td) f2(**td, **d) f2(**d, **td) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictNonMappingMethods] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict A = TypedDict('A', {'x': int, 'y': List[int]}) a: A @@ -1644,7 +1934,7 @@ a.clear() # E: "A" has no attribute "clear" a.setdefault('invalid', 1) # E: TypedDict "A" has no key "invalid" reveal_type(a.setdefault('x', 1)) # N: Revealed type is "builtins.int" reveal_type(a.setdefault('y', [])) # N: Revealed type is "builtins.list[builtins.int]" -a.setdefault('y', '') # E: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "List[int]" +a.setdefault('y', '') # E: Argument 2 to "setdefault" of "TypedDict" has incompatible type "str"; expected "list[int]" x = '' a.setdefault(x, 1) # E: Expected TypedDict key to be string literal alias = a.setdefault @@ -1659,12 +1949,12 @@ a.update({'z': 1}) # E: Unexpected TypedDict key "z" a.update({'z': 1, 'zz': 1}) # E: Unexpected TypedDict keys ("z", "zz") a.update({'z': 1, 'x': 1}) # E: Expected TypedDict key "x" but found keys ("z", "x") d = {'x': 1} -a.update(d) # E: Argument 1 to "update" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'x'?: int, 'y'?: List[int]})" +a.update(d) # E: Argument 1 to "update" of "TypedDict" has incompatible type "dict[str, int]"; expected "TypedDict({'x'?: int, 'y'?: list[int]})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictPopMethod] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict A = TypedDict('A', {'x': int, 'y': List[int]}, total=False) B = TypedDict('B', {'x': int}) @@ -1674,7 +1964,7 @@ b: B reveal_type(a.pop('x')) # N: Revealed type is "builtins.int" reveal_type(a.pop('y', [])) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(a.pop('x', '')) # N: Revealed type is "Union[builtins.int, Literal['']?]" -reveal_type(a.pop('x', (1, 2))) # N: Revealed type is "Union[builtins.int, Tuple[Literal[1]?, Literal[2]?]]" +reveal_type(a.pop('x', (1, 2))) # N: Revealed type is "Union[builtins.int, tuple[Literal[1]?, Literal[2]?]]" a.pop('invalid', '') # E: TypedDict "A" has no key "invalid" b.pop('x') # E: Key "x" of TypedDict "B" cannot be deleted x = '' @@ -1683,10 +1973,10 @@ pop = b.pop pop('x') # E: Argument 1 has incompatible type "str"; expected "Never" pop('invalid') # E: Argument 1 has incompatible type "str"; expected "Never" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictDel] -from typing import List -from mypy_extensions import TypedDict +from typing import List, TypedDict A = TypedDict('A', {'x': int, 'y': List[int]}, total=False) B = TypedDict('B', {'x': int}) @@ -1703,10 +1993,10 @@ alias = b.__delitem__ alias('x') alias(s) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testPluginUnionsOfTypedDicts] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union class TDA(TypedDict): a: int @@ -1719,8 +2009,8 @@ class TDB(TypedDict): td: Union[TDA, TDB] -reveal_type(td.get('a')) # N: Revealed type is "Union[builtins.int, None]" -reveal_type(td.get('b')) # N: Revealed type is "Union[builtins.str, None, builtins.int]" +reveal_type(td.get('a')) # N: Revealed type is "builtins.int" +reveal_type(td.get('b')) # N: Revealed type is "Union[builtins.str, builtins.int]" reveal_type(td.get('c')) # N: Revealed type is "builtins.object" reveal_type(td['a']) # N: Revealed type is "builtins.int" @@ -1731,8 +2021,7 @@ reveal_type(td['c']) # N: Revealed type is "Union[Any, builtins.int]" \ [typing fixtures/typing-typeddict.pyi] [case testPluginUnionsOfTypedDictsNonTotal] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union class TDA(TypedDict, total=False): a: int @@ -1747,8 +2036,9 @@ td: Union[TDA, TDB] reveal_type(td.pop('a')) # N: Revealed type is "builtins.int" reveal_type(td.pop('b')) # N: Revealed type is "Union[builtins.str, builtins.int]" -reveal_type(td.pop('c')) # E: TypedDict "TDA" has no key "c" \ - # N: Revealed type is "Union[Any, builtins.int]" +reveal_type(td.pop('c')) # N: Revealed type is "Union[Any, builtins.int]" \ + # E: TypedDict "TDA" has no key "c" + [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -1762,6 +2052,7 @@ class Point(TypedDict): p = Point(x=42, y=1337) reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testCanCreateTypedDictWithTypingProper] from typing import TypedDict @@ -1776,8 +2067,7 @@ reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtin [typing fixtures/typing-typeddict.pyi] [case testTypedDictOptionalUpdate] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union class A(TypedDict): x: int @@ -1785,6 +2075,7 @@ class A(TypedDict): d: A d.update({'x': 1}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictOverlapWithDict] # mypy: strict-equality @@ -1812,7 +2103,7 @@ class Config(TypedDict): x: Dict[str, str] y: Config -x == y # E: Non-overlapping equality check (left operand type: "Dict[str, str]", right operand type: "Config") +x == y # E: Non-overlapping equality check (left operand type: "dict[str, str]", right operand type: "Config") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -1842,7 +2133,7 @@ class Config(TypedDict, total=False): x: Dict[str, str] y: Config -x == y # E: Non-overlapping equality check (left operand type: "Dict[str, str]", right operand type: "Config") +x == y # E: Non-overlapping equality check (left operand type: "dict[str, str]", right operand type: "Config") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -1855,7 +2146,7 @@ class Config(TypedDict): b: str x: Config -x == {} # E: Non-overlapping equality check (left operand type: "Config", right operand type: "Dict[Never, Never]") +x == {} # E: Non-overlapping equality check (left operand type: "Config", right operand type: "dict[Never, Never]") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -2015,8 +2306,7 @@ v = {bad2: 2} # E: Missing key "num" for TypedDict "Value" \ [case testOperatorContainsNarrowsTypedDicts_unionWithList] from __future__ import annotations -from typing import assert_type, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, TypedDict, Union @final class D(TypedDict): @@ -2033,12 +2323,11 @@ else: assert_type(d_or_list, list[str]) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_total] from __future__ import annotations -from typing import assert_type, Literal, TypedDict, TypeVar, Union -from typing_extensions import final +from typing import assert_type, final, Literal, TypedDict, TypeVar, Union @final class D1(TypedDict): @@ -2084,13 +2373,12 @@ def f(arg: TD) -> None: [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_final] # flags: --warn-unreachable from __future__ import annotations -from typing import assert_type, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, TypedDict, Union @final class DFinal(TypedDict): @@ -2128,12 +2416,11 @@ else: assert_type(d_union, DNotFinal) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_partialThroughTotalFalse] from __future__ import annotations -from typing import assert_type, Literal, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, Literal, TypedDict, Union @final class DTotal(TypedDict): @@ -2164,12 +2451,12 @@ else: assert_type(d, Union[DTotal, DNotTotal]) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testOperatorContainsNarrowsTypedDicts_partialThroughNotRequired] from __future__ import annotations -from typing import assert_type, Required, NotRequired, TypedDict, Union -from typing_extensions import final +from typing import assert_type, final, TypedDict, Union +from typing_extensions import Required, NotRequired @final class D1(TypedDict): @@ -2196,11 +2483,10 @@ else: assert_type(d, Union[D1, D2]) [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testCannotSubclassFinalTypedDict] -from typing import TypedDict -from typing_extensions import final +from typing import TypedDict, final @final class DummyTypedDict(TypedDict): @@ -2212,11 +2498,10 @@ class SubType(DummyTypedDict): # E: Cannot inherit from final class "DummyTypedD pass [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testCannotSubclassFinalTypedDictWithForwardDeclarations] -from typing import TypedDict -from typing_extensions import final +from typing import TypedDict, final @final class DummyTypedDict(TypedDict): @@ -2228,7 +2513,7 @@ class SubType(DummyTypedDict): # E: Cannot inherit from final class "DummyTypedD class ForwardDeclared: pass [builtins fixtures/dict.pyi] -[typing fixtures/typing-typeddict.pyi] +[typing fixtures/typing-full.pyi] [case testTypedDictTypeNarrowingWithFinalKey] from typing import Final, Optional, TypedDict @@ -2248,8 +2533,7 @@ if foo[KEY_NAME] is not None: [typing fixtures/typing-typeddict.pyi] [case testTypedDictDoubleForwardClass] -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict class Foo(TypedDict): bar: Bar @@ -2264,8 +2548,7 @@ reveal_type(foo['baz']) # N: Revealed type is "builtins.list[Any]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictDoubleForwardFunc] -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict Foo = TypedDict('Foo', {'bar': 'Bar', 'baz': 'Bar'}) @@ -2278,8 +2561,7 @@ reveal_type(foo['baz']) # N: Revealed type is "builtins.list[Any]" [typing fixtures/typing-typeddict.pyi] [case testTypedDictDoubleForwardMixed] -from mypy_extensions import TypedDict -from typing import Any, List +from typing import Any, List, TypedDict Bar = List[Any] @@ -2356,11 +2638,12 @@ d[True] # E: TypedDict key must be a string literal; expected one of ("foo") [typing fixtures/typing-typeddict.pyi] [case testTypedDictUppercaseKey] -from mypy_extensions import TypedDict +from typing import TypedDict Foo = TypedDict('Foo', {'camelCaseKey': str}) value: Foo = {} # E: Missing key "camelCaseKey" for TypedDict "Foo" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictWithDeferredFieldTypeEval] from typing import Generic, TypeVar, TypedDict, NotRequired @@ -2614,8 +2897,9 @@ def func(foo: Union[Foo1, Foo2]): del foo["missing"] # E: TypedDict "Foo1" has no key "missing" \ # E: TypedDict "Foo2" has no key "missing" - del foo[1] # E: Expected TypedDict key to be string literal \ - # E: Argument 1 to "__delitem__" has incompatible type "int"; expected "str" + del foo[1] # E: Argument 1 to "__delitem__" has incompatible type "int"; expected "str" \ + # E: Expected TypedDict key to be string literal + [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -2736,7 +3020,7 @@ class TD(TypedDict): reveal_type(TD.__iter__) # N: Revealed type is "def (typing._TypedDict) -> typing.Iterator[builtins.str]" reveal_type(TD.__annotations__) # N: Revealed type is "typing.Mapping[builtins.str, builtins.object]" -reveal_type(TD.values) # N: Revealed type is "def (self: typing.Mapping[T`1, T_co`2]) -> typing.Iterable[T_co`2]" +reveal_type(TD.values) # N: Revealed type is "def (self: typing.Mapping[builtins.str, builtins.object]) -> typing.Iterable[builtins.object]" [builtins fixtures/dict-full.pyi] [typing fixtures/typing-typeddict.pyi] @@ -2753,11 +3037,11 @@ Alias = TD[List[T]] ad: Alias[str] reveal_type(ad) # N: Revealed type is "TypedDict('__main__.TD', {'key': builtins.int, 'value': builtins.list[builtins.str]})" -Alias[str](key=0, value=0) # E: Incompatible types (expression has type "int", TypedDict item "value" has type "List[str]") +Alias[str](key=0, value=0) # E: Incompatible types (expression has type "int", TypedDict item "value" has type "list[str]") # Generic aliases are *always* filled with Any, so this is different from TD(...) call. Alias(key=0, value=0) # E: Missing type parameters for generic type "Alias" \ - # E: Incompatible types (expression has type "int", TypedDict item "value" has type "List[Any]") + # E: Incompatible types (expression has type "int", TypedDict item "value" has type "list[Any]") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -2858,7 +3142,7 @@ def method(message: Response) -> None: ... method({'type': 'a', 'value': True}) # OK method({'type': 'b', 'value': 'abc'}) # OK method({'type': 'a', 'value': 'abc'}) # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \ - # E: Argument 1 to "method" has incompatible type "Dict[str, str]"; expected "Union[A, B]" + # E: Argument 1 to "method" has incompatible type "dict[str, str]"; expected "Union[A, B]" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -2877,12 +3161,12 @@ class D(TypedDict, total=False): def foo(data: Union[A, B]) -> None: ... foo({"foo": {"c": "foo"}}) # OK foo({"foo": {"e": "foo"}}) # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \ - # E: Argument 1 to "foo" has incompatible type "Dict[str, Dict[str, str]]"; expected "Union[A, B]" + # E: Argument 1 to "foo" has incompatible type "dict[str, dict[str, str]]"; expected "Union[A, B]" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] [case testTypedDictMissingEmptyKey] -from typing_extensions import TypedDict +from typing import TypedDict class A(TypedDict): my_attr_1: str @@ -2894,7 +3178,7 @@ d[''] # E: TypedDict "A" has no key "" [typing fixtures/typing-typeddict.pyi] [case testTypedDictFlexibleUpdate] -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int}) @@ -2909,7 +3193,7 @@ a.update(a) [case testTypedDictStrictUpdate] # flags: --extra-checks -from mypy_extensions import TypedDict +from typing import TypedDict A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int}) @@ -2923,8 +3207,7 @@ a.update(a) # OK [typing fixtures/typing-typeddict.pyi] [case testTypedDictFlexibleUpdateUnion] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int}) @@ -2937,8 +3220,7 @@ a.update(u) [typing fixtures/typing-typeddict.pyi] [case testTypedDictFlexibleUpdateUnionExtra] -from typing import Union -from mypy_extensions import TypedDict +from typing import TypedDict, Union A = TypedDict("A", {"foo": int, "bar": int}) B = TypedDict("B", {"foo": int, "extra": int}) @@ -2952,8 +3234,7 @@ a.update(u) [case testTypedDictFlexibleUpdateUnionStrict] # flags: --extra-checks -from typing import Union, NotRequired -from mypy_extensions import TypedDict +from typing import TypedDict, Union, NotRequired A = TypedDict("A", {"foo": int, "bar": int}) A1 = TypedDict("A1", {"foo": int, "bar": NotRequired[int]}) @@ -3013,7 +3294,7 @@ class Bar(TypedDict): b: int foo: Foo = {"a": 1, "b": "a"} -bar1: Bar = {**foo, "b": 2} # Incompatible item is overriden +bar1: Bar = {**foo, "b": 2} # Incompatible item is overridden bar2: Bar = {**foo, "a": 2} # E: Incompatible types (expression has type "str", TypedDict item "b" has type "int") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -3137,7 +3418,7 @@ bar2: Bar = {**bar, "c": {**bar["c"], "b": "wrong"}, "d": 2} # E: Incompatible [typing fixtures/typing-typeddict.pyi] [case testTypedDictUnpackOverrideRequired] -from mypy_extensions import TypedDict +from typing import TypedDict Details = TypedDict('Details', {'first_name': str, 'last_name': str}) DetailsSubset = TypedDict('DetailsSubset', {'first_name': str, 'last_name': str}, total=False) @@ -3155,7 +3436,7 @@ class Bar(TypedDict): pass foo: Dict[str, Any] = {} -bar: Bar = {**foo} # E: Unsupported type "Dict[str, Any]" for ** expansion in TypedDict +bar: Bar = {**foo} # E: Unsupported type "dict[str, Any]" for ** expansion in TypedDict [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -3268,8 +3549,7 @@ f: Foo = {**foo("no")} # E: Argument 1 to "foo" has incompatible type "str"; ex [case testTypedDictWith__or__method] -from typing import Dict -from mypy_extensions import TypedDict +from typing import Dict, TypedDict class Foo(TypedDict): key: int @@ -3286,7 +3566,7 @@ d1: Dict[str, int] d2: Dict[int, str] reveal_type(foo1 | d1) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" -foo1 | d2 # E: Unsupported operand types for | ("Foo" and "Dict[int, str]") +foo1 | d2 # E: Unsupported operand types for | ("Foo" and "dict[int, str]") class Bar(TypedDict): @@ -3304,12 +3584,12 @@ reveal_type(bar | {'key': 'a', 'value': 1}) # N: Revealed type is "builtins.dic reveal_type(bar | foo1) # N: Revealed type is "TypedDict('__main__.Bar', {'key': builtins.int, 'value': builtins.str})" reveal_type(bar | d1) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" -bar | d2 # E: Unsupported operand types for | ("Bar" and "Dict[int, str]") +bar | d2 # E: Unsupported operand types for | ("Bar" and "dict[int, str]") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__or__method_error] -from mypy_extensions import TypedDict +from typing import TypedDict class Foo(TypedDict): key: int @@ -3318,22 +3598,18 @@ foo: Foo = {'key': 1} foo | 1 class SubDict(dict): ... -foo | SubDict() +reveal_type(foo | SubDict()) [out] main:7: error: No overload variant of "__or__" of "TypedDict" matches argument type "int" main:7: note: Possible overload variants: main:7: note: def __or__(self, TypedDict({'key'?: int}), /) -> Foo -main:7: note: def __or__(self, Dict[str, Any], /) -> Dict[str, object] -main:10: error: No overload variant of "__ror__" of "dict" matches argument type "Foo" -main:10: note: Possible overload variants: -main:10: note: def __ror__(self, Dict[Any, Any], /) -> Dict[Any, Any] -main:10: note: def [T, T2] __ror__(self, Dict[T, T2], /) -> Dict[Union[Any, T], Union[Any, T2]] +main:7: note: def __or__(self, dict[str, Any], /) -> dict[str, object] +main:10: note: Revealed type is "builtins.dict[builtins.str, builtins.object]" [builtins fixtures/dict-full.pyi] [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__ror__method] -from typing import Dict -from mypy_extensions import TypedDict +from typing import Dict, TypedDict class Foo(TypedDict): key: int @@ -3349,9 +3625,11 @@ d1: Dict[str, int] d2: Dict[int, str] reveal_type(d1 | foo) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" -d2 | foo # E: Unsupported operand types for | ("Dict[int, str]" and "Foo") -1 | foo # E: Unsupported left operand type for | ("int") - +d2 | foo # E: Unsupported operand types for | ("dict[int, str]" and "Foo") +1 | foo # E: No overload variant of "__ror__" of "TypedDict" matches argument type "int" \ + # N: Possible overload variants: \ + # N: def __ror__(self, TypedDict({'key'?: int}), /) -> Foo \ + # N: def __ror__(self, dict[str, Any], /) -> dict[str, object] class Bar(TypedDict): key: int @@ -3367,13 +3645,12 @@ reveal_type({'value': 1} | bar) # N: Revealed type is "builtins.dict[builtins.s reveal_type({'key': 'a', 'value': 1} | bar) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" reveal_type(d1 | bar) # N: Revealed type is "builtins.dict[builtins.str, builtins.object]" -d2 | bar # E: Unsupported operand types for | ("Dict[int, str]" and "Bar") +d2 | bar # E: Unsupported operand types for | ("dict[int, str]" and "Bar") [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictWith__ior__method] -from typing import Dict -from mypy_extensions import TypedDict +from typing import Dict, TypedDict class Foo(TypedDict): key: int @@ -3389,8 +3666,8 @@ foo |= {'b': 2} # E: Unexpected TypedDict key "b" d1: Dict[str, int] d2: Dict[int, str] -foo |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'key'?: int})" -foo |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int})" +foo |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[str, int]"; expected "TypedDict({'key'?: int})" +foo |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[int, str]"; expected "TypedDict({'key'?: int})" class Bar(TypedDict): @@ -3404,8 +3681,8 @@ bar |= {'key': 'a', 'value': 'a', 'b': 'a'} # E: Expected TypedDict keys ("key" # E: Incompatible types (expression has type "str", TypedDict item "key" has type "int") bar |= foo -bar |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[str, int]"; expected "TypedDict({'key'?: int, 'value'?: str})" -bar |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "Dict[int, str]"; expected "TypedDict({'key'?: int, 'value'?: str})" +bar |= d1 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[str, int]"; expected "TypedDict({'key'?: int, 'value'?: str})" +bar |= d2 # E: Argument 1 to "__ior__" of "TypedDict" has incompatible type "dict[int, str]"; expected "TypedDict({'key'?: int, 'value'?: str})" [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict-iror.pyi] @@ -3469,7 +3746,7 @@ class TotalInTheMiddle(TypedDict, a=1, total=True, b=2, c=3): # E: Unexpected k [typing fixtures/typing-typeddict.pyi] [case testCanCreateClassWithFunctionBasedTypedDictBase] -from mypy_extensions import TypedDict +from typing import TypedDict class Params(TypedDict("Params", {'x': int})): pass @@ -3477,6 +3754,7 @@ class Params(TypedDict("Params", {'x': int})): p: Params = {'x': 2} reveal_type(p) # N: Revealed type is "TypedDict('__main__.Params', {'x': builtins.int})" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testInitTypedDictFromType] from typing import TypedDict, Type @@ -3487,7 +3765,7 @@ class Point(TypedDict, total=False): y: int def func(cls: Type[Point]) -> None: - reveal_type(cls) # N: Revealed type is "Type[TypedDict('__main__.Point', {'x': builtins.int, 'y'?: builtins.int})]" + reveal_type(cls) # N: Revealed type is "type[TypedDict('__main__.Point', {'x': builtins.int, 'y'?: builtins.int})]" cls(x=1, y=2) cls(1, 2) # E: Too many positional arguments cls(x=1) @@ -3511,7 +3789,7 @@ class A(Generic[T]): self.a = a def func(self) -> T: - reveal_type(self.a) # N: Revealed type is "Type[T`1]" + reveal_type(self.a) # N: Revealed type is "type[T`1]" self.a(x=1, y=2) self.a(y=2) # E: Missing named argument "x" return self.a(x=1) @@ -3519,8 +3797,8 @@ class A(Generic[T]): [builtins fixtures/tuple.pyi] [case testNameUndefinedErrorDoesNotLoseUnpackedKWArgsInformation] -from typing import overload -from typing_extensions import TypedDict, Unpack +from typing import TypedDict, overload +from typing_extensions import Unpack class TD(TypedDict, total=False): x: int @@ -3555,10 +3833,11 @@ class B(A): reveal_type(B.f) # N: Revealed type is "def (self: __main__.B, **kwargs: Unpack[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: builtins.str})])" B().f(x=1.0) # E: Argument "x" to "f" of "B" has incompatible type "float"; expected "int" [builtins fixtures/primitives.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictUnpackWithParamSpecInference] -from typing import TypeVar, ParamSpec, Callable -from typing_extensions import TypedDict, Unpack +from typing import TypedDict, TypeVar, ParamSpec, Callable +from typing_extensions import Unpack P = ParamSpec("P") R = TypeVar("R") @@ -3579,11 +3858,12 @@ class Test: def h(self, **params: Unpack[Params]) -> None: 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] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] [case testTypedDictUnpackSingleWithSubtypingNoCrash] -from typing import Callable -from typing_extensions import TypedDict, Unpack +from typing import Callable, TypedDict +from typing_extensions import Unpack class Kwargs(TypedDict): name: str @@ -3597,7 +3877,8 @@ class C: # 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] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypedDictInlineNoOldStyleAlias] # flags: --enable-incomplete-feature=InlineTypedDict @@ -3718,19 +3999,24 @@ del x["optional_key"] # E: Key "optional_key" of TypedDict "TP" cannot be delet [typing fixtures/typing-typeddict.pyi] [case testTypedDictReadOnlyMutateMethods] -from typing import ReadOnly, TypedDict +from typing import ReadOnly, NotRequired, TypedDict class TP(TypedDict): key: ReadOnly[str] + optional_key: ReadOnly[NotRequired[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" +reveal_type(x.pop("key")) # N: Revealed type is "builtins.str" \ + # E: Key "key" of TypedDict "TP" cannot be deleted +reveal_type(x.pop("optional_key")) # N: Revealed type is "builtins.str" \ + # E: Key "optional_key" of TypedDict "TP" cannot be deleted + 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("optional_key", "foo") # E: ReadOnly TypedDict key "optional_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] @@ -3760,7 +4046,8 @@ x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated [typing fixtures/typing-typeddict.pyi] [case testTypedDictReadOnlyMutate__ior__Statements] -from typing_extensions import ReadOnly, TypedDict +from typing import TypedDict +from typing_extensions import ReadOnly class TP(TypedDict): key: ReadOnly[str] @@ -3775,7 +4062,8 @@ x |= {"key": "a", "other": 1, "mutable": True} # E: ReadOnly TypedDict keys ("k [typing fixtures/typing-typeddict-iror.pyi] [case testTypedDictReadOnlyMutate__or__Statements] -from typing_extensions import ReadOnly, TypedDict +from typing import TypedDict +from typing_extensions import ReadOnly class TP(TypedDict): key: ReadOnly[str] @@ -3814,7 +4102,7 @@ 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 +tp4: TP = {**tp, **d} # E: Unsupported type "dict[str, object]" for ** expansion in TypedDict [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] @@ -3894,6 +4182,20 @@ 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 @@ -3921,7 +4223,7 @@ 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]" +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] @@ -3953,7 +4255,8 @@ reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'=: builtins.int, 'y': bu [typing fixtures/typing-typeddict.pyi] [case testTypedDictReadOnlyUnpack] -from typing_extensions import TypedDict, Unpack, ReadOnly +from typing import TypedDict +from typing_extensions import Unpack, ReadOnly class TD(TypedDict): x: ReadOnly[int] @@ -3988,3 +4291,259 @@ class TP(TypedDict): 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] + + +[case testTypedDictFinalAndClassVar] +from typing import TypedDict, Final, ClassVar + +class My(TypedDict): + a: Final # E: Final[...] can't be used inside a TypedDict + b: Final[int] # E: Final[...] can't be used inside a TypedDict + c: ClassVar # E: ClassVar[...] can't be used inside a TypedDict + d: ClassVar[int] # E: ClassVar[...] can't be used inside a TypedDict + +Func = TypedDict('Func', { + 'a': Final, # E: Final[...] can't be used inside a TypedDict + 'b': Final[int], # E: Final[...] can't be used inside a TypedDict + 'c': ClassVar, # E: ClassVar[...] can't be used inside a TypedDict + 'd': ClassVar[int], # E: ClassVar[...] can't be used inside a TypedDict +}) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictNestedInClassAndInherited] +from typing import TypedDict + +class Base: + class Params(TypedDict): + name: str + +class Derived(Base): + pass + +class DerivedOverride(Base): + class Params(Base.Params): + pass + +Base.Params(name="Robert") +Derived.Params(name="Robert") +DerivedOverride.Params(name="Robert") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testEnumAsClassMemberNoCrash] +# https://github.com/python/mypy/issues/18736 +from typing import TypedDict + +class Base: + def __init__(self, namespace: dict[str, str]) -> None: + # Not a bug: trigger defer + names = {n: n for n in namespace if fail} # E: Name "fail" is not defined + self.d = TypedDict("d", names) # E: TypedDict type as attribute is not supported \ + # E: TypedDict() expects a dictionary literal as the second argument +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAlias] +from typing import NotRequired, TypedDict +from typing_extensions import TypeAlias + +class Base(TypedDict): + foo: int + +Base1 = Base +class Child1(Base1): + bar: NotRequired[int] +c11: Child1 = {"foo": 0} +c12: Child1 = {"foo": 0, "bar": 1} +c13: Child1 = {"foo": 0, "bar": 1, "baz": "error"} # E: Extra key "baz" for TypedDict "Child1" + +Base2: TypeAlias = Base +class Child2(Base2): + bar: NotRequired[int] +c21: Child2 = {"foo": 0} +c22: Child2 = {"foo": 0, "bar": 1} +c23: Child2 = {"foo": 0, "bar": 1, "baz": "error"} # E: Extra key "baz" for TypedDict "Child2" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAliasInheritance] +from typing import TypedDict +from typing_extensions import TypeAlias + +class A(TypedDict): + x: str +class B(TypedDict): + y: int + +B1 = B +B2: TypeAlias = B + +class C(A, B1): + pass +c1: C = {"y": 1} # E: Missing key "x" for TypedDict "C" +c2: C = {"x": "x", "y": 2} +c3: C = {"x": 1, "y": 2} # E: Incompatible types (expression has type "int", TypedDict item "x" has type "str") + +class D(A, B2): + pass +d1: D = {"y": 1} # E: Missing key "x" for TypedDict "D" +d2: D = {"x": "x", "y": 2} +d3: D = {"x": 1, "y": 2} # E: Incompatible types (expression has type "int", TypedDict item "x" has type "str") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAliasDuplicateBases] +from typing import TypedDict +from typing_extensions import TypeAlias + +class A(TypedDict): + x: str + +A1 = A +A2 = A +A3: TypeAlias = A + +class E(A1, A2): pass # E: Duplicate base class "A" +class F(A1, A3): pass # E: Duplicate base class "A" +class G(A, A1): pass # E: Duplicate base class "A" + +class H(A, list): pass # E: All bases of a new TypedDict must be TypedDict types +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAliasGeneric] +from typing import Generic, TypedDict, TypeVar +from typing_extensions import TypeAlias + +_T = TypeVar("_T") + +class A(Generic[_T], TypedDict): + x: _T + +# This is by design - no_args aliases are only supported for instances +A0 = A +class B(A0[str]): # E: Bad number of arguments for type alias, expected 0, given 1 + y: int + +A1 = A[_T] +A2: TypeAlias = A[_T] +Aint = A[int] + +class C(A1[_T]): + y: str +c1: C[int] = {"x": 0, "y": "a"} +c2: C[int] = {"x": "no", "y": "a"} # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") + +class D(A2[_T]): + y: str +d1: D[int] = {"x": 0, "y": "a"} +d2: D[int] = {"x": "no", "y": "a"} # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") + +class E(Aint): + y: str +e1: E = {"x": 0, "y": "a"} +e2: E = {"x": "no", "y": "a"} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAliasAsInstanceAttribute] +from typing import TypedDict + +class Dicts: + class TF(TypedDict, total=False): + user_id: int + TotalFalse = TF + +dicts = Dicts() +reveal_type(dicts.TF) # N: Revealed type is "def (*, user_id: builtins.int =) -> TypedDict('__main__.Dicts.TF', {'user_id'?: builtins.int})" +reveal_type(dicts.TotalFalse) # N: Revealed type is "def (*, user_id: builtins.int =) -> TypedDict('__main__.Dicts.TF', {'user_id'?: builtins.int})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testRecursiveNestedTypedDictInference] +from typing import TypedDict, Sequence +from typing_extensions import NotRequired + +class Component(TypedDict): + type: str + components: NotRequired[Sequence['Component']] + +inputs: Sequence[Component] = [{ + 'type': 'tuple', + 'components': [ + {'type': 'uint256'}, + {'type': 'address'}, + ] +}] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAssignableToWiderContext] +from typing import TypedDict, Union + +class TD(TypedDict): + x: int + +x: Union[TD, dict[str, str]] = {"x": "foo"} +y: Union[TD, dict[str, int]] = {"x": "foo"} # E: Dict entry 0 has incompatible type "str": "str"; expected "str": "int" + +def ok(d: Union[TD, dict[str, str]]) -> None: ... +ok({"x": "foo"}) + +def bad(d: Union[TD, dict[str, int]]) -> None: ... +bad({"x": "foo"}) # E: Dict entry 0 has incompatible type "str": "str"; expected "str": "int" + +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-typeform.test b/test-data/unit/check-typeform.test new file mode 100644 index 0000000000000..627067903a92d --- /dev/null +++ b/test-data/unit/check-typeform.test @@ -0,0 +1,842 @@ +-- TypeForm Type + +[case testRecognizesUnparameterizedTypeFormInAnnotation] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm = str +reveal_type(typx) # N: Revealed type is "TypeForm[Any]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testRecognizesParameterizedTypeFormInAnnotation] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[str] = str +reveal_type(typx) # N: Revealed type is "TypeForm[builtins.str]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Expression Location: Assignment + +[case testCanAssignTypeExpressionToTypeFormVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[str] = str +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanAssignTypeExpressionToUnionTypeFormVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[str | None] = str | None +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCannotAssignTypeExpressionToTypeFormVariableWithIncompatibleItemType] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[str] = int # E: Incompatible types in assignment (expression has type "TypeForm[int]", variable has type "TypeForm[str]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanAssignValueExpressionToTypeFormVariableIfValueIsATypeForm1] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx1: TypeForm = str +typx2: TypeForm = typx1 # looks like a type expression: name +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanAssignValueExpressionToTypeFormVariableIfValueIsATypeForm2] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +def identity_tf(x: TypeForm) -> TypeForm: + return x +typx1: TypeForm = str +typx2: TypeForm = identity_tf(typx1) # does not look like a type expression +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCannotAssignValueExpressionToTypeFormVariableIfValueIsNotATypeForm] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +val: int = 42 +typx: TypeForm = val # E: Incompatible types in assignment (expression has type "int", variable has type "TypeForm[Any]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanAssignNoneTypeExpressionToTypeFormVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm = None +reveal_type(typx) # N: Revealed type is "TypeForm[Any]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanAssignTypeExpressionToTypeFormVariableDeclaredEarlier] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Type, TypeForm +typ: Type +typ = int | None # E: Incompatible types in assignment (expression has type "object", variable has type "type[Any]") +typx: TypeForm +typx = int | None +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanAssignTypeExpressionWithStringAnnotationToTypeFormVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[str | None] = 'str | None' +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Expression Location: Function Parameter + +[case testCanPassTypeExpressionToTypeFormParameterInFunction] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +def is_type(typx: TypeForm) -> bool: + return isinstance(typx, type) +is_type(int | None) +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCannotPassTypeExpressionToTypeParameter] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +def is_type(typ: type) -> bool: + return isinstance(typ, type) +is_type(int | None) # E: Argument 1 to "is_type" has incompatible type "object"; expected "type" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanPassTypeExpressionToTypeFormParameterInMethod] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +class C: + def is_type(self, typx: TypeForm) -> bool: + return isinstance(typx, type) +C().is_type(int | None) +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanPassTypeExpressionToTypeFormParameterInOverload] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import overload, TypeForm +@overload +def is_type(typx: TypeForm) -> bool: ... +@overload +def is_type(typx: type) -> bool: ... +def is_type(typx): + return isinstance(typx, type) +is_type(int | None) +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanPassTypeExpressionToTypeFormParameterInDecorator] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Callable, TypeForm, TypeVar +P = TypeVar('P') +R = TypeVar('R') +def expects_type(typx: TypeForm) -> Callable[[Callable[[P], R]], Callable[[P], R]]: + def wrap(func: Callable[[P], R]) -> Callable[[P], R]: + func.expected_type = typx # type: ignore[attr-defined] + return func + return wrap +@expects_type(int | None) +def sum_ints(x: int | None) -> int: + return (x or 0) +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanPassTypeExpressionToTypeFormVarargsParameter] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Callable, ParamSpec, TypeForm, TypeVar +P = ParamSpec('P') +R = TypeVar('R') +def expects_types(*typxs: TypeForm) -> Callable[[Callable[P, R]], Callable[P, R]]: + def wrap(func: Callable[P, R]) -> Callable[P, R]: + func.expected_types = typxs # type: ignore[attr-defined] + return func + return wrap +@expects_types(int | None, int) +def sum_ints(x: int | None, y: int) -> tuple[int, int]: + return ((x or 0), y) +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanPassTypeExpressionWithStringAnnotationToTypeFormParameter] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +def is_type(typx: TypeForm) -> bool: + return isinstance(typx, type) +is_type('int | None') +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Expression Location: Return Statement + +[case testCanReturnTypeExpressionInFunctionWithTypeFormReturnType] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +def maybe_int_type() -> TypeForm: + return int | None +reveal_type(maybe_int_type()) # N: Revealed type is "TypeForm[Any]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testCanReturnTypeExpressionWithStringAnnotationInFunctionWithTypeFormReturnType] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +def maybe_int_type() -> TypeForm: + return 'int | None' +reveal_type(maybe_int_type()) # N: Revealed type is "TypeForm[Any]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Expression Location: Other + +-- In particular ensure that ExpressionChecker.try_parse_as_type_expression() in +-- the TypeChecker pass is able to parse types correctly even though it doesn't +-- have the same rich context as SemanticAnalyzer.try_parse_as_type_expression(). + +[case testTypeExpressionWithoutStringAnnotationRecognizedInOtherSyntacticLocations] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Dict, List, TypeForm +list_of_typx: List[TypeForm] = [int | str] +dict_with_typx_keys: Dict[TypeForm, int] = { + int | str: 1, + str | None: 2, +} +dict_with_typx_keys[int | str] + 1 +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeExpressionWithStringAnnotationNotRecognizedInOtherSyntacticLocations] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Dict, List, TypeForm +list_of_typx: List[TypeForm] = ['int | str'] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: List item 0 has incompatible type "str"; expected "TypeForm[Any]" +dict_with_typx_keys: Dict[TypeForm, int] = { + 'int | str': 1, # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: Dict entry 0 has incompatible type "str": "int"; expected "TypeForm[Any]": "int" + 'str | None': 2, # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: Dict entry 1 has incompatible type "str": "int"; expected "TypeForm[Any]": "int" +} +dict_with_typx_keys['int | str'] += 1 # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: Invalid index type "str" for "dict[TypeForm[Any], int]"; expected type "TypeForm[Any]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] + +[case testValueExpressionWithStringInTypeFormContextEmitsConservativeWarning] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Any, Dict, List, TypeForm +types: Dict[str, TypeForm] = {'any': Any} +# Ensure warning can be ignored if does not apply. +list_of_typx1: List[TypeForm] = [types['any']] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. +list_of_typx2: List[TypeForm] = [types['any']] # type: ignore[maybe-unrecognized-str-typeform] +# Ensure warning can be fixed using the suggested fix in the warning message. +list_of_typx3: List[TypeForm] = ['Any'] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: List item 0 has incompatible type "str"; expected "TypeForm[Any]" +list_of_typx4: List[TypeForm] = [TypeForm('Any')] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] + +[case testSelfRecognizedInOtherSyntacticLocations] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import List, Self, TypeForm +class C: + def foo(self) -> None: + list_of_typx1: List[TypeForm] = [Self] + typx1: TypeForm = Self + typx2: TypeForm = 'Self' +list_of_typx2: List[TypeForm] = [Self] # E: List item 0 has incompatible type "int"; expected "TypeForm[Any]" +typx3: TypeForm = Self # E: Incompatible types in assignment (expression has type "int", variable has type "TypeForm[Any]") +typx4: TypeForm = 'Self' # E: Incompatible types in assignment (expression has type "str", variable has type "TypeForm[Any]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testNameOrDottedNameRecognizedInOtherSyntacticLocations] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +import typing +from typing import List, TypeForm +list_of_typx: List[TypeForm] = [List | typing.Optional[str]] +typx: TypeForm = List | typing.Optional[str] +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testInvalidNameOrDottedNameRecognizedInOtherSyntacticLocations] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import List, TypeForm +list_of_typx1: List[TypeForm] = [NoSuchType] # E: Name "NoSuchType" is not defined +list_of_typx2: List[TypeForm] = [no_such_module.NoSuchType] # E: Name "no_such_module" is not defined +typx1: TypeForm = NoSuchType # E: Name "NoSuchType" is not defined +typx2: TypeForm = no_such_module.NoSuchType # E: Name "no_such_module" is not defined +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Expression Context: Union[TypeForm, ] + +[case testAcceptsTypeFormLiteralAssignedToUnionOfTypeFormAndNonStr] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx_or_int1: TypeForm[int | None] | int = int | None # No error; interpret as TypeForm +typx_or_int2: TypeForm[int | None] | int = str | None # E: Incompatible types in assignment (expression has type "object", variable has type "Union[TypeForm[Optional[int]], int]") +typx_or_int3: TypeForm[int | None] | int = 1 +typx_or_int4: TypeForm[int | None] | int = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[TypeForm[Optional[int]], int]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testAcceptsTypeFormLiteralAssignedToUnionOfTypeFormAndStr] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx_or_str1: TypeForm[int | None] | str = 'int | None' +typx_or_str2: TypeForm[int | None] | str = 'str | None' # No error; interpret as str +typx_or_str3: TypeForm[int | None] | str = 'hello' +typx_or_str4: TypeForm[int | None] | str = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[TypeForm[Optional[int]], str]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testValueExpressionWithStringInTypeFormUnionContextEmitsConservativeWarning1] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import List, TypeForm +list_of_typx1: List[TypeForm[int | None] | str] = ['int | None'] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. +list_of_typx2: List[TypeForm[int | None] | str] = ['str | None'] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testValueExpressionWithStringInTypeFormUnionContextEmitsConservativeWarning2] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import List, TypeForm +list_of_typx3: List[TypeForm[int | None] | int] = ['int | None'] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: List item 0 has incompatible type "str"; expected "Union[TypeForm[Optional[int]], int]" +list_of_typx4: List[TypeForm[str | None] | int] = ['str | None'] # E: TypeForm containing a string annotation cannot be recognized here. Surround with TypeForm(...) to recognize. \ + # E: List item 0 has incompatible type "str"; expected "Union[TypeForm[Optional[str]], int]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Assignability (is_subtype) + +[case testTypeFormToTypeFormAssignability] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] is assignable to TypeForm[T2] iff T1 is assignable to T2. +# - In particular TypeForm[Any] is assignable to TypeForm[Any]. +from typing_extensions import TypeForm +INT_OR_STR_TF: TypeForm[int | str] = int | str +INT_TF: TypeForm[int] = int +STR_TF: TypeForm[str] = str +OBJECT_TF: TypeForm[object] = object +ANY_TF: TypeForm = object +reveal_type(ANY_TF) # N: Revealed type is "TypeForm[Any]" +typx1: TypeForm[int | str] = INT_OR_STR_TF +typx2: TypeForm[int | str] = INT_TF +typx3: TypeForm[int | str] = STR_TF +typx4: TypeForm[int | str] = OBJECT_TF # E: Incompatible types in assignment (expression has type "TypeForm[object]", variable has type "TypeForm[Union[int, str]]") +typx5: TypeForm[int | str] = ANY_TF # no error +typx6: TypeForm[int] = INT_OR_STR_TF # E: Incompatible types in assignment (expression has type "TypeForm[Union[int, str]]", variable has type "TypeForm[int]") +typx7: TypeForm[int] = INT_TF +typx8: TypeForm[int] = STR_TF # E: Incompatible types in assignment (expression has type "TypeForm[str]", variable has type "TypeForm[int]") +typx9: TypeForm[int] = OBJECT_TF # E: Incompatible types in assignment (expression has type "TypeForm[object]", variable has type "TypeForm[int]") +typx10: TypeForm[int] = ANY_TF # no error +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeToTypeFormAssignability] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - Type[C] is assignable to TypeForm[T] iff C is assignable to T. +# - In particular Type[Any] is assignable to TypeForm[Any]. +from typing import Type, TypeForm +INT_T: Type[int] = int +STR_T: Type[str] = str +OBJECT_T: Type[object] = object +ANY_T: Type = object +reveal_type(ANY_T) # N: Revealed type is "type[Any]" +typx1: TypeForm[int | str] = INT_T +typx2: TypeForm[int | str] = STR_T +typx3: TypeForm[int | str] = OBJECT_T # E: Incompatible types in assignment (expression has type "type[object]", variable has type "TypeForm[Union[int, str]]") +typx4: TypeForm[int | str] = ANY_T # no error +typx5: TypeForm[int] = INT_T +typx6: TypeForm[int] = STR_T # E: Incompatible types in assignment (expression has type "type[str]", variable has type "TypeForm[int]") +typx7: TypeForm[int] = OBJECT_T # E: Incompatible types in assignment (expression has type "type[object]", variable has type "TypeForm[int]") +typx8: TypeForm[int] = ANY_T # no error +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeFormToTypeAssignability] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T] is NOT assignable to Type[C]. +# - In particular TypeForm[Any] is NOT assignable to Type[Any]. +from typing import Type, TypeForm +INT_OR_STR_TF: TypeForm[int | str] = int | str +INT_TF: TypeForm[int] = int +STR_TF: TypeForm[str] = str +OBJECT_TF: TypeForm[object] = object +ANY_TF: TypeForm = object +reveal_type(ANY_TF) # N: Revealed type is "TypeForm[Any]" +typ1: Type[int] = INT_OR_STR_TF # E: Incompatible types in assignment (expression has type "TypeForm[Union[int, str]]", variable has type "type[int]") +typ2: Type[int] = INT_TF # E: Incompatible types in assignment (expression has type "TypeForm[int]", variable has type "type[int]") +typ3: Type[int] = STR_TF # E: Incompatible types in assignment (expression has type "TypeForm[str]", variable has type "type[int]") +typ4: Type[int] = OBJECT_TF # E: Incompatible types in assignment (expression has type "TypeForm[object]", variable has type "type[int]") +typ5: Type[int] = ANY_TF # E: Incompatible types in assignment (expression has type "TypeForm[Any]", variable has type "type[int]") +typ6: Type[object] = INT_OR_STR_TF # E: Incompatible types in assignment (expression has type "TypeForm[Union[int, str]]", variable has type "type[object]") +typ7: Type[object] = INT_TF # E: Incompatible types in assignment (expression has type "TypeForm[int]", variable has type "type[object]") +typ8: Type[object] = STR_TF # E: Incompatible types in assignment (expression has type "TypeForm[str]", variable has type "type[object]") +typ9: Type[object] = OBJECT_TF # E: Incompatible types in assignment (expression has type "TypeForm[object]", variable has type "type[object]") +typ10: Type[object] = ANY_TF # E: Incompatible types in assignment (expression has type "TypeForm[Any]", variable has type "type[object]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +# NOTE: This test doesn't involve TypeForm at all, but is still illustrative +# when compared with similarly structured TypeForm-related tests above. +[case testTypeToTypeAssignability] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - Type[C1] is assignable to Type[C2] iff C1 is assignable to C2. +# - In particular Type[Any] is assignable to Type[Any]. +from typing import Type +INT_T: Type[int] = int +STR_T: Type[str] = str +OBJECT_T: Type[object] = object +ANY_T: Type = object +reveal_type(ANY_T) # N: Revealed type is "type[Any]" +typ1: Type[int] = INT_T +typ2: Type[int] = STR_T # E: Incompatible types in assignment (expression has type "type[str]", variable has type "type[int]") +typ3: Type[int] = OBJECT_T # E: Incompatible types in assignment (expression has type "type[object]", variable has type "type[int]") +typ4: Type[int] = ANY_T # no error +typ5: Type[object] = INT_T +typ6: Type[object] = STR_T +typ7: Type[object] = OBJECT_T +typ8: Type[object] = ANY_T # no error +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeFormToObjectAssignability] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T] is assignable to object and Any. +from typing import Any, TypeForm +INT_TF: TypeForm[int] = int +OBJECT_TF: TypeForm[object] = object +ANY_TF: TypeForm = object +reveal_type(ANY_TF) # N: Revealed type is "TypeForm[Any]" +obj1: object = INT_TF +obj2: object = OBJECT_TF +obj3: object = ANY_TF +any1: Any = INT_TF +any2: Any = OBJECT_TF +any3: Any = ANY_TF +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Join (join_types) + +[case testTypeFormToTypeFormJoin] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] join TypeForm[T2] == TypeForm[T1 join T2] +from typing_extensions import TypeForm +class AB: + pass +class A(AB): + pass +class B(AB): + pass +A_TF: TypeForm[A] = A +B_TF: TypeForm[B] = B +reveal_type([A_TF, B_TF][0]) # N: Revealed type is "TypeForm[__main__.AB]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeToTypeFormJoin] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] join Type[T2] == TypeForm[T1 join T2] +from typing import Type, TypeForm +class AB: + pass +class A(AB): + pass +class B(AB): + pass +A_T: Type[A] = A +B_TF: TypeForm[B] = B +reveal_type([A_T, B_TF][0]) # N: Revealed type is "TypeForm[__main__.AB]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeFormToTypeJoin] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] join Type[T2] == TypeForm[T1 join T2] +from typing import Type, TypeForm +class AB: + pass +class A(AB): + pass +class B(AB): + pass +A_TF: TypeForm[A] = A +B_T: Type[B] = B +reveal_type([A_TF, B_T][0]) # N: Revealed type is "TypeForm[__main__.AB]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +# NOTE: This test doesn't involve TypeForm at all, but is still illustrative +# when compared with similarly structured TypeForm-related tests above. +[case testTypeToTypeJoin] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - Type[T1] join Type[T2] == Type[T1 join T2] +from typing import Type, TypeForm +class AB: + pass +class A(AB): + pass +class B(AB): + pass +A_T: Type[A] = A +B_T: Type[B] = B +reveal_type([A_T, B_T][0]) # N: Revealed type is "type[__main__.AB]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Meet (meet_types) + +[case testTypeFormToTypeFormMeet] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] meet TypeForm[T2] == TypeForm[T1 meet T2] +from typing import Callable, TypeForm, TypeVar +class AB: + pass +class A(AB): + pass +class B(AB): + pass +class C(AB): + pass +T = TypeVar('T') +def f(x: Callable[[T, T], None]) -> T: pass # type: ignore[empty-body] +def g(x: TypeForm[A | B], y: TypeForm[B | C]) -> None: pass +reveal_type(f(g)) # N: Revealed type is "TypeForm[__main__.B]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeToTypeFormMeet] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] meet Type[T2] == Type[T1 meet T2] +from typing import Callable, Type, TypeForm, TypeVar +class AB: + pass +class A(AB): + pass +class B(AB): + pass +class C(AB): + pass +T = TypeVar('T') +def f(x: Callable[[T, T], None]) -> T: pass # type: ignore[empty-body] +def g(x: Type[B], y: TypeForm[B | C]) -> None: pass +reveal_type(f(g)) # N: Revealed type is "type[__main__.B]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeFormToTypeMeet] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - TypeForm[T1] meet Type[T2] == Type[T1 meet T2] +from typing import Callable, Type, TypeForm, TypeVar +class AB: + pass +class A(AB): + pass +class B(AB): + pass +class C(AB): + pass +T = TypeVar('T') +def f(x: Callable[[T, T], None]) -> T: pass # type: ignore[empty-body] +def g(x: TypeForm[A | B], y: Type[B]) -> None: pass +reveal_type(f(g)) # N: Revealed type is "type[__main__.B]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +# NOTE: This test doesn't involve TypeForm at all, but is still illustrative +# when compared with similarly structured TypeForm-related tests above. +[case testTypeToTypeMeet] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# - Type[T1] meet Type[T2] == Type[T1 meet T2] +from typing import Callable, Type, TypedDict, TypeForm, TypeVar +class AB(TypedDict): + a: str + b: str +class BC(TypedDict): + b: str + c: str +T = TypeVar('T') +def f(x: Callable[[T, T], None]) -> T: pass # type: ignore[empty-body] +def g(x: Type[AB], y: Type[BC]) -> None: pass +reveal_type(f(g)) # N: Revealed type is "type[TypedDict({'b': builtins.str, 'c': builtins.str, 'a': builtins.str})]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- TypeForm(...) Expression + +[case testTypeFormExpression] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +tf1 = TypeForm(int | str) +reveal_type(tf1) # N: Revealed type is "TypeForm[Union[builtins.int, builtins.str]]" +tf2 = TypeForm('int | str') +reveal_type(tf2) # N: Revealed type is "TypeForm[Union[builtins.int, builtins.str]]" +tf3: TypeForm = TypeForm(int | str) +reveal_type(tf3) # N: Revealed type is "TypeForm[Any]" +tf4: TypeForm = TypeForm(1) # E: Invalid type: try using Literal[1] instead? +tf5: TypeForm = TypeForm(int) | TypeForm(str) # E: Incompatible types in assignment (expression has type "object", variable has type "TypeForm[Any]") +tf6: TypeForm = TypeForm(TypeForm(int) | TypeForm(str)) # E: TypeForm argument is not a type +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- isinstance + +[case testTypeFormAndTypeIsinstance] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[str] = str +if isinstance(typx, type): + reveal_type(typx) # N: Revealed type is "type[builtins.str]" +else: + reveal_type(typx) # N: Revealed type is "TypeForm[builtins.str]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Variables + +[case testLinkTypeFormToTypeFormWithTypeVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm, TypeVar +T = TypeVar('T') +def as_typeform(typx: TypeForm[T]) -> TypeForm[T]: + return typx +reveal_type(as_typeform(int | str)) # N: Revealed type is "TypeForm[Union[builtins.int, builtins.str]]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testLinkTypeFormToTypeWithTypeVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Type, TypeForm, TypeVar +T = TypeVar('T') +def as_type(typx: TypeForm[T]) -> Type[T] | None: + if isinstance(typx, type): + return typx + else: + return None +reveal_type(as_type(int | str)) # N: Revealed type is "Union[type[builtins.int], type[builtins.str], None]" +reveal_type(as_type(int)) # N: Revealed type is "Union[type[builtins.int], None]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testLinkTypeFormToInstanceWithTypeVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm, TypeVar +T = TypeVar('T') +def as_instance(typx: TypeForm[T]) -> T | None: + if isinstance(typx, type): + return typx() + else: + return None +reveal_type(as_instance(int | str)) # N: Revealed type is "Union[builtins.int, builtins.str, None]" +reveal_type(as_instance(int)) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testLinkTypeFormToTypeIsWithTypeVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm, TypeVar +from typing_extensions import TypeIs +T = TypeVar('T') +def isassignable(value: object, typx: TypeForm[T]) -> TypeIs[T]: + raise BaseException() +count: int | str = 1 +if isassignable(count, int): + reveal_type(count) # N: Revealed type is "builtins.int" +else: + reveal_type(count) # N: Revealed type is "builtins.str" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testLinkTypeFormToTypeGuardWithTypeVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm, TypeVar +from typing_extensions import TypeGuard +T = TypeVar('T') +def isassignable(value: object, typx: TypeForm[T]) -> TypeGuard[T]: + raise BaseException() +count: int | str = 1 +if isassignable(count, int): + reveal_type(count) # N: Revealed type is "builtins.int" +else: + reveal_type(count) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Type Expressions Assignable To TypeForm Variable + +[case testEveryKindOfTypeExpressionIsAssignableToATypeFormVariable] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +# NOTE: Importing Callable from collections.abc also works OK +from typing import ( + Any, Callable, Dict, List, Literal, LiteralString, NoReturn, + Optional, ParamSpec, Self, Type, TypeGuard, TypeVar, Union, +) +from typing_extensions import ( + Annotated, Concatenate, Never, TypeAlias, TypeForm, TypeIs, + TypeVarTuple, Unpack, +) +# +class SomeClass: + pass +SomeTypeAlias: TypeAlias = SomeClass +SomeTypeVar = TypeVar('SomeTypeVar') +Ts = TypeVarTuple('Ts') +IntTuple: TypeAlias = tuple[int, Unpack[Ts]] +P = ParamSpec('P') +R = TypeVar('R') +# +typx: TypeForm +# Begin rules taken from: https://typing.python.org/en/latest/spec/annotations.html#grammar-token-expression-grammar-type_expression +# +typx = Any +class SelfBinder: + def bind_self(self) -> Self: + typx: TypeForm + # (valid only in some contexts) + typx = Self + return self +# +typx = LiteralString +# +typx = NoReturn +# +typx = Never +# +typx = None +# name (where name must refer to a valid in-scope class) +typx = SomeClass +# name (where name must refer to a valid in-scope type alias) +typx = SomeTypeAlias +# name (where name must refer to a valid in-scope TypeVar) +# NOTE: Unbound TypeVar isn't currently accepted as a TypeForm. Is that OK? +typx = SomeTypeVar # E: Incompatible types in assignment (expression has type "TypeVar", variable has type "TypeForm[Any]") +# name '[' type_expression (',' type_expression)* ']' +typx = Dict[str, int] +# (TODO: Add: name '[' unpacked ']') +# (TODO: Add: name '[' type_expression_list (',' type_expression_list)* ']') +# name '[' '(' ')' ']' (denoting specialization with an empty TypeVarTuple) +typx = IntTuple[()] +# '[' expression (',' expression) ']' +typx = Literal[1] +# type_expression '|' type_expression +typx = int | str +# '[' type_expression ']' +typx = Optional[str] +# '[' type_expression (',' type_expression)* ']' +typx = Union[int, str] +# '[' ']' +typx = type[Any] +# '[' name ']' (where name must refer to a valid in-scope class) +typx = type[int] +# (TODO: Add: '[' name ']' (where name must refer to a valid in-scope TypeVar)) +# '[' '...' ',' type_expression ']' +typx = Callable[..., str] +def bind_R(input: R) -> R: + typx: TypeForm + # '[' name ',' type_expression ']' (where name must be a valid in-scope ParamSpec) + typx = Callable[P, R] + # '[' '[' (type_expression ',')+ + # (name | '...') ']' ',' type_expression ']' + # (where name must be a valid in-scope ParamSpec) + typx = Callable[Concatenate[int, P], R] + return input +# '[' '[' maybe_unpacked (',' maybe_unpacked)* ']' ',' type_expression ']' +typx = Callable[[int, str], None] +# '[' '(' ')' ']' (representing an empty tuple) +typx = tuple[()] +# '[' type_expression ',' '...' ']' (representing an arbitrary-length tuple) +typx = tuple[int, ...] +# '[' maybe_unpacked (',' maybe_unpacked)* ']' +typx = tuple[int, str] +# '[' type_expression ',' expression (',' expression)* ']' +typx = Annotated[str, 'uppercase'] +# '[' type_expression ']' (valid only in some contexts) +typx = TypeGuard[List[str]] +# '[' type_expression ']' (valid only in some contexts) +typx = TypeIs[List[str]] +# string_annotation (must evaluate to a valid type_expression) +typx = 'int | str' +# End rules +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + + +-- Misc + +[case testTypeFormHasAllObjectAttributesAndMethods] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +typx: TypeForm[int | str] = int | str +print(typx.__class__) # OK +print(typx.__hash__()) # OK +obj: object = typx +[file builtins.py] +class object: + def __init__(self) -> None: pass + __class__: None + def __hash__(self) -> int: pass +def print(x): + raise BaseException() +class int: pass +class dict: pass +class str: pass +class type: pass +class tuple: pass +class ellipsis: pass +class BaseException: pass +class float: pass +[typing fixtures/typing-full.pyi] + +[case testDottedTypeFormsAreRecognized] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing_extensions import TypeForm +import typing +class C1: + class C2: + pass +typx1: TypeForm[C1.C2] = C1.C2 # OK +typx2: TypeForm[typing.Any] = typing.Any # OK +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +-- mypy already refused to recognize TypeVars in value expressions before +-- the TypeForm feature was introduced. +[case testTypeVarTypeFormsAreOnlyRecognizedInStringAnnotation] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Generic, List, TypeForm, TypeVar +E = TypeVar('E') +class Box(Generic[E]): + def foo(self, e: E) -> None: + list_of_typx: List[TypeForm] = [E] # E: "E" is a type variable and only valid in type context + typx1: TypeForm = E # E: "E" is a type variable and only valid in type context + typx2: TypeForm = 'E' +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + +[case testIncompleteTypeFormsAreNotRecognized] +# flags: --python-version 3.14 --enable-incomplete-feature=TypeForm +from typing import Optional, TypeForm +typx: TypeForm = Optional # E: Incompatible types in assignment (expression has type "int", variable has type "TypeForm[Any]") +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index 27b88553fb435..b15458d5819a4 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -84,7 +84,16 @@ T = TypeVar('T') def is_two_element_tuple(a: Tuple[T, ...]) -> TypeGuard[Tuple[T, T]]: pass def main(a: Tuple[T, ...]): if is_two_element_tuple(a): - reveal_type(a) # N: Revealed type is "Tuple[T`-1, T`-1]" + 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] @@ -249,7 +258,7 @@ def main1(a: object) -> None: ta = (a,) if is_float(*ta): # E: Type guard requires positional argument - reveal_type(ta) # N: Revealed type is "Tuple[builtins.object]" + reveal_type(ta) # N: Revealed type is "tuple[builtins.object]" reveal_type(a) # N: Revealed type is "builtins.object" la = [a] @@ -443,7 +452,7 @@ def g(x: object) -> None: ... def test(x: List[object]) -> None: if not(f(x) or isinstance(x, A)): return - g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" + g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" [builtins fixtures/tuple.pyi] [case testTypeGuardMultipleCondition-xfail] @@ -606,7 +615,7 @@ def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeGuard[Tuple[_T, _T]]: def func(names: Tuple[str, ...]): reveal_type(names) # N: Revealed type is "builtins.tuple[builtins.str, ...]" if is_two_element_tuple(names): - reveal_type(names) # N: Revealed type is "Tuple[builtins.str, builtins.str]" + reveal_type(names) # N: Revealed type is "tuple[builtins.str, builtins.str]" [builtins fixtures/tuple.pyi] [case testTypeGuardErroneousDefinitionFails] @@ -721,3 +730,170 @@ x: object assert a(x=x) reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +# https://github.com/python/mypy/issues/19575 +[case testNoCrashOnDunderCallTypeGuardTemporaryObject] +from typing_extensions import TypeGuard +class E: + def __init__(self) -> None: ... + def __call__(self, o: object) -> TypeGuard[int]: + return True +x = object() +if E()(x): + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testNoCrashOnDunderCallTypeIsTemporaryObject] +from typing_extensions import TypeIs +class E: + def __init__(self) -> None: ... + def __call__(self, o: object) -> TypeIs[int]: + return True +x = object() +if E()(x): + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testNoCrashOnDunderCallTypeIsTemporaryObjectGeneric] +from typing import Generic, TypeVar +from typing_extensions import TypeIs +T = TypeVar("T") +class E(Generic[T]): + def __init__(self) -> None: ... + def __call__(self, o: object) -> TypeIs[T]: + return True +x = object() +if E[int]()(x): + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeGuardTemporaryObjectWithKeywordArg] +from typing_extensions import TypeGuard +class E: + def __init__(self) -> None: ... + def __call__(self, o: object) -> TypeGuard[int]: + return True +x = object() +if E()(o=x): + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testTypeGuardRestrictAwaySingleInvariant] +from typing import List +from typing_extensions import TypeGuard + +class B: ... +class C(B): ... + +def is_c_list(x: list[B]) -> TypeGuard[list[C]]: ... + +def test() -> None: + x: List[B] + if not is_c_list(x): + reveal_type(x) # N: Revealed type is "builtins.list[__main__.B]" + return + reveal_type(x) # N: Revealed type is "builtins.list[__main__.C]" +[builtins fixtures/tuple.pyi] + +[case testTypeGuardedTypeDoesNotLeak] +# https://github.com/python/mypy/issues/18895 +from enum import Enum +from typing import Literal, Union +from typing_extensions import TypeGuard + +class Model(str, Enum): + A1 = 'model_a1' + A2 = 'model_a2' + B = 'model_b' + +MODEL_A = Literal[Model.A1, Model.A2] +MODEL_B = Literal[Model.B] + +def is_model_a(model: str) -> TypeGuard[MODEL_A]: + return True + +def is_model_b(model: str) -> TypeGuard[MODEL_B]: + return True + +def process_model(model: Union[MODEL_A, MODEL_B]) -> int: + return 42 + +def handle(model: Model) -> int: + if is_model_a(model) or is_model_b(model): + reveal_type(model) # N: Revealed type is "__main__.Model" + return process_model(model) + return 0 +[builtins fixtures/tuple.pyi] + +[case testTypeGuardedTypeDoesNotLeakTypeVar] +# flags: --debug-serialize +# https://github.com/python/mypy/issues/20015 +from typing import Generic, TypeVar, TypeGuard + +class A: ... +class B: ... + +def is_a(_: object) -> TypeGuard[A]: return True +def is_b(_: object) -> TypeGuard[B]: return True + +_T = TypeVar("_T") + +class Foo(Generic[_T]): + def __init__(self, v: _T) -> None: + if is_a(v) or is_b(v): + self.v = v +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testTypeGuardRestrictTypeVarUnion] +from typing import Union, TypeVar +from typing_extensions import TypeGuard + +class A: + x: int +class B: + x: str + +def is_b(x: object) -> TypeGuard[B]: ... + +T = TypeVar("T") +def test(x: T) -> T: + if isinstance(x, A) or is_b(x): + reveal_type(x.x) # N: Revealed type is "Union[builtins.int, builtins.str]" + return x +[builtins fixtures/isinstance.pyi] + +[case testOverloadedTypeGuardType] +from __future__ import annotations +from typing_extensions import TypeIs, Never, overload + +class X: ... + +@overload # E: An overloaded function outside a stub file must have an implementation +def is_xlike(obj: Never) -> TypeIs[X | type[X]]: ... # type: ignore +@overload +def is_xlike(obj: type) -> TypeIs[type[X]]: ... +@overload +def is_xlike(obj: object) -> TypeIs[X | type[X]]: ... + +raw_target: object +if isinstance(raw_target, type) and is_xlike(raw_target): + reveal_type(raw_target) # N: Revealed type is "type[__main__.X]" +[builtins fixtures/tuple.pyi] + +[case testTypeGuardWithDefer] +from typing import Union +from typing_extensions import TypeGuard + +class A: ... +class B: ... + +def is_a(x: object) -> TypeGuard[A]: + return defer_not_defined() # E: Name "defer_not_defined" is not defined + +def main(x: Union[A, B]) -> None: + if is_a(x): + reveal_type(x) # N: Revealed type is "__main__.A" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.A, __main__.B]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 6b96845504abf..2f54ac5bf5dbf 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -104,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 @@ -125,6 +134,91 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] +[case testTypeIsUnionWithGeneric] +from typing import Any, List, Sequence, Union +from typing_extensions import TypeIs + +def is_int_list(a: object) -> TypeIs[List[int]]: pass +def is_int_seq(a: object) -> TypeIs[Sequence[int]]: pass +def is_seq(a: object) -> TypeIs[Sequence[Any]]: pass + +def f1(a: Union[List[int], List[str]]) -> None: + if is_int_list(a): + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + else: + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.list[builtins.str]]" + +def f2(a: Union[List[int], int]) -> None: + if is_int_list(a): + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + else: + reveal_type(a) # N: Revealed type is "builtins.int" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.int]" + +def f3(a: Union[List[bool], List[str]]) -> None: + if is_int_seq(a): + reveal_type(a) # N: Revealed type is "builtins.list[builtins.bool]" + else: + reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.bool], builtins.list[builtins.str]]" + +def f4(a: Union[List[int], int]) -> None: + if is_seq(a): + reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" + else: + reveal_type(a) # N: Revealed type is "builtins.int" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsTupleGeneric] +# flags: --warn-unreachable +from __future__ import annotations +from typing_extensions import TypeIs, Unpack + +class A: ... +class B: ... + +def is_tuple_of_B(v: tuple[A | B, ...]) -> TypeIs[tuple[B, ...]]: ... + +def test1(t: tuple[A]) -> None: + if is_tuple_of_B(t): + reveal_type(t) # E: Statement is unreachable + else: + reveal_type(t) # N: Revealed type is "tuple[__main__.A]" + +def test2(t: tuple[B, A]) -> None: + if is_tuple_of_B(t): + reveal_type(t) # E: Statement is unreachable + else: + reveal_type(t) # N: Revealed type is "tuple[__main__.B, __main__.A]" + +def test3(t: tuple[A | B]) -> None: + if is_tuple_of_B(t): + reveal_type(t) # N: Revealed type is "tuple[__main__.B]" + else: + reveal_type(t) # N: Revealed type is "tuple[Union[__main__.A, __main__.B]]" + +def test4(t: tuple[A | B, A | B]) -> None: + if is_tuple_of_B(t): + reveal_type(t) # N: Revealed type is "tuple[__main__.B, __main__.B]" + else: + reveal_type(t) # N: Revealed type is "tuple[Union[__main__.A, __main__.B], Union[__main__.A, __main__.B]]" + +def test5(t: tuple[A | B, ...]) -> None: + if is_tuple_of_B(t): + reveal_type(t) # N: Revealed type is "builtins.tuple[__main__.B, ...]" + else: + reveal_type(t) # N: Revealed type is "builtins.tuple[Union[__main__.A, __main__.B], ...]" + +def test6(t: tuple[B, Unpack[tuple[A | B, ...]], B]) -> None: + if is_tuple_of_B(t): + # Should this be tuple[B, *tuple[B, ...], B] + reveal_type(t) # N: Revealed type is "tuple[__main__.B, Never, __main__.B]" + else: + reveal_type(t) # N: Revealed type is "tuple[__main__.B, Unpack[builtins.tuple[Union[__main__.A, __main__.B], ...]], __main__.B]" +[builtins fixtures/tuple.pyi] + [case testTypeIsNonzeroFloat] from typing_extensions import TypeIs def is_nonzero(a: object) -> TypeIs[float]: pass @@ -375,9 +469,9 @@ def guard(a: object) -> TypeIs[B]: a = A() if guard(a): - reveal_type(a) # N: Revealed type is "__main__." + reveal_type(a) # N: Revealed type is "__main__." a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - reveal_type(a) # N: Revealed type is "__main__." + reveal_type(a) # N: Revealed type is "__main__." a = A() reveal_type(a) # N: Revealed type is "__main__.A" reveal_type(a) # N: Revealed type is "__main__.A" @@ -445,7 +539,7 @@ def g(x: object) -> None: ... def test(x: List[Any]) -> None: if not(f(x) or isinstance(x, A)): return - g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" + g(reveal_type(x)) # N: Revealed type is "Union[builtins.list[builtins.str], __main__.]" [builtins fixtures/tuple.pyi] [case testTypeIsMultipleCondition] @@ -464,13 +558,13 @@ def is_bar(item: object) -> TypeIs[Bar]: def foobar(x: object): if not isinstance(x, Foo) or not isinstance(x, Bar): return - reveal_type(x) # N: Revealed type is "__main__." + reveal_type(x) # N: Revealed type is "__main__." def foobar_typeis(x: object): if not is_foo(x) or not is_bar(x): return # Looks like a typo but this is what our unique name generation produces - reveal_type(x) # N: Revealed type is "__main__.1" + reveal_type(x) # N: Revealed type is "__main__." [builtins fixtures/tuple.pyi] [case testTypeIsAsFunctionArgAsBoolSubtype] @@ -631,7 +725,7 @@ def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeIs[Tuple[_T, _T]]: def func(names: Tuple[str, ...]): reveal_type(names) # N: Revealed type is "builtins.tuple[builtins.str, ...]" if is_two_element_tuple(names): - reveal_type(names) # N: Revealed type is "Tuple[builtins.str, builtins.str]" + reveal_type(names) # N: Revealed type is "tuple[builtins.str, builtins.str]" [builtins fixtures/tuple.pyi] [case testTypeIsErroneousDefinitionFails] @@ -752,7 +846,7 @@ def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of inpu T = TypeVar('T') -def g(x: List[T]) -> TypeIs[Sequence[T]]: # E: Narrowed type "Sequence[T]" is not a subtype of input type "List[T]" +def g(x: List[T]) -> TypeIs[Sequence[T]]: # E: Narrowed type "Sequence[T]" is not a subtype of input type "list[T]" pass [builtins fixtures/tuple.pyi] @@ -808,3 +902,51 @@ accept_typeguard(typeis) # E: Argument 1 to "accept_typeguard" has incompatible accept_typeguard(typeguard) [builtins fixtures/tuple.pyi] + +[case testTypeIsEnumOverlappingUnionExcludesIrrelevant] +from enum import Enum +from typing import Literal +from typing_extensions import TypeIs + +class Model(str, Enum): + A = 'a' + B = 'a' + +def is_model_a(model: str) -> TypeIs[Literal[Model.A, "foo"]]: + return True + +def handle(model: Model) -> None: + if is_model_a(model): + reveal_type(model) # N: Revealed type is "Literal[__main__.Model.A]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsAwaitableAny] +from __future__ import annotations +from typing import Any, Awaitable, Callable +from typing_extensions import TypeIs + +def is_async_callable(obj: Any) -> TypeIs[Callable[..., Awaitable[Any]]]: ... + +def main(f: Callable[[], int | Awaitable[int]]) -> None: + if is_async_callable(f): + reveal_type(f) # N: Revealed type is "def (*Any, **Any) -> typing.Awaitable[Any]" + else: + reveal_type(f) # N: Revealed type is "def () -> Union[builtins.int, typing.Awaitable[builtins.int]]" +[builtins fixtures/tuple.pyi] + +[case testTypeIsWithDefer] +from typing import Union +from typing_extensions import TypeIs + +class A: ... +class B: ... + +def is_a(x: object) -> TypeIs[A]: + return defer_not_defined() # E: Name "defer_not_defined" is not defined + +def main(x: Union[A, B]) -> None: + if is_a(x): + reveal_type(x) # N: Revealed type is "__main__.A" + else: + reveal_type(x) # N: Revealed type is "__main__.B" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 9ca67376da26a..103c0e782797d 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,11 +9,11 @@ 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]]: ... -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]]]]" +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(Generic[T1]): ... @@ -23,7 +22,7 @@ class ClassA3(Generic[Unpack[Ts1]]): ... 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]]]]" +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 testTypeVarDefaultsValid] @@ -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] @@ -417,6 +416,38 @@ def func_c4( reveal_type(m) # N: Revealed type is "__main__.ClassC4[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] +[case testTypeVarDefaultsSwap] +from typing import TypeVar, Generic + +T = TypeVar("T") +X = TypeVar("X", default=object) +Y = TypeVar("Y", default=object) + + +class Foo(Generic[T, Y]): + def test(self) -> None: + reveal_type( Foo[Y, T]() ) # N: Revealed type is "__main__.Foo[Y`2 = builtins.object, T`1]" + + +class Bar(Generic[X, Y]): + def test(self) -> None: + reveal_type( Bar[Y, X]() ) # N: Revealed type is "__main__.Bar[Y`2 = builtins.object, X`1 = builtins.object]" + + +[case testTypeVarDefaultsSwap2] +from typing import TypeVar, Generic + +X = TypeVar("X", default=object) +Y = TypeVar("Y", default=object) +U = TypeVar("U", default=object) +V = TypeVar("V", default=object) + +class Transform(Generic[X, Y]): + def invert(self) -> "Transform[Y, X]": ... + +class Foo(Transform[U, V], Generic[U, V]): + def invert(self) -> "Foo[V, U]": ... + [case testTypeVarDefaultsClassRecursive1] # flags: --disallow-any-generics from typing import Generic, TypeVar, List @@ -545,11 +576,11 @@ def func_a2( d: TA2[float, float, float], e: TA2[float, float, float, float], # E: Bad number of arguments for type alias, expected between 1 and 3, given 4 ) -> None: - reveal_type(a) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]" - reveal_type(b) # N: Revealed type is "Tuple[builtins.float, builtins.int, builtins.str]" - reveal_type(c) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.str]" - reveal_type(d) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]" - reveal_type(e) # N: Revealed type is "Tuple[Any, builtins.int, builtins.str]" + reveal_type(a) # N: Revealed type is "tuple[Any, builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "tuple[builtins.float, builtins.int, builtins.str]" + reveal_type(c) # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.str]" + reveal_type(d) # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.float]" + reveal_type(e) # N: Revealed type is "tuple[Any, builtins.int, builtins.str]" TA3 = Union[Dict[T1, T2], List[T3]] @@ -575,11 +606,11 @@ def func_a4( d: TA4[float, float, float], e: TA4[float, float, float, float], # E: Bad number of arguments for type alias, expected between 2 and 3, given 4 ) -> None: - reveal_type(a) # N: Revealed type is "Tuple[Any, Any, builtins.int]" - reveal_type(b) # N: Revealed type is "Tuple[Any, Any, builtins.int]" - reveal_type(c) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.int]" - reveal_type(d) # N: Revealed type is "Tuple[builtins.float, builtins.float, builtins.float]" - reveal_type(e) # N: Revealed type is "Tuple[Any, Any, builtins.int]" + reveal_type(a) # N: Revealed type is "tuple[Any, Any, builtins.int]" + reveal_type(b) # N: Revealed type is "tuple[Any, Any, builtins.int]" + reveal_type(c) # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.int]" + reveal_type(d) # N: Revealed type is "tuple[builtins.float, builtins.float, builtins.float]" + reveal_type(e) # N: Revealed type is "tuple[Any, Any, builtins.int]" [builtins fixtures/dict.pyi] [case testTypeVarDefaultsTypeAlias2] @@ -639,7 +670,7 @@ def func_c1( 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]" + reveal_type(b) # N: Revealed type is "tuple[builtins.float]" TC2 = Tuple[T3, Unpack[Ts3]] @@ -650,7 +681,7 @@ def func_c2( ) -> None: # reveal_type(a) # Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO # reveal_type(b) # Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]]]" # TODO - reveal_type(c) # N: Revealed type is "Tuple[builtins.int]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int]" TC3 = Tuple[T3, Unpack[Ts4]] @@ -660,8 +691,8 @@ def func_c3( c: TC3[int, Unpack[Tuple[float]]], ) -> None: # reveal_type(a) # Revealed type is "Tuple[builtins.str]" # TODO - reveal_type(b) # N: Revealed type is "Tuple[builtins.int]" - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]" + reveal_type(b) # N: Revealed type is "tuple[builtins.int]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, builtins.float]" TC4 = Tuple[T1, Unpack[Ts1], T3] @@ -670,9 +701,9 @@ def func_c4( b: TC4[int], c: TC4[int, float], ) -> None: - reveal_type(a) # N: Revealed type is "Tuple[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]" + reveal_type(a) # N: Revealed type is "tuple[Any, Unpack[builtins.tuple[Any, ...]], builtins.str]" # reveal_type(b) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO - reveal_type(c) # N: Revealed type is "Tuple[builtins.int, builtins.float]" + reveal_type(c) # N: Revealed type is "tuple[builtins.int, builtins.float]" [builtins fixtures/tuple.pyi] [case testTypeVarDefaultsTypeAliasRecursive1] @@ -717,3 +748,135 @@ 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] + +[case testTypeVarConstraintsDefaultAliasesTypeAliasType] +from typing import Generic +from typing_extensions import TypeAliasType, TypeVar + +K = TypeAliasType("K", int) +V = TypeAliasType("V", int) +L = TypeAliasType("L", list[int]) +T1 = TypeVar("T1", str, K, default=K) +T2 = TypeVar("T2", str, K, default=V) +T3 = TypeVar("T3", str, L, default=L) + +class A1(Generic[T1]): + x: T1 +class A2(Generic[T2]): + x: T2 +class A3(Generic[T3]): + x: T3 + +reveal_type(A1().x) # N: Revealed type is "builtins.int" +reveal_type(A2().x) # N: Revealed type is "builtins.int" +reveal_type(A3().x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarConstraintsDefaultAliasesImplicitAlias] +from typing_extensions import TypeVar + +K = int +V = int +L = list[int] +T1 = TypeVar("T1", str, K, default=K) +T2 = TypeVar("T2", str, K, default=V) +T3 = TypeVar("T3", str, L, default=L) +[builtins fixtures/tuple.pyi] + +[case testTypeVarConstraintsDefaultAliasesExplicitAlias] +from typing_extensions import TypeAlias, TypeVar + +K: TypeAlias = int +V: TypeAlias = int +L: TypeAlias = list[int] +T1 = TypeVar("T1", str, K, default=K) +T2 = TypeVar("T2", str, K, default=V) +T3 = TypeVar("T3", str, L, default=L) +[builtins fixtures/tuple.pyi] + +[case testTypeVarConstraintsDefaultSpecialTypes] +from typing import Generic, NamedTuple +from typing_extensions import TypedDict, TypeVar + +class TD(TypedDict): + foo: str + +class NT(NamedTuple): + foo: str + +T1 = TypeVar("T1", str, TD, default=TD) +T2 = TypeVar("T2", str, NT, default=NT) + +class A1(Generic[T1]): + x: T1 +class A2(Generic[T2]): + x: T2 + +reveal_type(A1().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.str})" +reveal_type(A2().x) # N: Revealed type is "tuple[builtins.str, fallback=__main__.NT]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarConstraintsDefaultSpecialTypesGeneric] +from typing import Generic, NamedTuple +from typing_extensions import TypedDict, TypeVar + +T = TypeVar("T") + +class TD(TypedDict, Generic[T]): + foo: T +class TD2(TD[int]): pass +class TD3(TD[int]): + bar: str + +class NT(NamedTuple, Generic[T]): + foo: T +class NT2(NT[int]): pass + +T1 = TypeVar("T1", str, TD[int], default=TD[int]) +T2 = TypeVar("T2", str, NT[int], default=NT[int]) +T3 = TypeVar("T3", str, TD2, default=TD[int]) +T4 = TypeVar("T4", str, TD3, default=TD[int]) # E: TypeVar default must be one of the constraint types +T5 = TypeVar("T5", str, NT2, default=NT[int]) # E: TypeVar default must be one of the constraint types + +class A1(Generic[T1]): + x: T1 +class A2(Generic[T2]): + x: T2 +class A3(Generic[T3]): + x: T3 + +reveal_type(A1().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})" +reveal_type(A2().x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.NT[builtins.int]]" +reveal_type(A3().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index f49e1b3c66132..c60d0aec0835a 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -13,17 +13,17 @@ args2: Tuple[bool, str] = (False, 'y') args3: Tuple[int, str, bool] = (2, 'z', True) varargs: Tuple[int, ...] = (1, 2, 3) -reveal_type(f(args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(f(args)) # N: Revealed type is "tuple[builtins.int, builtins.str]" reveal_type(f(varargs)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[Never, ...]" +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected "tuple[Never, ...]" def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: return a -reveal_type(g(args, args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" -reveal_type(g(args, args2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(g(args, args)) # N: Revealed type is "tuple[builtins.int, builtins.str]" +reveal_type(g(args, args2)) # N: Revealed type is "tuple[builtins.int, builtins.str]" reveal_type(g(args, args3)) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]" reveal_type(g(any, any)) # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/tuple.pyi] @@ -54,21 +54,21 @@ f_args: Tuple[int, str] f_args2: Tuple[int] f_args3: Tuple[int, str, bool] -reveal_type(f(f_args)) # N: Revealed type is "Tuple[builtins.str, builtins.str]" -reveal_type(f(f_args2)) # N: Revealed type is "Tuple[builtins.str]" -reveal_type(f(f_args3)) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.bool]" -f(empty) # E: Argument 1 to "f" has incompatible type "Tuple[()]"; expected "Tuple[int]" -f(bad_args) # E: Argument 1 to "f" has incompatible type "Tuple[str, str]"; expected "Tuple[int, str]" +reveal_type(f(f_args)) # N: Revealed type is "tuple[builtins.str, builtins.str]" +reveal_type(f(f_args2)) # N: Revealed type is "tuple[builtins.str]" +reveal_type(f(f_args3)) # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.bool]" +f(empty) # E: Argument 1 to "f" has incompatible type "tuple[()]"; expected "tuple[int]" +f(bad_args) # E: Argument 1 to "f" has incompatible type "tuple[str, str]"; expected "tuple[int, str]" # The reason for error in subtle: actual can be empty, formal cannot. -reveal_type(f(var_len_tuple)) # N: Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]]]" \ - # E: Argument 1 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]]]" +reveal_type(f(var_len_tuple)) # N: Revealed type is "tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]]]" \ + # E: Argument 1 to "f" has incompatible type "tuple[int, ...]"; expected "tuple[int, Unpack[tuple[int, ...]]]" g_args: Tuple[str, int] -reveal_type(g(g_args)) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +reveal_type(g(g_args)) # N: Revealed type is "tuple[builtins.str, builtins.str]" h_args: Tuple[bool, int, str, int, str, object] -reveal_type(h(h_args)) # N: Revealed type is "Tuple[builtins.str, builtins.int]" +reveal_type(h(h_args)) # N: Revealed type is "tuple[builtins.str, builtins.int]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleChaining] @@ -91,8 +91,8 @@ def h(a: Tuple[bool, int, Unpack[Ts], str, object]) -> Tuple[str, Unpack[Ts]]: return x args: Tuple[bool, int, str, int, str, object] -reveal_type(g(args)) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.int]" -reveal_type(h(args)) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.int]" +reveal_type(g(args)) # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.int]" +reveal_type(h(args)) # N: Revealed type is "tuple[builtins.str, builtins.str, builtins.int]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleGenericClassDefn] @@ -123,7 +123,7 @@ reveal_type(empty) # N: Revealed type is "__main__.Variadic[()]" omitted: Variadic reveal_type(omitted) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[Any, ...]]]" -bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]] # E: More than one Unpack in a type is not allowed +bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]] # E: More than one variadic Unpack in a type is not allowed reveal_type(bad) # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[builtins.int, ...]], builtins.str]" bad2: Unpack[Tuple[int, ...]] # E: Unpack is only valid in a variadic position @@ -147,7 +147,7 @@ def foo(t: Variadic[int, Unpack[Ts], object]) -> Tuple[int, Unpack[Ts]]: ... v: Variadic[int, str, bool, object] -reveal_type(foo(v)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" +reveal_type(foo(v)) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleGenericClassWithMethods] @@ -166,7 +166,7 @@ class Variadic(Generic[T, Unpack[Ts], S]): ... v: Variadic[float, str, bool, object] -reveal_type(v.foo(0)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" +reveal_type(v.foo(0)) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleIsNotValidAliasTarget] @@ -306,7 +306,7 @@ def prefix_tuple( ... z = prefix_tuple(x=0, y=(True, 'a')) -reveal_type(z) # N: Revealed type is "Tuple[builtins.int, builtins.bool, builtins.str]" +reveal_type(z) # N: Revealed type is "tuple[builtins.int, builtins.bool, builtins.str]" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarTupleUnpacking] @@ -333,7 +333,7 @@ process_batch_channels(x) y: Array[Batch, Channels] process_batch_channels(y) z: Array[Batch] -process_batch_channels(z) # E: Argument 1 to "process_batch_channels" has incompatible type "Array[Batch]"; expected "Array[Batch, Unpack[Tuple[Any, ...]], Channels]" +process_batch_channels(z) # E: Argument 1 to "process_batch_channels" has incompatible type "Array[Batch]"; expected "Array[Batch, Unpack[tuple[Any, ...]], Channels]" u: Array[Unpack[Tuple[Any, ...]]] @@ -353,14 +353,14 @@ expect_variadic_array_2(u) Ts = TypeVarTuple("Ts") Ts2 = TypeVarTuple("Ts2") -def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one Unpack in a type is not allowed +def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one variadic Unpack in a type is not allowed ... -reveal_type(bad) # N: Revealed type is "def [Ts, Ts2] (x: Tuple[builtins.int, Unpack[Ts`-1], builtins.str])" +reveal_type(bad) # N: Revealed type is "def [Ts, Ts2] (x: tuple[builtins.int, Unpack[Ts`-1], builtins.str])" -def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None: # E: More than one Unpack in a type is not allowed +def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None: # E: More than one variadic Unpack in a type is not allowed ... -reveal_type(bad2) # N: Revealed type is "def (x: Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])" +reveal_type(bad2) # N: Revealed type is "def (x: tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgsBasic] @@ -370,23 +370,23 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: - reveal_type(args) # N: Revealed type is "Tuple[Unpack[Ts`-1]]" - reveal_type(args_to_tuple(1, *args)) # N: Revealed type is "Tuple[Literal[1]?, Unpack[Ts`-1]]" - reveal_type(args_to_tuple(*args, 'a')) # N: Revealed type is "Tuple[Unpack[Ts`-1], Literal['a']?]" - reveal_type(args_to_tuple(1, *args, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Unpack[Ts`-1], Literal['a']?]" + reveal_type(args) # N: Revealed type is "tuple[Unpack[Ts`-1]]" + reveal_type(args_to_tuple(1, *args)) # N: Revealed type is "tuple[Literal[1]?, Unpack[Ts`-1]]" + reveal_type(args_to_tuple(*args, 'a')) # N: Revealed type is "tuple[Unpack[Ts`-1], Literal['a']?]" + reveal_type(args_to_tuple(1, *args, 'a')) # N: Revealed type is "tuple[Literal[1]?, Unpack[Ts`-1], Literal['a']?]" args_to_tuple(*args, *args) # E: Passing multiple variadic unpacks in a call is not supported ok = (1, 'a') - reveal_type(args_to_tuple(*ok, *ok)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.int, builtins.str]" + reveal_type(args_to_tuple(*ok, *ok)) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.int, builtins.str]" if int(): return args else: return args_to_tuple(*args) -reveal_type(args_to_tuple(1, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Literal['a']?]" +reveal_type(args_to_tuple(1, 'a')) # N: Revealed type is "tuple[Literal[1]?, Literal['a']?]" vt: Tuple[int, ...] -reveal_type(args_to_tuple(1, *vt)) # N: Revealed type is "Tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]]]" -reveal_type(args_to_tuple(*vt, 'a')) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]" -reveal_type(args_to_tuple(1, *vt, 'a')) # N: Revealed type is "Tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]" +reveal_type(args_to_tuple(1, *vt)) # N: Revealed type is "tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]]]" +reveal_type(args_to_tuple(*vt, 'a')) # N: Revealed type is "tuple[Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]" +reveal_type(args_to_tuple(1, *vt, 'a')) # N: Revealed type is "tuple[Literal[1]?, Unpack[builtins.tuple[builtins.int, ...]], Literal['a']?]" args_to_tuple(*vt, *vt) # E: Passing multiple variadic unpacks in a call is not supported [builtins fixtures/tuple.pyi] @@ -398,34 +398,34 @@ Ts = TypeVarTuple("Ts") def args_to_tuple(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: with_prefix_suffix(*args) # E: Too few arguments for "with_prefix_suffix" \ - # E: Argument 1 to "with_prefix_suffix" has incompatible type "*Tuple[Unpack[Ts]]"; expected "bool" + # E: Argument 1 to "with_prefix_suffix" has incompatible type "*tuple[Unpack[Ts]]"; expected "bool" new_args = (True, "foo", *args, 5) with_prefix_suffix(*new_args) return args def with_prefix_suffix(*args: Unpack[Tuple[bool, str, Unpack[Ts], int]]) -> Tuple[bool, str, Unpack[Ts], int]: - reveal_type(args) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" - reveal_type(args_to_tuple(*args)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" - reveal_type(args_to_tuple(1, *args, 'a')) # N: Revealed type is "Tuple[Literal[1]?, builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int, Literal['a']?]" + reveal_type(args) # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" + reveal_type(args_to_tuple(*args)) # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" + reveal_type(args_to_tuple(1, *args, 'a')) # N: Revealed type is "tuple[Literal[1]?, builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int, Literal['a']?]" return args -reveal_type(with_prefix_suffix(True, "bar", "foo", 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]" -reveal_type(with_prefix_suffix(True, "bar", 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, builtins.int]" +reveal_type(with_prefix_suffix(True, "bar", "foo", 5)) # N: Revealed type is "tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]" +reveal_type(with_prefix_suffix(True, "bar", 5)) # N: Revealed type is "tuple[builtins.bool, builtins.str, builtins.int]" with_prefix_suffix(True, "bar", "foo", 1.0) # E: Argument 4 to "with_prefix_suffix" has incompatible type "float"; expected "int" with_prefix_suffix(True, "bar") # E: Too few arguments for "with_prefix_suffix" t = (True, "bar", "foo", 5) -reveal_type(with_prefix_suffix(*t)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, builtins.str, builtins.int]" -reveal_type(with_prefix_suffix(True, *("bar", "foo"), 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]" +reveal_type(with_prefix_suffix(*t)) # N: Revealed type is "tuple[builtins.bool, builtins.str, builtins.str, builtins.int]" +reveal_type(with_prefix_suffix(True, *("bar", "foo"), 5)) # N: Revealed type is "tuple[builtins.bool, builtins.str, Literal['foo']?, builtins.int]" -reveal_type(with_prefix_suffix(True, "bar", *["foo1", "foo2"], 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[builtins.tuple[builtins.str, ...]], builtins.int]" +reveal_type(with_prefix_suffix(True, "bar", *["foo1", "foo2"], 5)) # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[builtins.tuple[builtins.str, ...]], builtins.int]" bad_t = (True, "bar") with_prefix_suffix(*bad_t) # E: Too few arguments for "with_prefix_suffix" def foo(*args: Unpack[Ts]) -> None: - reveal_type(with_prefix_suffix(True, "bar", *args, 5)) # N: Revealed type is "Tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" + reveal_type(with_prefix_suffix(True, "bar", *args, 5)) # N: Revealed type is "tuple[builtins.bool, builtins.str, Unpack[Ts`-1], builtins.int]" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgsFixedLengthTuple] @@ -433,7 +433,7 @@ from typing import Tuple from typing_extensions import Unpack def foo(*args: Unpack[Tuple[int, str]]) -> None: - reveal_type(args) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + reveal_type(args) # N: Revealed type is "tuple[builtins.int, builtins.str]" foo(0, "foo") foo(0, 1) # E: Argument 2 to "foo" has incompatible type "int"; expected "str" @@ -444,15 +444,15 @@ foo() # E: Too few arguments for "foo" foo(*(0, "foo")) def foo2(*args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None: - reveal_type(args) # N: Revealed type is "Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]" + reveal_type(args) # N: Revealed type is "tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]" # It is hard to normalize callable types in definition, because there is deep relation between `FuncDef.type` # and `FuncDef.arguments`, therefore various typeops need to be sure to normalize Callable types before using them. -reveal_type(foo2) # N: Revealed type is "def (*args: Unpack[Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])" +reveal_type(foo2) # N: Revealed type is "def (*args: Unpack[tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])" class C: def foo2(self, *args: Unpack[Tuple[bool, Unpack[Tuple[int, str]], bool]]) -> None: ... -reveal_type(C().foo2) # N: Revealed type is "def (*args: Unpack[Tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])" +reveal_type(C().foo2) # N: Revealed type is "def (*args: Unpack[tuple[builtins.bool, builtins.int, builtins.str, builtins.bool]])" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646TypeVarStarArgsVariableLengthTuple] @@ -466,7 +466,7 @@ foo(0, 1, 2) foo(0, 1, "bar") # E: Argument 3 to "foo" has incompatible type "str"; expected "int" def foo2(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], bool, bool]]) -> None: - reveal_type(args) # N: Revealed type is "Tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.bool, builtins.bool]" + reveal_type(args) # N: Revealed type is "tuple[builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.bool, builtins.bool]" reveal_type(args[1]) # N: Revealed type is "builtins.int" def foo3(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], str, float]]) -> None: @@ -480,7 +480,7 @@ def foo3(*args: Unpack[Tuple[str, Unpack[Tuple[int, ...]], str, float]]) -> None reveal_type(args[-3]) # N: Revealed type is "Union[builtins.str, builtins.int]" args[-4] # E: Tuple index out of range \ # N: Variadic tuple can have length 3 - reveal_type(args[::-1]) # N: Revealed type is "Tuple[builtins.float, builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.str]" + reveal_type(args[::-1]) # N: Revealed type is "tuple[builtins.float, builtins.str, Unpack[builtins.tuple[builtins.int, ...]], builtins.str]" args[::2] # E: Ambiguous slice of a variadic tuple args[:2] # E: Ambiguous slice of a variadic tuple @@ -490,8 +490,8 @@ def foo4(*args: Unpack[Tuple[str, Unpack[Ts], bool, bool]]) -> None: foo2("bar", 1, 2, 3, False, True) foo2(0, 1, 2, 3, False, True) # E: Argument 1 to "foo2" has incompatible type "int"; expected "str" -foo2("bar", "bar", 2, 3, False, True) # E: Argument 2 to "foo2" has incompatible type "str"; expected "Unpack[Tuple[Unpack[Tuple[int, ...]], bool, bool]]" -foo2("bar", 1, 2, 3, 4, True) # E: Argument 5 to "foo2" has incompatible type "int"; expected "Unpack[Tuple[Unpack[Tuple[int, ...]], bool, bool]]" +foo2("bar", "bar", 2, 3, False, True) # E: Argument 2 to "foo2" has incompatible type "str"; expected "Unpack[tuple[Unpack[tuple[int, ...]], bool, bool]]" +foo2("bar", 1, 2, 3, 4, True) # E: Argument 5 to "foo2" has incompatible type "int"; expected "Unpack[tuple[Unpack[tuple[int, ...]], bool, bool]]" foo2(*("bar", 1, 2, 3, False, True)) [builtins fixtures/tuple.pyi] @@ -518,15 +518,15 @@ call(target=func, args=(0, 'foo')) call(target=func, args=('bar', 'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[str, str], None]" call(target=func, args=(True, 'foo', 0)) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[bool, str, int], None]" call(target=func, args=(0, 0, 'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[int, int, str], None]" -call(target=func, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "Callable[[VarArg(int)], None]" +call(target=func, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, str], None]"; expected "def (*int) -> None" # NOTE: This behavior may be a bit contentious, it is maybe inconsistent with our handling of # PEP646 but consistent with our handling of callable constraints. -call(target=func2, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, int], None]"; expected "Callable[[VarArg(int)], None]" +call(target=func2, args=vargs) # E: Argument "target" to "call" has incompatible type "Callable[[int, int], None]"; expected "def (*int) -> None" call(target=func3, args=vargs) call(target=func3, args=(0,1)) -call(target=func3, args=(0,'foo')) # E: Argument "target" to "call" has incompatible type "Callable[[VarArg(int)], None]"; expected "Callable[[int, str], None]" -call(target=func3, args=vargs_str) # E: Argument "target" to "call" has incompatible type "Callable[[VarArg(int)], None]"; expected "Callable[[VarArg(str)], None]" +call(target=func3, args=(0,'foo')) # E: Argument "target" to "call" has incompatible type "def func3(*args: int) -> None"; expected "Callable[[int, str], None]" +call(target=func3, args=vargs_str) # E: Argument "target" to "call" has incompatible type "def func3(*args: int) -> None"; expected "def (*str) -> None" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646CallableWithPrefixSuffix] @@ -553,7 +553,7 @@ from typing import Callable, Tuple, TypeVar from typing_extensions import Unpack, TypeVarTuple x: Callable[[str, Unpack[Tuple[int, ...]], bool], None] -reveal_type(x) # N: Revealed type is "def (builtins.str, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])" +reveal_type(x) # N: Revealed type is "def (builtins.str, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])" T = TypeVar("T") S = TypeVar("S") @@ -562,7 +562,7 @@ A = Callable[[T, Unpack[Ts], S], int] y: A[int, str, bool] reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str, builtins.bool) -> builtins.int" z: A[Unpack[Tuple[int, ...]]] -reveal_type(z) # N: Revealed type is "def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]) -> builtins.int" +reveal_type(z) # N: Revealed type is "def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]) -> builtins.int" [builtins fixtures/tuple.pyi] [case testTypeVarTuplePep646CallableInvalidSyntax] @@ -571,7 +571,7 @@ from typing_extensions import Unpack, TypeVarTuple Ts = TypeVarTuple("Ts") Us = TypeVarTuple("Us") -a: Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one Unpack in a type is not allowed +a: Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one variadic Unpack in a type is not allowed reveal_type(a) # N: Revealed type is "def [Ts, Us] (*Unpack[Ts`-1]) -> builtins.int" b: Callable[[Unpack], int] # E: Unpack[...] requires exactly one type argument reveal_type(b) # N: Revealed type is "def (*Any) -> builtins.int" @@ -584,7 +584,7 @@ from typing_extensions import ParamSpec x: Callable[[str, *Tuple[int, ...]], None] reveal_type(x) # N: Revealed type is "def (builtins.str, *builtins.int)" y: Callable[[str, *Tuple[int, ...], bool], None] -reveal_type(y) # N: Revealed type is "def (builtins.str, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])" +reveal_type(y) # N: Revealed type is "def (builtins.str, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.bool]])" P = ParamSpec("P") class C(Generic[P]): ... @@ -659,7 +659,7 @@ Ts = TypeVarTuple("Ts") A = List[Tuple[T, Unpack[Ts], T]] x: A[int, str, str] -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, builtins.str, builtins.int]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str, builtins.str, builtins.int]]" [builtins fixtures/tuple.pyi] [case testVariadicAliasBasicCallable] @@ -700,7 +700,7 @@ Ts = TypeVarTuple("Ts") Start = Tuple[int, str] A = List[Tuple[T, Unpack[Ts], S]] x: A[Unpack[Start], int] -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str, builtins.int]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str, builtins.int]]" [builtins fixtures/tuple.pyi] [case testVariadicAliasUnpackFixedTupleTarget] @@ -714,7 +714,7 @@ Ts = TypeVarTuple("Ts") Prefix = Tuple[int, int] A = Tuple[Unpack[Prefix], Unpack[Ts]] x: A[str, str] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str, builtins.str]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str, builtins.str]" [builtins fixtures/tuple.pyi] [case testVariadicAliasMultipleUnpacks] @@ -725,15 +725,15 @@ Ts = TypeVarTuple("Ts") Us = TypeVarTuple("Us") class G(Generic[Unpack[Ts]]): ... -A = Tuple[Unpack[Ts], Unpack[Us]] # E: More than one Unpack in a type is not allowed +A = Tuple[Unpack[Ts], Unpack[Us]] # E: More than one variadic Unpack in a type is not allowed x: A[int, str] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str]" -B = Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one Unpack in a type is not allowed +B = Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one variadic Unpack in a type is not allowed y: B[int, str] reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int" -C = G[Unpack[Ts], Unpack[Us]] # E: More than one Unpack in a type is not allowed +C = G[Unpack[Ts], Unpack[Us]] # E: More than one variadic Unpack in a type is not allowed z: C[int, str] reveal_type(z) # N: Revealed type is "__main__.G[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] @@ -748,7 +748,7 @@ class G(Generic[Unpack[Ts]]): ... A = List[Tuple[T, Unpack[Ts], T]] x: A -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]" B = Callable[[T, Unpack[Ts]], int] y: B @@ -770,7 +770,7 @@ class G(Generic[Unpack[Ts]]): ... A = List[Tuple[T, Unpack[Ts], S]] x: A[int] # E: Bad number of arguments for type alias, expected at least 2, given 1 -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[Any, Unpack[builtins.tuple[Any, ...]], Any]]" B = Callable[[T, S, Unpack[Ts]], int] y: B[int] # E: Bad number of arguments for type alias, expected at least 2, given 1 @@ -789,11 +789,11 @@ Ts = TypeVarTuple("Ts") A = Tuple[Unpack[Ts], Optional[A[Unpack[Ts]]]] x: A[int, str] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[..., None]]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str, Union[..., None]]" *_, last = x if last is not None: - reveal_type(last) # N: Revealed type is "Tuple[builtins.int, builtins.str, Union[Tuple[builtins.int, builtins.str, Union[..., None]], None]]" + reveal_type(last) # N: Revealed type is "tuple[builtins.int, builtins.str, Union[tuple[builtins.int, builtins.str, Union[..., None]], None]]" [builtins fixtures/tuple.pyi] [case testVariadicAliasUpperBoundCheck] @@ -823,7 +823,7 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") A = Tuple[int, Unpack[Ts], str] x: A[()] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [case testVariadicAliasVariadicTupleArg] @@ -836,7 +836,7 @@ A = Tuple[int, Unpack[Ts]] B = A[str, Unpack[Ts]] C = B[Unpack[Tuple[bool, ...]]] x: C -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.str, Unpack[builtins.tuple[builtins.bool, ...]]]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str, Unpack[builtins.tuple[builtins.bool, ...]]]" [builtins fixtures/tuple.pyi] [case testVariadicAliasVariadicTupleArgGeneric] @@ -849,7 +849,7 @@ Ts = TypeVarTuple("Ts") A = Tuple[int, Unpack[Ts]] B = A[Unpack[Tuple[T, ...]]] x: B[str] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.str, ...]]]" [builtins fixtures/tuple.pyi] [case testVariadicAliasVariadicTupleArgSplit] @@ -863,10 +863,10 @@ Ts = TypeVarTuple("Ts") A = Tuple[T, Unpack[Ts], S, T] x: A[int, Unpack[Tuple[bool, ...]], str] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.bool, ...]], builtins.str, builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.bool, ...]], builtins.str, builtins.int]" y: A[Unpack[Tuple[bool, ...]]] -reveal_type(y) # N: Revealed type is "Tuple[builtins.bool, Unpack[builtins.tuple[builtins.bool, ...]], builtins.bool, builtins.bool]" +reveal_type(y) # N: Revealed type is "tuple[builtins.bool, Unpack[builtins.tuple[builtins.bool, ...]], builtins.bool, builtins.bool]" [builtins fixtures/tuple.pyi] [case testBanPathologicalRecursiveTuples] @@ -881,12 +881,11 @@ y: B z: C reveal_type(x) # N: Revealed type is "Any" reveal_type(y) # N: Revealed type is "Any" -reveal_type(z) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" +reveal_type(z) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicWithBadType] -# flags: --new-type-inference from typing import TypeVar, Callable, Generic from typing_extensions import Unpack, TypeVarTuple @@ -1009,7 +1008,7 @@ Ints = Tuple[int, int] c: C[Unpack[Ints]] reveal_type(c.prefix) # N: Revealed type is "builtins.int" reveal_type(c.suffix) # N: Revealed type is "builtins.int" -reveal_type(c.middle) # N: Revealed type is "Tuple[()]" +reveal_type(c.middle) # N: Revealed type is "tuple[()]" [builtins fixtures/tuple.pyi] [case testVariadicUnpackItemInInstanceArguments] @@ -1079,12 +1078,12 @@ class A(Tuple[Unpack[Ts]]): fn: Callable[[Unpack[Ts]], None] x: A[int] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.A[builtins.int]]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.A[builtins.int]]" reveal_type(x[0]) # N: Revealed type is "builtins.int" reveal_type(x.fn) # N: Revealed type is "def (builtins.int)" y: A[int, str] -reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" +reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" reveal_type(y[0]) # N: Revealed type is "builtins.int" reveal_type(y.fn) # N: Revealed type is "def (builtins.int, builtins.str)" @@ -1094,7 +1093,7 @@ reveal_type(z[0]) # N: Revealed type is "builtins.int" reveal_type(z.fn) # N: Revealed type is "def (*builtins.int)" t: A[int, Unpack[Tuple[int, str]], str] -reveal_type(t) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str, builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]" +reveal_type(t) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str, builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]" reveal_type(t[0]) # N: Revealed type is "builtins.int" reveal_type(t.fn) # N: Revealed type is "def (builtins.int, builtins.int, builtins.str, builtins.str)" [builtins fixtures/tuple.pyi] @@ -1110,28 +1109,28 @@ class A(NamedTuple, Generic[Unpack[Ts], T]): val: T y: A[int, str] -reveal_type(y) # N: Revealed type is "Tuple[def (builtins.int), builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" +reveal_type(y) # N: Revealed type is "tuple[def (builtins.int), builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" reveal_type(y[0]) # N: Revealed type is "def (builtins.int)" reveal_type(y.fn) # N: Revealed type is "def (builtins.int)" z: A[Unpack[Tuple[int, ...]]] -reveal_type(z) # N: Revealed type is "Tuple[def (*builtins.int), builtins.int, fallback=__main__.A[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]" +reveal_type(z) # N: Revealed type is "tuple[def (*builtins.int), builtins.int, fallback=__main__.A[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]]" reveal_type(z.fn) # N: Revealed type is "def (*builtins.int)" t: A[int, Unpack[Tuple[int, str]], str] -reveal_type(t) # N: Revealed type is "Tuple[def (builtins.int, builtins.int, builtins.str), builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]" +reveal_type(t) # N: Revealed type is "tuple[def (builtins.int, builtins.int, builtins.str), builtins.str, fallback=__main__.A[builtins.int, builtins.int, builtins.str, builtins.str]]" def test(x: int, y: str) -> None: ... nt = A(fn=test, val=42) -reveal_type(nt) # N: Revealed type is "Tuple[def (builtins.int, builtins.str), builtins.int, fallback=__main__.A[builtins.int, builtins.str, builtins.int]]" +reveal_type(nt) # N: Revealed type is "tuple[def (builtins.int, builtins.str), builtins.int, fallback=__main__.A[builtins.int, builtins.str, builtins.int]]" def bad() -> int: ... nt2 = A(fn=bad, val=42) # E: Argument "fn" to "A" has incompatible type "Callable[[], int]"; expected "Callable[[], None]" [builtins fixtures/tuple.pyi] [case testVariadicTypedDict] -from typing import Tuple, Callable, Generic, TypeVar -from typing_extensions import TypeVarTuple, Unpack, TypedDict +from typing import Tuple, Callable, Generic, TypedDict, TypeVar +from typing_extensions import TypeVarTuple, Unpack T = TypeVar("T") Ts = TypeVarTuple("Ts") @@ -1156,7 +1155,8 @@ reveal_type(td) # N: Revealed type is "TypedDict('__main__.A', {'fn': def (buil def bad() -> int: ... td2 = A({"fn": bad, "val": 42}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]") -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFixedUnpackWithRegularInstance] from typing import Tuple, Generic, TypeVar @@ -1199,9 +1199,9 @@ Alias = Tuple[int, Unpack[Ts], str] A = Union[int, str] x: List[Alias[int, Unpack[A], str]] # E: "Union[int, str]" cannot be unpacked (must be tuple or TypeVarTuple) -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str, builtins.str]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str, builtins.str]]" y: List[Alias[int, Unpack[Undefined], str]] # E: Name "Undefined" is not defined -reveal_type(y) # N: Revealed type is "builtins.list[Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str]]" +reveal_type(y) # N: Revealed type is "builtins.list[tuple[builtins.int, Unpack[builtins.tuple[Any, ...]], builtins.str]]" [builtins fixtures/tuple.pyi] [case testVariadicAliasForwardRefToFixedUnpack] @@ -1214,7 +1214,7 @@ Ts = TypeVarTuple("Ts") Alias = Tuple[T, Unpack[Ts], S] x: Alias[int, Unpack[Other]] Other = Tuple[int, str] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [case testVariadicAliasForwardRefToVariadicUnpack] @@ -1227,7 +1227,7 @@ Ts = TypeVarTuple("Ts") Alias = Tuple[T, Unpack[Ts], S] x: Alias[int, Unpack[Other]] Other = Tuple[int, ...] -reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" +reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" [builtins fixtures/tuple.pyi] [case testVariadicInstanceStrictPrefixSuffixCheck] @@ -1270,7 +1270,7 @@ class A(Tuple[Unpack[TP]]): ... def test(d: A[int, str]) -> None: if isinstance(d, A): - reveal_type(d) # N: Revealed type is "Tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" + reveal_type(d) # N: Revealed type is "tuple[builtins.int, builtins.str, fallback=__main__.A[builtins.int, builtins.str]]" else: reveal_type(d) # E: Statement is unreachable @@ -1314,7 +1314,7 @@ f2(t1) f2(t2) f2(t3) f2(t4) -f2(t5) # E: Argument 1 to "f2" has incompatible type "Tuple[int, ...]"; expected "Tuple[float, Unpack[Tuple[float, ...]]]" +f2(t5) # E: Argument 1 to "f2" has incompatible type "tuple[int, ...]"; expected "tuple[float, Unpack[tuple[float, ...]]]" f2(tl) f2(tr) @@ -1323,16 +1323,16 @@ f3(t1) f3(t2) f3(t3) f3(t4) -f3(t5) # E: Argument 1 to "f3" has incompatible type "Tuple[int, ...]"; expected "Tuple[Unpack[Tuple[float, ...]], float]" +f3(t5) # E: Argument 1 to "f3" has incompatible type "tuple[int, ...]"; expected "tuple[Unpack[tuple[float, ...]], float]" f3(tl) f3(tr) f4(t1) -f4(t2) # E: Argument 1 to "f4" has incompatible type "Tuple[int, Unpack[Tuple[int, ...]]]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]" -f4(t3) # E: Argument 1 to "f4" has incompatible type "Tuple[Unpack[Tuple[int, ...]], int]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]" +f4(t2) # E: Argument 1 to "f4" has incompatible type "tuple[int, Unpack[tuple[int, ...]]]"; expected "tuple[float, Unpack[tuple[float, ...]], float]" +f4(t3) # E: Argument 1 to "f4" has incompatible type "tuple[Unpack[tuple[int, ...]], int]"; expected "tuple[float, Unpack[tuple[float, ...]], float]" f4(t4) -f4(t5) # E: Argument 1 to "f4" has incompatible type "Tuple[int, ...]"; expected "Tuple[float, Unpack[Tuple[float, ...]], float]" +f4(t5) # E: Argument 1 to "f4" has incompatible type "tuple[int, ...]"; expected "tuple[float, Unpack[tuple[float, ...]], float]" f4(tl) f4(tr) @@ -1349,7 +1349,7 @@ T = TypeVar("T") def f(x: Tuple[int, Unpack[Tuple[T, ...]]]) -> T: ... vt0: Tuple[int, ...] -f(vt0) # E: Argument 1 to "f" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]]]" +f(vt0) # E: Argument 1 to "f" has incompatible type "tuple[int, ...]"; expected "tuple[int, Unpack[tuple[int, ...]]]" vt1: Tuple[Unpack[Tuple[int, ...]], int] reveal_type(f(vt1)) # N: Revealed type is "builtins.int" @@ -1357,12 +1357,12 @@ reveal_type(f(vt1)) # N: Revealed type is "builtins.int" S = TypeVar("S") Ts = TypeVarTuple("Ts") def g(x: Tuple[T, Unpack[Ts], S]) -> Tuple[T, Unpack[Ts], S]: ... -g(vt0) # E: Argument 1 to "g" has incompatible type "Tuple[int, ...]"; expected "Tuple[int, Unpack[Tuple[int, ...]], int]" +g(vt0) # E: Argument 1 to "g" has incompatible type "tuple[int, ...]"; expected "tuple[int, Unpack[tuple[int, ...]], int]" U = TypeVar("U") def h(x: List[Tuple[T, S, U]]) -> Tuple[T, S, U]: ... vt2: Tuple[Unpack[Tuple[int, ...]], int] -vt2 = h(reveal_type([])) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int, builtins.int]]" +vt2 = h(reveal_type([])) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.int, builtins.int]]" [builtins fixtures/tuple.pyi] [case testVariadicSelfTypeErasure] @@ -1394,7 +1394,7 @@ fii(C()) # E: Argument 1 to "fii" has incompatible type "C"; expected "B[int, i fii(D()) # E: Argument 1 to "fii" has incompatible type "D"; expected "B[int, int]" fis(C()) fis(D()) # E: Argument 1 to "fis" has incompatible type "D"; expected "B[int, str]" -fiv(C()) # E: Argument 1 to "fiv" has incompatible type "C"; expected "B[Unpack[Tuple[int, ...]]]" +fiv(C()) # E: Argument 1 to "fiv" has incompatible type "C"; expected "B[Unpack[tuple[int, ...]]]" fiv(D()) [builtins fixtures/tuple.pyi] @@ -1416,14 +1416,14 @@ civ: C[Unpack[Tuple[int, ...]]] fii(cii) fii(cis) # E: Argument 1 to "fii" has incompatible type "C[int, str]"; expected "B[int, int]" -fii(civ) # E: Argument 1 to "fii" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, int]" +fii(civ) # E: Argument 1 to "fii" has incompatible type "C[Unpack[tuple[int, ...]]]"; expected "B[int, int]" fis(cii) # E: Argument 1 to "fis" has incompatible type "C[int, int]"; expected "B[int, str]" fis(cis) -fis(civ) # E: Argument 1 to "fis" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, str]" +fis(civ) # E: Argument 1 to "fis" has incompatible type "C[Unpack[tuple[int, ...]]]"; expected "B[int, str]" fiv(cii) -fiv(cis) # E: Argument 1 to "fiv" has incompatible type "C[int, str]"; expected "B[Unpack[Tuple[int, ...]]]" +fiv(cis) # E: Argument 1 to "fiv" has incompatible type "C[int, str]"; expected "B[Unpack[tuple[int, ...]]]" fiv(civ) [builtins fixtures/tuple.pyi] @@ -1446,10 +1446,10 @@ civ: C[Unpack[Tuple[int, ...]]] ff(cii) ff(cis) # E: Argument 1 to "ff" has incompatible type "C[int, str]"; expected "B[int, int, int]" -ff(civ) # E: Argument 1 to "ff" has incompatible type "C[Unpack[Tuple[int, ...]]]"; expected "B[int, int, int]" +ff(civ) # E: Argument 1 to "ff" has incompatible type "C[Unpack[tuple[int, ...]]]"; expected "B[int, int, int]" fv(cii) -fv(cis) # E: Argument 1 to "fv" has incompatible type "C[int, str]"; expected "B[Unpack[Tuple[int, ...]]]" +fv(cis) # E: Argument 1 to "fv" has incompatible type "C[int, str]"; expected "B[Unpack[tuple[int, ...]]]" fv(civ) [builtins fixtures/tuple.pyi] @@ -1485,17 +1485,17 @@ class C3(B[int, Unpack[Ts], T]): ... class C4(B[Unpack[Tuple[T, ...]]]): ... c1: C1 -reveal_type(c1.meth()) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(c1.meth()) # N: Revealed type is "tuple[builtins.int, builtins.str]" c2f: C2[int, str] c2v: C2[Unpack[Tuple[int, ...]]] -reveal_type(c2f.meth()) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(c2f.meth()) # N: Revealed type is "tuple[builtins.int, builtins.str]" reveal_type(c2v.meth()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" c3f: C3[int, str] c3v: C3[Unpack[Tuple[int, ...]]] -reveal_type(c3f.meth()) # N: Revealed type is "Tuple[builtins.int, builtins.int, builtins.str]" -reveal_type(c3v.meth()) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" +reveal_type(c3f.meth()) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.str]" +reveal_type(c3v.meth()) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.int]" c4: C4[int] reveal_type(c4.meth()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" @@ -1648,9 +1648,9 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: x = *arg, - reveal_type(x) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" y = 1, *arg, 2 - reveal_type(y) # N: Revealed type is "Tuple[builtins.int, builtins.int, Unpack[Ts`-1], builtins.str, builtins.int]" + reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.int, Unpack[Ts`-1], builtins.str, builtins.int]" z = (*arg, *arg) reveal_type(z) # N: Revealed type is "builtins.tuple[builtins.object, ...]" [builtins fixtures/tuple.pyi] @@ -1666,14 +1666,14 @@ b: Tuple[int, Unpack[Tuple[float, ...]], str] x = *a, reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.float, ...]" y = 1, *a, 2 -reveal_type(y) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" +reveal_type(y) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" z = (*a, *a) reveal_type(z) # N: Revealed type is "builtins.tuple[builtins.float, ...]" x2 = *b, -reveal_type(x2) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" +reveal_type(x2) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" y2 = 1, *b, 2 -reveal_type(y2) # N: Revealed type is "Tuple[builtins.int, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str, builtins.int]" +reveal_type(y2) # N: Revealed type is "tuple[builtins.int, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str, builtins.int]" z2 = (*b, *b) reveal_type(z2) # N: Revealed type is "builtins.tuple[builtins.object, ...]" [builtins fixtures/tuple.pyi] @@ -1713,16 +1713,16 @@ from typing_extensions import TypeVarTuple, Unpack vtf: Tuple[float, ...] vt: Tuple[int, Unpack[Tuple[float, ...]], int] -reveal_type(vt + (1, 2)) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int, Literal[1]?, Literal[2]?]" -reveal_type((1, 2) + vt) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" +reveal_type(vt + (1, 2)) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int, Literal[1]?, Literal[2]?]" +reveal_type((1, 2) + vt) # N: Revealed type is "tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.int]" reveal_type(vt + vt) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.float], ...]" -reveal_type(vtf + (1, 2)) # N: Revealed type is "Tuple[Unpack[builtins.tuple[builtins.float, ...]], Literal[1]?, Literal[2]?]" -reveal_type((1, 2) + vtf) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, Unpack[builtins.tuple[builtins.float, ...]]]" +reveal_type(vtf + (1, 2)) # N: Revealed type is "tuple[Unpack[builtins.tuple[builtins.float, ...]], Literal[1]?, Literal[2]?]" +reveal_type((1, 2) + vtf) # N: Revealed type is "tuple[Literal[1]?, Literal[2]?, Unpack[builtins.tuple[builtins.float, ...]]]" Ts = TypeVarTuple("Ts") def foo(arg: Tuple[int, Unpack[Ts], str]) -> None: - reveal_type(arg + (1, 2)) # N: Revealed type is "Tuple[builtins.int, Unpack[Ts`-1], builtins.str, Literal[1]?, Literal[2]?]" - reveal_type((1, 2) + arg) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[Ts`-1], builtins.str]" + reveal_type(arg + (1, 2)) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str, Literal[1]?, Literal[2]?]" + reveal_type((1, 2) + arg) # N: Revealed type is "tuple[Literal[1]?, Literal[2]?, builtins.int, Unpack[Ts`-1], builtins.str]" reveal_type(arg + arg) # N: Revealed type is "builtins.tuple[builtins.object, ...]" [builtins fixtures/tuple.pyi] @@ -1806,7 +1806,7 @@ def add(self: Tuple[T, ...], other: Tuple[T, ...]) -> Tuple[T, ...]: def add(self: Any, other: Any) -> Any: ... def test(a: Tuple[int, str], b: Tuple[bool], c: Tuple[bool, ...]): - reveal_type(add(a, b)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.bool]" + reveal_type(add(a, b)) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.bool]" reveal_type(add(b, c)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" [builtins fixtures/tuple.pyi] @@ -1903,7 +1903,7 @@ def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T: return submit2(func, 1, *args) def foo_bad(func: Callable[[Unpack[Args2]], T], *args: Unpack[Args2]) -> T: - return submit2(func, 1, *args) # E: Argument 1 to "submit2" has incompatible type "Callable[[VarArg(Unpack[Args2])], T]"; expected "Callable[[int, VarArg(Unpack[Args2])], T]" + return submit2(func, 1, *args) # E: Argument 1 to "submit2" has incompatible type "def (*Unpack[Args2]) -> T"; expected "def (int, /, *Unpack[Args2]) -> T" [builtins fixtures/tuple.pyi] [case testTypeVarTupleParamSpecInteraction] @@ -1922,7 +1922,7 @@ def foo(func: Callable[[Unpack[Args]], T], *args: Unpack[Args]) -> T: return submit(func, *args) def foo2(func: Callable[[Unpack[Args]], T], *args: Unpack[Args2]) -> T: - return submit(func, *args) # E: Argument 2 to "submit" has incompatible type "*Tuple[Unpack[Args2]]"; expected "Unpack[Args]" + return submit(func, *args) # E: Argument 2 to "submit" has incompatible type "*tuple[Unpack[Args2]]"; expected "Unpack[Args]" def foo3(func: Callable[[int, Unpack[Args2]], T], *args: Unpack[Args2]) -> T: return submit(func, 1, *args) @@ -2014,12 +2014,12 @@ from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple("Ts") class B(Generic[Unpack[Ts]]): def __init__(self, x: Tuple[Unpack[Ts]], *args: Unpack[Ts]) -> None: ... -reveal_type(B) # N: Revealed type is "def [Ts] (x: Tuple[Unpack[Ts`1]], *args: Unpack[Ts`1]) -> __main__.B[Unpack[Ts`1]]" +reveal_type(B) # N: Revealed type is "def [Ts] (x: tuple[Unpack[Ts`1]], *args: Unpack[Ts`1]) -> __main__.B[Unpack[Ts`1]]" T = TypeVar("T") S = TypeVar("S") class C(B[T, S]): ... -reveal_type(C) # N: Revealed type is "def [T, S] (x: Tuple[T`1, S`2], T`1, S`2) -> __main__.C[T`1, S`2]" +reveal_type(C) # N: Revealed type is "def [T, S] (x: tuple[T`1, S`2], T`1, S`2) -> __main__.C[T`1, S`2]" [builtins fixtures/tuple.pyi] [case testVariadicClassGenericSelf] @@ -2034,13 +2034,13 @@ class B(Generic[Unpack[Ts]]): def on_pair(self: B[T, S]) -> Tuple[T, S]: ... b1: B[int] -reveal_type(b1.on_pair()) # E: Invalid self argument "B[int]" to attribute function "on_pair" with type "Callable[[B[T, S]], Tuple[T, S]]" \ - # N: Revealed type is "Tuple[Never, Never]" +reveal_type(b1.on_pair()) # E: Invalid self argument "B[int]" to attribute function "on_pair" with type "Callable[[B[T, S]], tuple[T, S]]" \ + # N: Revealed type is "tuple[Never, Never]" b2: B[int, str] -reveal_type(b2.on_pair()) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(b2.on_pair()) # N: Revealed type is "tuple[builtins.int, builtins.str]" b3: B[int, str, int] -reveal_type(b3.on_pair()) # E: Invalid self argument "B[int, str, int]" to attribute function "on_pair" with type "Callable[[B[T, S]], Tuple[T, S]]" \ - # N: Revealed type is "Tuple[Never, Never]" +reveal_type(b3.on_pair()) # E: Invalid self argument "B[int, str, int]" to attribute function "on_pair" with type "Callable[[B[T, S]], tuple[T, S]]" \ + # N: Revealed type is "tuple[Never, Never]" class C(B[T, S]): ... c: C[int, str] @@ -2083,9 +2083,9 @@ Ts = TypeVarTuple("Ts") class B(Generic[Unpack[Ts]]): items: Tuple[Unpack[Ts]] -reveal_type(B) # N: Revealed type is "def [Ts] (items: Tuple[Unpack[Ts`1]]) -> __main__.B[Unpack[Ts`1]]" +reveal_type(B) # N: Revealed type is "def [Ts] (items: tuple[Unpack[Ts`1]]) -> __main__.B[Unpack[Ts`1]]" b = B((1, "yes")) -reveal_type(b.items) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(b.items) # N: Revealed type is "tuple[builtins.int, builtins.str]" T = TypeVar("T") S = TypeVar("S") @@ -2095,9 +2095,9 @@ class C(B[T, S]): first: T second: S -reveal_type(C) # N: Revealed type is "def [T, S] (items: Tuple[T`1, S`2], first: T`1, second: S`2) -> __main__.C[T`1, S`2]" +reveal_type(C) # N: Revealed type is "def [T, S] (items: tuple[T`1, S`2], first: T`1, second: S`2) -> __main__.C[T`1, S`2]" c = C((1, "yes"), 2, "no") -reveal_type(c.items) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(c.items) # N: Revealed type is "tuple[builtins.int, builtins.str]" reveal_type(c.first) # N: Revealed type is "builtins.int" reveal_type(c.second) # N: Revealed type is "builtins.str" [builtins fixtures/dataclasses.pyi] @@ -2126,17 +2126,17 @@ class Good: def meth(self, __x: int, y: str) -> None: ... g: Good -reveal_type(get_items(g)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" -reveal_type(match(g)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(get_items(g)) # N: Revealed type is "tuple[builtins.int, builtins.str]" +reveal_type(match(g)) # N: Revealed type is "tuple[builtins.int, builtins.str]" b: Bad -get_items(b) # E: Argument 1 to "get_items" has incompatible type "Bad"; expected "P[Unpack[Tuple[Never, ...]]]" \ +get_items(b) # E: Argument 1 to "get_items" has incompatible type "Bad"; expected "P[Unpack[tuple[Never, ...]]]" \ # N: Following member(s) of "Bad" have conflicts: \ # N: Expected: \ - # N: def items(self) -> Tuple[Never, ...] \ + # N: def items(self) -> tuple[Never, ...] \ # N: Got: \ - # N: def items(self) -> List[int] -match(b) # E: Argument 1 to "match" has incompatible type "Bad"; expected "PC[Unpack[Tuple[Never, ...]]]" \ + # N: def items(self) -> list[int] +match(b) # E: Argument 1 to "match" has incompatible type "Bad"; expected "PC[Unpack[tuple[Never, ...]]]" \ # N: Following member(s) of "Bad" have conflicts: \ # N: Expected: \ # N: def meth(self, *args: Never) -> None \ @@ -2160,15 +2160,15 @@ from typing import Callable, Tuple f: Callable[[int, *Tuple[str, ...], int], None] g: Callable[[int, *Tuple[str, ...], int], None] -reveal_type([f, g]) # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.int]])]" +reveal_type([f, g]) # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.str, ...]], builtins.int]])]" h: Callable[[int, *Tuple[str, ...], str], None] -reveal_type([f, h]) # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.str, ...]], Never]])]" +reveal_type([f, h]) # N: Revealed type is "builtins.list[def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.str, ...]], Never]])]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleBothUnpacksSimple] -from typing import Tuple -from typing_extensions import Unpack, TypeVarTuple, TypedDict +from typing import Tuple, TypedDict +from typing_extensions import Unpack, TypeVarTuple class Keywords(TypedDict): a: str @@ -2202,11 +2202,12 @@ def bad2( **kwargs: Unpack[Ints], # E: Unpack item in ** argument must be a TypedDict ) -> None: ... reveal_type(bad2) # N: Revealed type is "def (one: builtins.int, *args: Any, other: builtins.str =, **kwargs: Any)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarTupleBothUnpacksCallable] -from typing import Callable, Tuple -from typing_extensions import Unpack, TypedDict +from typing import Callable, Tuple, TypedDict +from typing_extensions import Unpack class Keywords(TypedDict): a: str @@ -2217,23 +2218,24 @@ cb: Callable[[Unpack[Ints], Unpack[Keywords]], None] reveal_type(cb) # N: Revealed type is "def (*builtins.int, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" cb2: Callable[[int, Unpack[Ints], int, Unpack[Keywords]], None] -reveal_type(cb2) # N: Revealed type is "def (builtins.int, *Unpack[Tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]], **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" +reveal_type(cb2) # N: Revealed type is "def (builtins.int, *Unpack[tuple[Unpack[builtins.tuple[builtins.int, ...]], builtins.int]], **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" cb2(1, 2, 3, a="a", b="b") cb2(1, a="a", b="b") # E: Too few arguments cb2(1, 2, 3, a="a") # E: Missing named argument "b" -bad1: Callable[[Unpack[Ints], Unpack[Ints]], None] # E: More than one Unpack in a type is not allowed +bad1: Callable[[Unpack[Ints], Unpack[Ints]], None] # E: More than one variadic Unpack in a type is not allowed reveal_type(bad1) # N: Revealed type is "def (*builtins.int)" bad2: Callable[[Unpack[Keywords], Unpack[Keywords]], None] # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) reveal_type(bad2) # N: Revealed type is "def (*Any, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])" bad3: Callable[[Unpack[Keywords], Unpack[Ints]], None] # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) \ - # E: More than one Unpack in a type is not allowed + # E: More than one variadic Unpack in a type is not allowed reveal_type(bad3) # N: Revealed type is "def (*Any)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarTupleBothUnpacksApplication] -from typing import Callable, TypeVar, Optional -from typing_extensions import Unpack, TypeVarTuple, TypedDict +from typing import Callable, TypedDict, TypeVar, Optional +from typing_extensions import Unpack, TypeVarTuple class Keywords(TypedDict): a: str @@ -2262,7 +2264,8 @@ def test2( func(*args) # E: Missing named argument "a" \ # E: Missing named argument "b" return func(*args, **kwargs) -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackTupleSpecialCaseNoCrash] from typing import Tuple, TypeVar @@ -2279,7 +2282,7 @@ keys: Tuple[Unpack[Tuple[int, ...]]] foo(keys, 1) foo(*keys, 1) -bar(keys, 1) # E: Argument 1 to "bar" has incompatible type "Tuple[Unpack[Tuple[int, ...]]]"; expected "int" +bar(keys, 1) # E: Argument 1 to "bar" has incompatible type "tuple[Unpack[tuple[int, ...]]]"; expected "int" bar(*keys, 1) # OK reveal_type(baz(keys, 1)) # N: Revealed type is "builtins.object" @@ -2289,7 +2292,7 @@ reveal_type(baz(*keys, 1)) # N: Revealed type is "builtins.int" [case testVariadicTupleContextNoCrash] from typing import Tuple, Unpack -x: Tuple[int, Unpack[Tuple[int, ...]]] = () # E: Incompatible types in assignment (expression has type "Tuple[()]", variable has type "Tuple[int, Unpack[Tuple[int, ...]]]") +x: Tuple[int, Unpack[Tuple[int, ...]]] = () # E: Incompatible types in assignment (expression has type "tuple[()]", variable has type "tuple[int, Unpack[tuple[int, ...]]]") y: Tuple[int, Unpack[Tuple[int, ...]]] = (1, 2) z: Tuple[int, Unpack[Tuple[int, ...]]] = (1,) w: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *[2, 3, 4]) @@ -2305,18 +2308,21 @@ def higher_order(f: _CallableValue) -> None: ... def good1(*args: int) -> None: ... def good2(*args: str) -> int: ... -def bad1(a: str, b: int, /) -> None: ... -def bad2(c: bytes, *args: int) -> str: ... -def bad3(*, d: str) -> int: ... -def bad4(**kwargs: None) -> None: ... +# These are special-cased for *args: Any (as opposite to *args: object) +def ok1(a: str, b: int, /) -> None: ... +def ok2(c: bytes, *args: int) -> str: ... + +def bad1(*, d: str) -> int: ... +def bad2(**kwargs: None) -> None: ... higher_order(good1) higher_order(good2) -higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[VarArg(Any)], Any]" -higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[VarArg(Any)], Any]" -higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[VarArg(Any)], Any]" -higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(ok1) +higher_order(ok2) + +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "def bad1(*, d: str) -> int"; expected "def (*Any) -> Any" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "def bad2(**kwargs: None) -> None"; expected "def (*Any) -> Any" [builtins fixtures/tuple.pyi] [case testAliasToCallableWithUnpack2] @@ -2332,10 +2338,10 @@ def bad3(*, d: str) -> int: ... def bad4(**kwargs: None) -> None: ... higher_order(good) -higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" -higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" -higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" -higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "def (int, str, /, *Unpack[tuple[Unpack[tuple[Any, ...]], int]]) -> Any" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "def bad2(c: bytes, *args: int) -> str"; expected "def (int, str, /, *Unpack[tuple[Unpack[tuple[Any, ...]], int]]) -> Any" +higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "def bad3(*, d: str) -> int"; expected "def (int, str, /, *Unpack[tuple[Unpack[tuple[Any, ...]], int]]) -> Any" +higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "def bad4(**kwargs: None) -> None"; expected "def (int, str, /, *Unpack[tuple[Unpack[tuple[Any, ...]], int]]) -> Any" [builtins fixtures/tuple.pyi] [case testAliasToCallableWithUnpackInvalid] @@ -2351,7 +2357,7 @@ Alias = Callable[[Unpack[T]], int] # E: "T" cannot be unpacked (must be tuple o x: Alias[int] reveal_type(x) # N: Revealed type is "def (*Any) -> builtins.int" x = good -x = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]") +x = bad # E: Incompatible types in assignment (expression has type "def bad(*x: int, y: int) -> int", variable has type "def (*Any) -> int") [builtins fixtures/tuple.pyi] [case testTypeVarTupleInvariant] @@ -2365,19 +2371,19 @@ def pointwise_multiply(x: Array[Unpack[Ts]], y: Array[Unpack[Ts]]) -> Array[Unpa def a1(x: Array[int], y: Array[str], z: Array[int, str]) -> None: reveal_type(pointwise_multiply(x, x)) # N: Revealed type is "__main__.Array[builtins.int]" - reveal_type(pointwise_multiply(x, y)) # E: Cannot infer type argument 1 of "pointwise_multiply" \ + reveal_type(pointwise_multiply(x, y)) # E: Cannot infer value of type parameter "Ts" of "pointwise_multiply" \ # N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]" - reveal_type(pointwise_multiply(x, z)) # E: Cannot infer type argument 1 of "pointwise_multiply" \ + reveal_type(pointwise_multiply(x, z)) # E: Cannot infer value of type parameter "Ts" of "pointwise_multiply" \ # N: Revealed type is "__main__.Array[Unpack[builtins.tuple[Any, ...]]]" def func(x: Array[Unpack[Ts]], *args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ... def a2(x: Array[int, str]) -> None: - reveal_type(func(x, 2, "Hello")) # N: Revealed type is "Tuple[builtins.int, builtins.str]" - reveal_type(func(x, 2)) # E: Cannot infer type argument 1 of "func" \ + reveal_type(func(x, 2, "Hello")) # N: Revealed type is "tuple[builtins.int, builtins.str]" + reveal_type(func(x, 2)) # E: Cannot infer value of type parameter "Ts" of "func" \ # N: Revealed type is "builtins.tuple[Any, ...]" - reveal_type(func(x, 2, "Hello", True)) # E: Cannot infer type argument 1 of "func" \ + reveal_type(func(x, 2, "Hello", True)) # E: Cannot infer value of type parameter "Ts" of "func" \ # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/tuple.pyi] @@ -2422,8 +2428,8 @@ Ts = TypeVarTuple("Ts") @cm def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ... -reveal_type(test) # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1]) -> __main__.CM[Tuple[Unpack[Ts`-1]]]" -reveal_type(test(1, 2, 3)) # N: Revealed type is "__main__.CM[Tuple[Literal[1]?, Literal[2]?, Literal[3]?]]" +reveal_type(test) # N: Revealed type is "def [Ts] (*args: Unpack[Ts`-1]) -> __main__.CM[tuple[Unpack[Ts`-1]]]" +reveal_type(test(1, 2, 3)) # N: Revealed type is "__main__.CM[tuple[Literal[1]?, Literal[2]?, Literal[3]?]]" [builtins fixtures/tuple.pyi] [case testTypeVarTupleAgainstParamSpecActualFailedNoCrash] @@ -2437,7 +2443,7 @@ class CM(Generic[R]): ... def cm(fn: Callable[P, List[R]]) -> Callable[P, CM[R]]: ... Ts = TypeVarTuple("Ts") -@cm # E: Argument 1 to "cm" has incompatible type "Callable[[VarArg(Unpack[Ts])], Tuple[Unpack[Ts]]]"; expected "Callable[[VarArg(Never)], List[Never]]" +@cm # E: Argument 1 to "cm" has incompatible type "def [Ts`-1] test(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]"; expected "def (*args: Never) -> list[Never]" def test(*args: Unpack[Ts]) -> Tuple[Unpack[Ts]]: ... reveal_type(test) # N: Revealed type is "def (*args: Never) -> __main__.CM[Never]" @@ -2458,7 +2464,7 @@ Ts = TypeVarTuple("Ts") @cm 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]]]" +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] @@ -2487,3 +2493,249 @@ class C(Generic[P, R]): 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] + +[case testTypeVarTupleInstanceOverlap] +# flags: --strict-equality +from typing import TypeVarTuple, Unpack, Generic + +Ts = TypeVarTuple("Ts") + +class Foo(Generic[Unpack[Ts]]): + pass + +x1: Foo[Unpack[tuple[int, ...]]] +y1: Foo[Unpack[tuple[str, ...]]] +x1 is y1 # E: Non-overlapping identity check (left operand type: "Foo[Unpack[tuple[int, ...]]]", right operand type: "Foo[Unpack[tuple[str, ...]]]") + +x2: Foo[Unpack[tuple[int, ...]]] +y2: Foo[Unpack[tuple[int, ...]]] +x2 is y2 + +x3: Foo[Unpack[tuple[int, ...]]] +y3: Foo[Unpack[tuple[int, int]]] +x3 is y3 + +x4: Foo[Unpack[tuple[str, ...]]] +y4: Foo[Unpack[tuple[int, int]]] +x4 is y4 # E: Non-overlapping identity check (left operand type: "Foo[Unpack[tuple[str, ...]]]", right operand type: "Foo[int, int]") +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleErasureNormalized] +from typing import TypeVarTuple, Unpack, Generic, Union +from collections.abc import Callable + +Args = TypeVarTuple("Args") + +class Built(Generic[Unpack[Args]]): + pass + +def example( + fn: Union[Built[Unpack[Args]], Callable[[Unpack[Args]], None]] +) -> Built[Unpack[Args]]: ... + +@example +def command() -> None: + return +reveal_type(command) # N: Revealed type is "__main__.Built[()]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleSelfMappedPrefix] +from typing import TypeVarTuple, Generic, Unpack + +Ts = TypeVarTuple("Ts") +class Base(Generic[Unpack[Ts]]): + attr: tuple[Unpack[Ts]] + + @property + def prop(self) -> tuple[Unpack[Ts]]: + return self.attr + + def meth(self) -> tuple[Unpack[Ts]]: + return self.attr + +Ss = TypeVarTuple("Ss") +class Derived(Base[str, Unpack[Ss]]): + def test(self) -> None: + reveal_type(self.attr) # N: Revealed type is "tuple[builtins.str, Unpack[Ss`1]]" + reveal_type(self.prop) # N: Revealed type is "tuple[builtins.str, Unpack[Ss`1]]" + reveal_type(self.meth()) # N: Revealed type is "tuple[builtins.str, Unpack[Ss`1]]" +[builtins fixtures/property.pyi] + +[case testTypeVarTupleProtocolPrefix] +from typing import Protocol, Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +class A(Protocol[Unpack[Ts]]): + def f(self, z: str, *args: Unpack[Ts]) -> None: ... + +class C: + def f(self, z: str, x: int) -> None: ... + +def f(x: A[Unpack[Ts]]) -> tuple[Unpack[Ts]]: ... + +reveal_type(f(C())) # N: Revealed type is "tuple[builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleHomogeneousCallableNormalized] +from typing import Generic, Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): + def foo(self, *args: Unpack[Ts]) -> None: ... + +c: C[Unpack[tuple[int, ...]]] +reveal_type(c.foo) # N: Revealed type is "def (*args: builtins.int)" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleJoinInstanceTypeVar] +from typing import Any, Unpack, TypeVarTuple, TypeVar + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +def join(x: T, y: T) -> T: ... +def test(xs: tuple[Unpack[Ts]], xsi: tuple[int, Unpack[Ts]]) -> None: + a: tuple[Any, ...] + reveal_type(join(xs, a)) # N: Revealed type is "builtins.tuple[Any, ...]" + reveal_type(join(a, xs)) # N: Revealed type is "builtins.tuple[Any, ...]" + aa: tuple[Unpack[tuple[Any, ...]]] + reveal_type(join(xs, aa)) # N: Revealed type is "builtins.tuple[Any, ...]" + reveal_type(join(aa, xs)) # N: Revealed type is "builtins.tuple[Any, ...]" + ai: tuple[int, Unpack[tuple[Any, ...]]] + reveal_type(join(xsi, ai)) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" + reveal_type(join(ai, xsi)) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" +[builtins fixtures/tuple.pyi] + +[case testTypeVarTupleInferAgainstAnyCallableSuffix] +from typing import Any, Callable, TypeVar, TypeVarTuple + +Ts = TypeVarTuple("Ts") +R = TypeVar("R") +def deco(func: Callable[[*Ts, int], R]) -> Callable[[*Ts], R]: + ... + +untyped: Any +reveal_type(deco(untyped)) # N: Revealed type is "def (*Any) -> Any" +[builtins fixtures/tuple.pyi] + +[case testNoCrashOnNonNormalUnpackInCallable] +from typing import Callable, Unpack, TypeVar + +T = TypeVar("T") +def fn(f: Callable[[*tuple[T]], int]) -> Callable[[*tuple[T]], int]: ... + +def test(*args: Unpack[tuple[T]]) -> int: ... +reveal_type(fn(test)) # N: Revealed type is "def [T] (T`1) -> builtins.int" +[builtins fixtures/tuple.pyi] + +[case testNoGenericTypeVarTupleClassVarAccess] +from typing import Generic, Tuple, TypeVarTuple, Unpack + +Ts = TypeVarTuple("Ts") +class C(Generic[Unpack[Ts]]): + x: Tuple[Unpack[Ts]] + +reveal_type(C.x) # E: Access to generic instance variables via class is ambiguous \ + # N: Revealed type is "builtins.tuple[Any, ...]" + +class Bad(C[int, int]): + pass +reveal_type(Bad.x) # E: Access to generic instance variables via class is ambiguous \ + # N: Revealed type is "tuple[builtins.int, builtins.int]" +reveal_type(Bad().x) # N: Revealed type is "tuple[builtins.int, builtins.int]" + +class Good(C[int, int]): + x = (1, 1) +reveal_type(Good.x) # N: Revealed type is "tuple[builtins.int, builtins.int]" +reveal_type(Good().x) # N: Revealed type is "tuple[builtins.int, builtins.int]" +[builtins fixtures/tuple.pyi] + +[case testConstraintsIncludeTupleFallback] +from typing import Generic, TypeVar +from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") +_FT = TypeVar("_FT", bound=type) + +def identity(smth: _FT) -> _FT: + return smth + +@identity +class S(tuple[Unpack[Ts]], Generic[T, Unpack[Ts]]): + def f(self, x: T, /) -> T: ... +[builtins fixtures/tuple.pyi] + +[case testNoCrashSubclassingTupleWithTrivialUnpack] +# https://github.com/python/mypy/issues/19105 +from typing import Unpack + +class A(tuple[Unpack[tuple[int]]]): ... +class B(tuple[Unpack[tuple[()]]]): ... + +a: A +reveal_type(tuple(a)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +(x,) = a + +b: B +(_,) = b # E: Need more than 0 values to unpack (1 expected) +[builtins fixtures/tuple.pyi] + +[case testNoCrashSubclassingTupleWithVariadicUnpack] +# https://github.com/python/mypy/issues/19105 +from typing import Unpack + +class A(tuple[Unpack[tuple[int, ...]]]): ... + +a: A +tuple(a) +(x,) = a +(_,) = a +[builtins fixtures/tuple.pyi] + +[case testNoCrashOnUndefinedUnpackInBase] +from typing import TypeVarTuple, Generic, Unpack + +Ts = TypeVarTuple("Ts") + +class MyTuple(tuple[Unpack[TsWithTypo]], Generic[Unpack[Ts]]): # E: Name "TsWithTypo" is not defined + ... + +x: MyTuple[int, str] +reveal_type(x[0]) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testNoCrashOnInvalidUnpackInBase] +from typing import TypeVarTuple, Generic, Unpack, Union + +Ts = TypeVarTuple("Ts") + +class MyTuple(tuple[Unpack[Union[int, str]]], Generic[Unpack[Ts]]): # E: "Union[int, str]" cannot be unpacked (must be tuple or TypeVarTuple) + ... + +x: MyTuple[int, str] +reveal_type(x[0]) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] + +[case testHigherOrderFunctionUnpackTypeVarTupleViaParamSpec] +from typing import Callable, ParamSpec, TypeVar, TypeVarTuple, Unpack + +P = ParamSpec("P") +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +def call(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + return func(*args, **kwargs) + + +def run(func: Callable[[Unpack[Ts]], T], *args: Unpack[Ts], some_kwarg: str = "asdf") -> T: + raise + + +def foo() -> str: + return "hello" + + +# this is a false positive, but it no longer crashes +call(run, foo, some_kwarg="a") # E: Argument 1 to "call" has incompatible type "def [Ts`-1, T] run(func: def (*Unpack[Ts]) -> T, *args: Unpack[Ts], some_kwarg: str = ...) -> T"; expected "Callable[[Callable[[], str], str], str]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-unbound.test b/test-data/unit/check-typevar-unbound.test index ed6beaa100db0..587ae65773288 100644 --- a/test-data/unit/check-typevar-unbound.test +++ b/test-data/unit/check-typevar-unbound.test @@ -69,3 +69,50 @@ from typing import TypeVar T = TypeVar("T") def f(t: T) -> None: a, *b = t # E: "object" object is not iterable + +[case testTypeVarType] +from typing import Mapping, Type, TypeVar, Union +T = TypeVar("T") + +class A: ... +class B: ... + +lookup_table: Mapping[str, Type[Union[A,B]]] +def load(lookup_table: Mapping[str, Type[T]], lookup_key: str) -> T: + ... +reveal_type(load(lookup_table, "a")) # N: Revealed type is "Union[__main__.A, __main__.B]" + +lookup_table_a: Mapping[str, Type[A]] +def load2(lookup_table: Mapping[str, Type[Union[T, int]]], lookup_key: str) -> T: + ... +reveal_type(load2(lookup_table_a, "a")) # N: Revealed type is "__main__.A" + +[builtins fixtures/tuple.pyi] + +[case testTypeVarTypeAssignment] +# Adapted from https://github.com/python/mypy/issues/12115 +from typing import TypeVar, Type, Callable, Union, Any + +t1: Type[bool] = bool +t2: Union[Type[bool], Type[str]] = bool + +T1 = TypeVar("T1", bound=Union[bool, str]) +def foo1(t: Type[T1]) -> None: ... +foo1(t1) +foo1(t2) + +T2 = TypeVar("T2", bool, str) +def foo2(t: Type[T2]) -> None: ... +foo2(t1) +# Rejected correctly: T2 cannot be Union[bool, str] +foo2(t2) # E: Value of type variable "T2" of "foo2" cannot be "Union[bool, str]" + +T3 = TypeVar("T3") +def foo3(t: Type[T3]) -> None: ... +foo3(t1) +foo3(t2) + +def foo4(t: Type[Union[bool, str]]) -> None: ... +foo4(t1) +foo4(t2) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 8b961d88d23d9..1be75c0f47061 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -20,8 +20,8 @@ if int(): i = f(1) s = f('') o = f(1) \ - # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]") \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ + # E: Incompatible types in assignment (expression has type "list[int]", variable has type "list[object]") \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] @@ -592,11 +592,10 @@ class C: def f(self, x: T) -> T: L = List[S] y: L[C.T] = [x] - C.T # E: Type variable "C.T" cannot be used as an expression - A = C.T # E: Type variable "C.T" cannot be used as an expression + reveal_type(C.T) # N: Revealed type is "typing.TypeVar" return y[0] - [builtins fixtures/list.pyi] +[typing fixtures/typing-full.pyi] [case testTypeVarWithAnyTypeBound] # flags: --follow-imports=skip @@ -633,8 +632,7 @@ def f(x: S) -> None: h(x) [case testTypeVarWithTypedDictBoundInIndexExpression] -from typing import TypeVar -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar class Data(TypedDict): x: int @@ -645,11 +643,11 @@ T = TypeVar("T", bound=Data) def f(data: T) -> None: reveal_type(data["x"]) # N: Revealed type is "builtins.int" -[builtins fixtures/tuple.pyi] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarWithUnionTypedDictBoundInIndexExpression] -from typing import TypeVar, Union, Dict -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Dict class Data(TypedDict): x: int @@ -661,10 +659,10 @@ T = TypeVar("T", bound=Union[Data, Dict[str, str]]) def f(data: T) -> None: reveal_type(data["x"]) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testTypeVarWithTypedDictValueInIndexExpression] -from typing import TypeVar, Union, Dict -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Dict class Data(TypedDict): x: int @@ -676,10 +674,10 @@ T = TypeVar("T", Data, Dict[str, str]) def f(data: T) -> None: _: Union[str, int] = data["x"] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testSelfTypeVarIndexExpr] -from typing import TypeVar, Union, Type -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar, Union, Type T = TypeVar("T", bound="Indexable") @@ -697,6 +695,7 @@ class Indexable: def m(self: T) -> T: return self["foo"] [builtins fixtures/classmethod.pyi] +[typing fixtures/typing-full.pyi] [case testTypeVarWithValueDeferral] from typing import TypeVar, Callable @@ -732,3 +731,16 @@ def foo3(x: NT) -> None: def foo4(x: NT) -> None: p, q = 1, 2.0 # type: (int, float) [builtins fixtures/tuple.pyi] + +[case testTypeVarValuesNarrowing] +from typing import TypeVar + +W = TypeVar("W", int, str) + +def fn(w: W) -> W: + if type(w) is str: + reveal_type(w) # N: Revealed type is "builtins.str" + elif type(w) is int: + reveal_type(w) # N: Revealed type is "builtins.int" + return w +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-union-error-syntax.test b/test-data/unit/check-union-error-syntax.test index 2928cc3127096..e938598aaefe0 100644 --- a/test-data/unit/check-union-error-syntax.test +++ b/test-data/unit/check-union-error-syntax.test @@ -30,32 +30,50 @@ x = 3 # E: Incompatible types in assignment (expression has type "int", variable [case testLiteralOrErrorSyntax] # flags: --python-version 3.10 --no-force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], Literal[2], str] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1, 2] | str") [builtins fixtures/tuple.pyi] [case testLiteralUnionErrorSyntax] # flags: --python-version 3.10 --force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], Literal[2], str] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Union[str, Literal[1, 2]]") [builtins fixtures/tuple.pyi] [case testLiteralOrNoneErrorSyntax] # flags: --python-version 3.10 --no-force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], None] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1] | None") [builtins fixtures/tuple.pyi] [case testLiteralOptionalErrorSyntax] # flags: --python-version 3.10 --force-union-syntax -from typing import Union -from typing_extensions import Literal +from typing import Literal, Union x : Union[Literal[1], None] x = 3 # E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Optional[Literal[1]]") [builtins fixtures/tuple.pyi] + +[case testUnionSyntaxRecombined] +# flags: --python-version 3.10 --force-union-syntax --allow-redefinition-new --local-partial-types +# The following revealed type is recombined because the finally body is visited twice. +try: + x = 1 + x = "" + x = {1: ""} +finally: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.dict[builtins.int, builtins.str]]" +[builtins fixtures/isinstancelist.pyi] + +[case testOrSyntaxRecombined] +# flags: --python-version 3.10 --no-force-union-syntax --allow-redefinition-new --local-partial-types +# The following revealed type is recombined because the finally body is visited twice. +try: + x = 1 + x = "" + x = {1: ""} +finally: + reveal_type(x) # N: Revealed type is "builtins.int | builtins.str | builtins.dict[builtins.int, builtins.str]" +[builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index fcf679fff4014..35af44c628002 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -45,9 +45,10 @@ reveal_type(f) # N: Revealed type is "def (x: Union[__main__.A, __main__.B, __m [case testUnionOrSyntaxWithLiteral] # flags: --python-version 3.10 -from typing_extensions import Literal +from typing import Literal reveal_type(Literal[4] | str) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testUnionOrSyntaxWithBadOperator] # flags: --python-version 3.10 @@ -66,8 +67,7 @@ x: List[int | str] reveal_type(x) # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]" [builtins fixtures/list.pyi] -[case testUnionOrSyntaxWithQuotedFunctionTypesPre310] -# flags: --python-version 3.9 +[case testUnionOrSyntaxWithQuotedFunctionTypes] from typing import Union def f(x: 'Union[int, str, None]') -> 'Union[int, None]': reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" @@ -79,8 +79,7 @@ def g(x: "int | str | None") -> "int | None": return 42 reveal_type(g) # N: Revealed type is "def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]" -[case testUnionOrSyntaxWithQuotedVariableTypesPre310] -# flags: --python-version 3.9 +[case testUnionOrSyntaxWithQuotedVariableTypes] y: "int | str" = 42 reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" @@ -108,7 +107,7 @@ b: X # E: Variable "__main__.X" is not valid as a type \ from __future__ import annotations from typing import List T = int | str # E: Invalid type alias: expression is not a valid type \ - # E: Unsupported left operand type for | ("Type[int]") + # E: Unsupported left operand type for | ("type[int]") class C(List[int | str]): # E: Type expected within [...] \ # E: Invalid base class "List" pass @@ -136,7 +135,6 @@ x: int | None x: int | None # E: X | Y syntax for unions requires Python 3.10 [case testUnionOrSyntaxInStubFile] -# flags: --python-version 3.9 from lib import x [file lib.pyi] x: int | None @@ -180,13 +178,12 @@ def f(x: int | str | C) -> None: def g(x: int | str | tuple[int, str] | C) -> None: if isinstance(x, int | str | tuple): - reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, Tuple[builtins.int, builtins.str]]" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, tuple[builtins.int, builtins.str]]" else: reveal_type(x) # N: Revealed type is "__main__.C" [builtins fixtures/isinstance_python3_10.pyi] [case testUnionOrSyntaxInIsinstanceNotSupported] -# flags: --python-version 3.9 from typing import Union def f(x: Union[int, str, None]) -> None: if isinstance(x, int | str): diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 329896f7a1a7b..398b007ce57d0 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -347,7 +347,7 @@ C = NamedTuple('C', [('x', int)]) def foo(a: Union[A, B, C]): if isinstance(a, (B, C)): - reveal_type(a) # N: Revealed type is "Union[Tuple[builtins.int, fallback=__main__.B], Tuple[builtins.int, fallback=__main__.C]]" + reveal_type(a) # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.B], tuple[builtins.int, fallback=__main__.C]]" a.x a.y # E: Item "B" of "Union[B, C]" has no attribute "y" \ # E: Item "C" of "Union[B, C]" has no attribute "y" @@ -378,20 +378,20 @@ t_s: Type[str] t_a: Type[Any] # Two identical items -reveal_type(u(t_o, t_o)) # N: Revealed type is "Type[builtins.object]" -reveal_type(u(t_s, t_s)) # N: Revealed type is "Type[builtins.str]" -reveal_type(u(t_a, t_a)) # N: Revealed type is "Type[Any]" +reveal_type(u(t_o, t_o)) # N: Revealed type is "type[builtins.object]" +reveal_type(u(t_s, t_s)) # N: Revealed type is "type[builtins.str]" +reveal_type(u(t_a, t_a)) # N: Revealed type is "type[Any]" reveal_type(u(type, type)) # N: Revealed type is "def (x: builtins.object) -> builtins.type" # One type, other non-type -reveal_type(u(t_s, 1)) # N: Revealed type is "Union[builtins.int, Type[builtins.str]]" -reveal_type(u(1, t_s)) # N: Revealed type is "Union[Type[builtins.str], builtins.int]" +reveal_type(u(t_s, 1)) # N: Revealed type is "Union[builtins.int, type[builtins.str]]" +reveal_type(u(1, t_s)) # N: Revealed type is "Union[type[builtins.str], builtins.int]" reveal_type(u(type, 1)) # N: Revealed type is "Union[builtins.int, def (x: builtins.object) -> builtins.type]" reveal_type(u(1, type)) # N: Revealed type is "Union[def (x: builtins.object) -> builtins.type, builtins.int]" -reveal_type(u(t_a, 1)) # N: Revealed type is "Union[builtins.int, Type[Any]]" -reveal_type(u(1, t_a)) # N: Revealed type is "Union[Type[Any], builtins.int]" -reveal_type(u(t_o, 1)) # N: Revealed type is "Union[builtins.int, Type[builtins.object]]" -reveal_type(u(1, t_o)) # N: Revealed type is "Union[Type[builtins.object], builtins.int]" +reveal_type(u(t_a, 1)) # N: Revealed type is "Union[builtins.int, type[Any]]" +reveal_type(u(1, t_a)) # N: Revealed type is "Union[type[Any], builtins.int]" +reveal_type(u(t_o, 1)) # N: Revealed type is "Union[builtins.int, type[builtins.object]]" +reveal_type(u(1, t_o)) # N: Revealed type is "Union[type[builtins.object], builtins.int]" [case testSimplifyingUnionWithTypeTypes2] from typing import TypeVar, Union, Type, Any @@ -414,12 +414,12 @@ reveal_type(u(t_a, object())) # N: Revealed type is "builtins.object" reveal_type(u(object(), t_a)) # N: Revealed type is "builtins.object" # Union between type objects -reveal_type(u(t_o, t_a)) # N: Revealed type is "Union[Type[Any], Type[builtins.object]]" -reveal_type(u(t_a, t_o)) # N: Revealed type is "Union[Type[builtins.object], Type[Any]]" -reveal_type(u(t_s, t_o)) # N: Revealed type is "Type[builtins.object]" -reveal_type(u(t_o, t_s)) # N: Revealed type is "Type[builtins.object]" -reveal_type(u(t_o, type)) # N: Revealed type is "Type[builtins.object]" -reveal_type(u(type, t_o)) # N: Revealed type is "Type[builtins.object]" +reveal_type(u(t_o, t_a)) # N: Revealed type is "Union[type[Any], type[builtins.object]]" +reveal_type(u(t_a, t_o)) # N: Revealed type is "Union[type[builtins.object], type[Any]]" +reveal_type(u(t_s, t_o)) # N: Revealed type is "type[builtins.object]" +reveal_type(u(t_o, t_s)) # N: Revealed type is "type[builtins.object]" +reveal_type(u(t_o, type)) # N: Revealed type is "type[builtins.object]" +reveal_type(u(type, t_o)) # N: Revealed type is "type[builtins.object]" reveal_type(u(t_a, t)) # N: Revealed type is "builtins.type" reveal_type(u(t, t_a)) # N: Revealed type is "builtins.type" # The following should arguably not be simplified, but it's unclear how to fix then @@ -444,8 +444,8 @@ t_a: Type[A] reveal_type(u(M(*a), t_a)) # N: Revealed type is "__main__.M" reveal_type(u(t_a, M(*a))) # N: Revealed type is "__main__.M" -reveal_type(u(M2(*a), t_a)) # N: Revealed type is "Union[Type[__main__.A], __main__.M2]" -reveal_type(u(t_a, M2(*a))) # N: Revealed type is "Union[__main__.M2, Type[__main__.A]]" +reveal_type(u(M2(*a), t_a)) # N: Revealed type is "Union[type[__main__.A], __main__.M2]" +reveal_type(u(t_a, M2(*a))) # N: Revealed type is "Union[__main__.M2, type[__main__.A]]" [case testSimplifyUnionWithCallable] from typing import TypeVar, Union, Any, Callable @@ -528,7 +528,6 @@ x: Union[int, str] a: Any if bool(): x = a - # TODO: Maybe we should infer Any as the type instead. reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/bool.pyi] @@ -773,7 +772,7 @@ good: Union[Tuple[int, int], Tuple[str, str]] x, y = t = good reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" -reveal_type(t) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]" +reveal_type(t) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.str, builtins.str]]" [builtins fixtures/tuple.pyi] [out] @@ -784,7 +783,7 @@ good: Union[Tuple[int, int], Tuple[str, str]] t = x, y = good reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" -reveal_type(t) # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.str, builtins.str]]" +reveal_type(t) # N: Revealed type is "Union[tuple[builtins.int, builtins.int], tuple[builtins.str, builtins.str]]" [builtins fixtures/tuple.pyi] [out] @@ -935,7 +934,7 @@ a: Any d: Dict[str, Tuple[List[Tuple[str, str]], str]] x, _ = d.get(a, (None, None)) -for y in x: pass # E: Item "None" of "Optional[List[Tuple[str, str]]]" has no attribute "__iter__" (not iterable) +for y in x: pass # E: Item "None" of "Optional[list[tuple[str, str]]]" has no attribute "__iter__" (not iterable) if x: for s, t in x: reveal_type(s) # N: Revealed type is "builtins.str" @@ -950,7 +949,7 @@ x = None d: Dict[str, Tuple[List[Tuple[str, str]], str]] x, _ = d.get(a, (None, None)) -for y in x: pass # E: Item "None" of "Optional[List[Tuple[str, str]]]" has no attribute "__iter__" (not iterable) +for y in x: pass # E: Item "None" of "Optional[list[tuple[str, str]]]" has no attribute "__iter__" (not iterable) if x: for s, t in x: reveal_type(s) # N: Revealed type is "builtins.str" @@ -964,7 +963,7 @@ x: object a: Any d: Dict[str, Tuple[List[Tuple[str, str]], str]] x, _ = d.get(a, (None, None)) -reveal_type(x) # N: Revealed type is "Union[builtins.list[Tuple[builtins.str, builtins.str]], None]" +reveal_type(x) # N: Revealed type is "Union[builtins.list[tuple[builtins.str, builtins.str]], None]" if x: for y in x: pass @@ -977,7 +976,7 @@ from typing import Dict, Tuple, List, Any a: Any d: Dict[str, Tuple[List[Tuple[str, str]], str]] x, _ = d.get(a, ([], "")) -reveal_type(x) # N: Revealed type is "builtins.list[Tuple[builtins.str, builtins.str]]" +reveal_type(x) # N: Revealed type is "builtins.list[tuple[builtins.str, builtins.str]]" for y in x: pass [builtins fixtures/dict.pyi] @@ -1049,8 +1048,8 @@ class Boop(Enum): def do_thing_with_enums(enums: Union[List[Enum], Enum]) -> None: ... boop: List[Boop] = [] -do_thing_with_enums(boop) # E: Argument 1 to "do_thing_with_enums" has incompatible type "List[Boop]"; expected "Union[List[Enum], Enum]" \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +do_thing_with_enums(boop) # E: Argument 1 to "do_thing_with_enums" has incompatible type "list[Boop]"; expected "Union[list[Enum], Enum]" \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/isinstancelist.pyi] @@ -1254,7 +1253,7 @@ class B: field_2: Mapped[str] = Mapped('2') mix: Union[Type[A], Type[B]] = A -reveal_type(mix) # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]" +reveal_type(mix) # N: Revealed type is "Union[type[__main__.A], type[__main__.B]]" reveal_type(mix.field_1) # N: Revealed type is "builtins.list[builtins.int]" reveal_type(mix().field_1) # N: Revealed type is "builtins.int" [builtins fixtures/list.pyi] @@ -1346,3 +1345,30 @@ x: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11] y: Union[C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, None] x = y # E: Incompatible types in assignment (expression has type "Union[C1, C2, C3, C4, C5, <6 more items>, None]", variable has type "Union[C1, C2, C3, C4, C5, <6 more items>]") \ # N: Item in the first union not in the second: "None" + +[case testTypeAliasWithOldUnionIsInstance] +# flags: --python-version 3.10 +from typing import Union +SimpleAlias = Union[int, str] + +def foo(x: Union[int, str, tuple]): + # TODO: fix the typeshed stub for isinstance + if isinstance(x, SimpleAlias): # E: Argument 2 to "isinstance" has incompatible type ""; expected "type" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]" + else: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]" +[builtins fixtures/tuple.pyi] + + +[case testTypeAliasWithOldUnionIsInstancePython39] +# flags: --python-version 3.9 +from typing import Union +SimpleAlias = Union[int, str] + +def foo(x: Union[int, str, tuple]): + if isinstance(x, SimpleAlias): # E: Parameterized generics cannot be used with class or instance checks \ + # E: Argument 2 to "isinstance" has incompatible type ""; expected "type" + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]" + else: + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, builtins.tuple[Any, ...]]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index cbad1bd5449ed..34d800404903d 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -481,25 +481,101 @@ import typing def make() -> bool: pass PY2 = PY3 = make() -a = PY2 and 's' -b = PY3 and 's' -c = PY2 or 's' -d = PY3 or 's' -e = (PY2 or PY3) and 's' -f = (PY3 or PY2) and 's' -g = (PY2 or PY3) or 's' -h = (PY3 or PY2) or 's' +a = PY2 and str() +b = PY3 and str() +c = PY2 or str() +d = PY3 or str() +e = (PY2 or PY3) and str() +f = (PY3 or PY2) and str() +g = (PY2 or PY3) or str() +h = (PY3 or PY2) or str() reveal_type(a) # N: Revealed type is "builtins.bool" -reveal_type(b) # N: Revealed type is "Literal['s']" -reveal_type(c) # N: Revealed type is "Literal['s']" +reveal_type(b) # N: Revealed type is "builtins.str" +reveal_type(c) # N: Revealed type is "builtins.str" reveal_type(d) # N: Revealed type is "builtins.bool" -reveal_type(e) # N: Revealed type is "Literal['s']" -reveal_type(f) # N: Revealed type is "Literal['s']" +reveal_type(e) # N: Revealed type is "builtins.str" +reveal_type(f) # N: Revealed type is "builtins.str" reveal_type(g) # N: Revealed type is "builtins.bool" reveal_type(h) # N: Revealed type is "builtins.bool" [builtins fixtures/ops.pyi] [out] +[case testConditionalValuesBinaryOps] +# flags: --platform linux +import sys + +t_and_t = (sys.platform == 'linux' and sys.platform == 'linux') and str() +t_or_t = (sys.platform == 'linux' or sys.platform == 'linux') and str() +t_and_f = (sys.platform == 'linux' and sys.platform == 'windows') and str() +t_or_f = (sys.platform == 'linux' or sys.platform == 'windows') and str() +f_and_t = (sys.platform == 'windows' and sys.platform == 'linux') and str() +f_or_t = (sys.platform == 'windows' or sys.platform == 'linux') and str() +f_and_f = (sys.platform == 'windows' and sys.platform == 'windows') and str() +f_or_f = (sys.platform == 'windows' or sys.platform == 'windows') and str() +reveal_type(t_and_t) # N: Revealed type is "builtins.str" +reveal_type(t_or_t) # N: Revealed type is "builtins.str" +reveal_type(f_and_t) # N: Revealed type is "builtins.bool" +reveal_type(f_or_t) # N: Revealed type is "builtins.str" +reveal_type(t_and_f) # N: Revealed type is "builtins.bool" +reveal_type(t_or_f) # N: Revealed type is "builtins.str" +reveal_type(f_and_f) # N: Revealed type is "builtins.bool" +reveal_type(f_or_f) # N: Revealed type is "builtins.bool" +[builtins fixtures/ops.pyi] + +[case testConditionalValuesNegation] +# flags: --platform linux +import sys + +not_t = not sys.platform == 'linux' and str() +not_f = not sys.platform == 'windows' and str() +not_and_t = not (sys.platform == 'linux' and sys.platform == 'linux') and str() +not_and_f = not (sys.platform == 'linux' and sys.platform == 'windows') and str() +not_or_t = not (sys.platform == 'linux' or sys.platform == 'linux') and str() +not_or_f = not (sys.platform == 'windows' or sys.platform == 'windows') and str() +reveal_type(not_t) # N: Revealed type is "builtins.bool" +reveal_type(not_f) # N: Revealed type is "builtins.str" +reveal_type(not_and_t) # N: Revealed type is "builtins.bool" +reveal_type(not_and_f) # N: Revealed type is "builtins.str" +reveal_type(not_or_t) # N: Revealed type is "builtins.bool" +reveal_type(not_or_f) # N: Revealed type is "builtins.str" +[builtins fixtures/ops.pyi] + +[case testConditionalValuesUnsupportedOps] +# flags: --platform linux +import sys + +unary_minus = -(sys.platform == 'linux') and str() +binary_minus = ((sys.platform == 'linux') - (sys.platform == 'linux')) and str() +reveal_type(unary_minus) # N: Revealed type is "Union[Literal[0], builtins.str]" +reveal_type(binary_minus) # N: Revealed type is "Union[Literal[0], builtins.str]" +[builtins fixtures/ops.pyi] + +[case testMypyFalseValuesInBinaryOps_no_empty] +# flags: --platform linux +import sys +from typing import TYPE_CHECKING + +MYPY = 0 + +if TYPE_CHECKING and sys.platform == 'linux': + def foo1() -> int: ... +if sys.platform == 'linux' and TYPE_CHECKING: + def foo2() -> int: ... +if MYPY and sys.platform == 'linux': + def foo3() -> int: ... +if sys.platform == 'linux' and MYPY: + def foo4() -> int: ... + +if TYPE_CHECKING or sys.platform == 'linux': + def bar1() -> int: ... # E: Missing return statement +if sys.platform == 'linux' or TYPE_CHECKING: + def bar2() -> int: ... # E: Missing return statement +if MYPY or sys.platform == 'linux': + def bar3() -> int: ... # E: Missing return statement +if sys.platform == 'linux' or MYPY: + def bar4() -> int: ... # E: Missing return statement +[builtins fixtures/ops.pyi] + [case testShortCircuitAndWithConditionalAssignment] # flags: --platform linux import sys @@ -702,6 +778,22 @@ assert sys.platform == 'lol' def bar() -> None: pass [builtins fixtures/ops.pyi] +[case testUnreachableAfterToplevelAssertImportThirdParty] +# flags: --platform unknown +import sys +assert sys.platform == 'linux' +import does_not_exist +[builtins fixtures/ops.pyi] + +[case testUnreachableAfterToplevelAssertImportThirdParty2] +# flags: --platform unknown +import sys +import bad; assert sys.platform == 'linux'; import does_not_exist +[builtins fixtures/ops.pyi] +[out] +main:3: error: Cannot find implementation or library stub for module named "bad" +main:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports + [case testUnreachableAfterToplevelAssertNotInsideIf] import sys if sys.version_info[0] >= 2: @@ -798,7 +890,7 @@ def baz(x: int) -> int: [builtins fixtures/exception.pyi] [case testUnreachableFlagIgnoresSemanticAnalysisUnreachable] -# flags: --warn-unreachable --python-version 3.8 --platform win32 --always-false FOOBAR +# flags: --warn-unreachable --python-version 3.9 --platform win32 --always-false FOOBAR import sys from typing import TYPE_CHECKING @@ -828,7 +920,7 @@ if sys.version_info == (2, 7): else: reveal_type(x) # N: Revealed type is "builtins.int" -if sys.version_info == (3, 8): +if sys.version_info == (3, 9): reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) @@ -1000,11 +1092,20 @@ 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 -from typing import Optional, Iterator, Any -from typing_extensions import Literal +from typing import Literal, Optional, Iterator, Any class DoesNotSuppress1: def __enter__(self) -> int: ... def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... @@ -1068,8 +1169,7 @@ def f_no_suppress_5() -> int: [case testUnreachableFlagContextManagersSuppressed] # flags: --warn-unreachable from contextlib import contextmanager -from typing import Optional, Iterator, Any -from typing_extensions import Literal +from typing import Optional, Iterator, Literal, Any class DoesNotSuppress: def __enter__(self) -> int: ... @@ -1115,8 +1215,7 @@ def f_mix() -> int: # E: Missing return statement [case testUnreachableFlagContextManagersSuppressedNoStrictOptional] # flags: --warn-unreachable --no-strict-optional from contextlib import contextmanager -from typing import Optional, Iterator, Any -from typing_extensions import Literal +from typing import Optional, Iterator, Literal, Any class DoesNotSuppress1: def __enter__(self) -> int: ... @@ -1157,8 +1256,7 @@ def f_suppress() -> int: # E: Missing return statement [case testUnreachableFlagContextAsyncManagersNoSuppress] # flags: --warn-unreachable from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator, Any -from typing_extensions import Literal +from typing import Optional, AsyncIterator, Literal, Any class DoesNotSuppress1: async def __aenter__(self) -> int: ... @@ -1223,8 +1321,7 @@ async def f_no_suppress_5() -> int: [case testUnreachableFlagContextAsyncManagersSuppressed] # flags: --warn-unreachable from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator, Any -from typing_extensions import Literal +from typing import Optional, AsyncIterator, Literal, Any class DoesNotSuppress: async def __aenter__(self) -> int: ... @@ -1270,8 +1367,7 @@ async def f_mix() -> int: # E: Missing return statement [case testUnreachableFlagContextAsyncManagersAbnormal] # flags: --warn-unreachable from contextlib import asynccontextmanager -from typing import Optional, AsyncIterator, Any -from typing_extensions import Literal +from typing import Optional, AsyncIterator, Literal, Any class RegularManager: def __enter__(self) -> int: ... @@ -1370,7 +1466,7 @@ def f(t: T) -> None: [case testUnreachableLiteral] # flags: --warn-unreachable -from typing_extensions import Literal +from typing import Literal def nope() -> Literal[False]: ... @@ -1381,7 +1477,7 @@ def f() -> None: [case testUnreachableLiteralFrom__bool__] # flags: --warn-unreachable -from typing_extensions import Literal +from typing import Literal class Truth: def __bool__(self) -> Literal[True]: ... @@ -1507,3 +1603,50 @@ x = 0 # not unreachable f2: Callable[[], NoReturn] = lambda: foo() x = 0 # not unreachable + +[case testAttributeNoReturn] +# flags: --warn-unreachable +from typing import Optional, NoReturn, TypeVar + +def foo() -> NoReturn: + raise + +T = TypeVar("T") +def bar(x: Optional[list[T]] = None) -> T: + ... + +reveal_type(bar().attr) # N: Revealed type is "Never" +1 # not unreachable +reveal_type(foo().attr) # N: Revealed type is "Never" +1 # E: Statement is unreachable + +[case testIgnoreReturningNotImplemented] +# flags: --warn-unreachable + +class C: + def __add__(self, o: C) -> C: + if not isinstance(o, C): + return NotImplemented + return C() + def __sub__(self, o: C) -> C: + if isinstance(o, C): + return C() + return NotImplemented + def __mul__(self, o: C) -> C: + if isinstance(o, C): + return C() + else: + return NotImplemented + +[builtins fixtures/isinstance.pyi] + +[case testUnreachableStatementPrettyHighlighting] +# flags: --warn-unreachable --pretty +def x() -> None: + assert False + if 5: + pass +[out] +main:4: error: Statement is unreachable + if 5: + ^~~~~ diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 2495a883aa715..3b80b9e8829aa 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -11,8 +11,8 @@ def f( *b: 'B') -> None: ab: Tuple[B, ...] ac: Tuple[C, ...] if int(): - b = ac # E: Incompatible types in assignment (expression has type "Tuple[C, ...]", variable has type "Tuple[B, ...]") - ac = b # E: Incompatible types in assignment (expression has type "Tuple[B, ...]", variable has type "Tuple[C, ...]") + b = ac # E: Incompatible types in assignment (expression has type "tuple[C, ...]", variable has type "tuple[B, ...]") + ac = b # E: Incompatible types in assignment (expression has type "tuple[B, ...]", variable has type "tuple[C, ...]") b = ab ab = b @@ -121,7 +121,7 @@ T4 = TypeVar('T4') def f(a: T1, b: T2, c: T3, d: T4) -> Tuple[T1, T2, T3, T4]: ... x: Tuple[int, str] y: Tuple[float, bool] -reveal_type(f(*x, *y)) # N: Revealed type is "Tuple[builtins.int, builtins.str, builtins.float, builtins.bool]" +reveal_type(f(*x, *y)) # N: Revealed type is "tuple[builtins.int, builtins.str, builtins.float, builtins.bool]" [builtins fixtures/list.pyi] [case testCallVarargsFunctionWithIterableAndPositional] @@ -141,10 +141,18 @@ it1 = (1, 2) it2 = ('',) f(*it1, 1, 2) f(*it1, 1, *it1, 2) -f(*it1, 1, *it2, 2) # E: Argument 3 to "f" has incompatible type "*Tuple[str]"; expected "int" +f(*it1, 1, *it2, 2) # E: Argument 3 to "f" has incompatible type "*tuple[str]"; expected "int" f(*it1, '') # E: Argument 2 to "f" has incompatible type "str"; expected "int" [builtins fixtures/for.pyi] +[case testCallVarArgsWithMatchingNamedArgument] +def foo(*args: int) -> None: ... # N: "foo" defined here +foo(args=1) # E: Unexpected keyword argument "args" for "foo" + +def bar(*args: int, **kwargs: str) -> None: ... +bar(args=1) # E: Argument "args" to "bar" has incompatible type "int"; expected "str" +[builtins fixtures/for.pyi] + -- Calling varargs function + type inference -- ----------------------------------------- @@ -235,7 +243,7 @@ ab: List[B] a: A b: B -f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" +f(*aa) # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B" f(a, *ab) # Ok f(a, b) (cast(Any, f))(*aa) # IDEA: Move to check-dynamic? @@ -254,9 +262,9 @@ b: B c: C cc: CC -f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B, B]"; expected "C" -f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "*Tuple[B, B, C]"; expected "A" -f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type "*Tuple[B, B]"; expected "C" +f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, B, B]"; expected "C" +f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "*tuple[B, B, C]"; expected "A" +f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type "*tuple[B, B]"; expected "C" f(b, *(b, c)) # E: Argument 1 to "f" has incompatible type "B"; expected "A" f(*(a, b)) # E: Missing positional arguments "b", "c" in call to "f" f(*(a, b, c, c)) # E: Too many arguments for "f" @@ -268,17 +276,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] @@ -298,13 +308,13 @@ aa: List[A] ab: List[B] a: A b: B -f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" -f(a, *aa) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B" +f(*aa) # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B" +f(a, *aa) # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B" f(b, *ab) # E: Argument 1 to "f" has incompatible type "B"; expected "A" f(a, a, *ab) # E: Argument 2 to "f" has incompatible type "A"; expected "B" -f(a, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" +f(a, b, *aa) # E: Argument 3 to "f" has incompatible type "*list[A]"; expected "B" f(b, b, *ab) # E: Argument 1 to "f" has incompatible type "B"; expected "A" -g(*ab) # E: Argument 1 to "g" has incompatible type "*List[B]"; expected "A" +g(*ab) # E: Argument 1 to "g" has incompatible type "*list[B]"; expected "A" f(a, *ab) f(a, b, *ab) f(a, b, b, *ab) @@ -324,14 +334,14 @@ b: B c: C cc: CC -f(*(b, b, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[B, B, B]"; expected "A" -f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "B" -f(*(a, b, a)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B, A]"; expected "B" -f(a, *(a, b)) # E: Argument 2 to "f" has incompatible type "*Tuple[A, B]"; expected "B" +f(*(b, b, b)) # E: Argument 1 to "f" has incompatible type "*tuple[B, B, B]"; expected "A" +f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, A, B]"; expected "B" +f(*(a, b, a)) # E: Argument 1 to "f" has incompatible type "*tuple[A, B, A]"; expected "B" +f(a, *(a, b)) # E: Argument 2 to "f" has incompatible type "*tuple[A, B]"; expected "B" f(b, *(b, b)) # E: Argument 1 to "f" has incompatible type "B"; expected "A" f(b, b, *(b,)) # E: Argument 1 to "f" has incompatible type "B"; expected "A" f(a, a, *(b,)) # E: Argument 2 to "f" has incompatible type "A"; expected "B" -f(a, b, *(a,)) # E: Argument 3 to "f" has incompatible type "*Tuple[A]"; expected "B" +f(a, b, *(a,)) # E: Argument 3 to "f" has incompatible type "*tuple[A]"; expected "B" f(*()) # E: Too few arguments for "f" f(*(a, b, b)) f(a, *(b, b)) @@ -374,7 +384,7 @@ class B(A): pass aa: List[A] ab: List[B] -g(*aa) # E: Argument 1 to "g" has incompatible type "*List[A]"; expected "B" +g(*aa) # E: Argument 1 to "g" has incompatible type "*list[A]"; expected "B" f(*aa) f(*ab) g(*ab) @@ -391,10 +401,10 @@ class B: pass a, b = None, None # type: (A, B) f(*()) # E: Too few arguments for "f" -f(a, *[a]) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "Optional[B]" \ - # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B" -f(a, b, *[a]) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" -f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "Optional[B]" +f(a, *[a]) # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "Optional[B]" \ + # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B" +f(a, b, *[a]) # E: Argument 3 to "f" has incompatible type "*list[A]"; expected "B" +f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, A, B]"; expected "Optional[B]" f(*(a,)) f(*(a, b)) f(*(a, b, b, b)) @@ -410,7 +420,7 @@ f(x=1, *[2]) [builtins fixtures/list.pyi] [out] main:3: error: "f" gets multiple values for keyword argument "x" -main:3: error: Argument 1 to "f" has incompatible type "*List[int]"; expected "str" +main:3: error: Argument 1 to "f" has incompatible type "*list[int]"; expected "str" [case testVarArgsAfterKeywordArgInCall2] # see: mypy issue #2729 @@ -419,7 +429,7 @@ f(y='x', *[1]) [builtins fixtures/list.pyi] [out] main:3: error: "f" gets multiple values for keyword argument "y" -main:3: error: Argument 1 to "f" has incompatible type "*List[int]"; expected "str" +main:3: error: Argument 1 to "f" has incompatible type "*list[int]"; expected "str" [case testVarArgsAfterKeywordArgInCall3] def f(x: int, y: str) -> None: pass @@ -533,19 +543,19 @@ b: B aa: List[A] if int(): - a, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" + a, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B" if int(): - b, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" + b, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*list[A]"; expected "B" if int(): a, a = f(b, *aa) # E: Argument 1 to "f" has incompatible type "B"; expected "A" if int(): - b, b = f(b, *aa) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B" + b, b = f(b, *aa) # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B" if int(): - b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" + 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) @@ -569,11 +579,11 @@ a: A b: B if int(): - a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" + a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, B]"; expected "A" if int(): b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B" if int(): - a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" + a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, B]"; expected "A" if int(): b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B" if int(): @@ -602,11 +612,11 @@ class A: pass class B: pass if int(): - a, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") + a, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "list[A]", variable has type "A") if int(): - aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[Never]", variable has type "A") + aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "list[Never]", variable has type "A") if int(): - ab, aa = G().f(*[a]) # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" + ab, aa = G().f(*[a]) # E: Argument 1 to "f" of "G" has incompatible type "*list[A]"; expected "B" if int(): ao, ao = G().f(*[a]) if int(): @@ -621,7 +631,7 @@ T = TypeVar('T') def f(*args: T) -> T: ... reveal_type(f(*(1, None))) # N: Revealed type is "Union[Literal[1]?, None]" reveal_type(f(1, *(None, 1))) # N: Revealed type is "Union[Literal[1]?, None]" -reveal_type(f(1, *(1, None))) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(f(1, *(1, None))) # N: Revealed type is "Union[Literal[1]?, None]" [builtins fixtures/tuple.pyi] @@ -650,7 +660,7 @@ x: Callable[[int], None] def f(*x: int) -> None: pass def g(*x: str) -> None: pass x = f -x = g # E: Incompatible types in assignment (expression has type "Callable[[VarArg(str)], None]", variable has type "Callable[[int], None]") +x = g # E: Incompatible types in assignment (expression has type "def g(*x: str) -> None", variable has type "Callable[[int], None]") [builtins fixtures/list.pyi] [out] @@ -676,15 +686,15 @@ a = {'a': [1, 2]} b = {'b': ['c', 'd']} c = {'c': 1.0} d = {'d': 1} -f(a) # E: Argument 1 to "f" has incompatible type "Dict[str, List[int]]"; expected "Dict[str, Sequence[int]]" \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +f(a) # E: Argument 1 to "f" has incompatible type "dict[str, list[int]]"; expected "dict[str, Sequence[int]]" \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type -f(b) # E: Argument 1 to "f" has incompatible type "Dict[str, List[str]]"; expected "Dict[str, Sequence[int]]" +f(b) # E: Argument 1 to "f" has incompatible type "dict[str, list[str]]"; expected "dict[str, Sequence[int]]" g(c) -g(d) # E: Argument 1 to "g" has incompatible type "Dict[str, int]"; expected "Dict[str, float]" \ - # N: "Dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +g(d) # E: Argument 1 to "g" has incompatible type "dict[str, int]"; expected "dict[str, float]" \ + # N: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Mapping" instead, which is covariant in the value type -h(c) # E: Argument 1 to "h" has incompatible type "Dict[str, float]"; expected "Dict[str, int]" +h(c) # E: Argument 1 to "h" has incompatible type "dict[str, float]"; expected "dict[str, int]" h(d) [builtins fixtures/dict.pyi] [typing fixtures/typing-medium.pyi] @@ -693,13 +703,13 @@ h(d) from typing import List, Union def f(numbers: List[Union[int, float]]) -> None: pass a = [1, 2] -f(a) # E: Argument 1 to "f" has incompatible type "List[int]"; expected "List[Union[int, float]]" \ - # N: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ +f(a) # E: Argument 1 to "f" has incompatible type "list[int]"; expected "list[Union[int, float]]" \ + # N: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance \ # N: Consider using "Sequence" instead, which is covariant x = [1] y = ['a'] if int(): - x = y # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]") + x = y # E: Incompatible types in assignment (expression has type "list[str]", variable has type "list[int]") [builtins fixtures/list.pyi] [case testInvariantTypeConfusingNames] @@ -710,8 +720,8 @@ def f(x: Listener) -> None: pass def g(y: DictReader) -> None: pass a = [1, 2] b = {'b': 1} -f(a) # E: Argument 1 to "f" has incompatible type "List[int]"; expected "Listener" -g(b) # E: Argument 1 to "g" has incompatible type "Dict[str, int]"; expected "DictReader" +f(a) # E: Argument 1 to "f" has incompatible type "list[int]"; expected "Listener" +g(b) # E: Argument 1 to "g" has incompatible type "dict[str, int]"; expected "DictReader" [builtins fixtures/dict.pyi] [case testInvariantTypeConfusingNames2] @@ -737,13 +747,14 @@ 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 [case testUnpackKwargsReveal] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -752,9 +763,11 @@ def foo(arg: bool, **kwargs: Unpack[Person]) -> None: ... reveal_type(foo) # N: Revealed type is "def (arg: builtins.bool, **kwargs: Unpack[TypedDict('__main__.Person', {'name': builtins.str, 'age': builtins.int})])" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackOutsideOfKwargs] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str age: int @@ -766,6 +779,7 @@ def bar(x: int, *args: Unpack[Person]) -> None: # E: "Person" cannot be unpacke def baz(**kwargs: Unpack[Person]) -> None: # OK ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackWithoutTypedDict] from typing_extensions import Unpack @@ -775,7 +789,8 @@ def foo(**kwargs: Unpack[dict]) -> None: # E: Unpack item in ** argument must b [builtins fixtures/dict.pyi] [case testUnpackWithDuplicateKeywords] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -783,10 +798,11 @@ class Person(TypedDict): def foo(name: str, **kwargs: Unpack[Person]) -> None: # E: Overlap between argument names and ** TypedDict items: "name" ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackWithDuplicateKeywordKwargs] -from typing_extensions import Unpack, TypedDict -from typing import Dict, List +from typing_extensions import Unpack +from typing import Dict, List, TypedDict class Spec(TypedDict): args: List[int] @@ -795,20 +811,24 @@ def foo(**kwargs: Unpack[Spec]) -> None: # Allowed ... foo(args=[1], kwargs={"2": 3}) # E: Dict entry 0 has incompatible type "str": "int"; expected "int": "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsNonIdentifier] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack Weird = TypedDict("Weird", {"@": int}) def foo(**kwargs: Unpack[Weird]) -> None: reveal_type(kwargs["@"]) # N: Revealed type is "builtins.int" foo(**{"@": 42}) -foo(**{"no": "way"}) # E: Argument 1 to "foo" has incompatible type "**Dict[str, str]"; expected "int" +foo(**{"no": "way"}) # E: Argument 1 to "foo" has incompatible type "**dict[str, str]"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsEmpty] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack Empty = TypedDict("Empty", {}) @@ -817,9 +837,11 @@ def foo(**kwargs: Unpack[Empty]) -> None: # N: "foo" defined here foo() foo(x=1) # E: Unexpected keyword argument "x" for "foo" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackTypedDictTotality] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Circle(TypedDict, total=True): radius: int @@ -839,9 +861,11 @@ def bar(**kwargs: Unpack[Square]): ... bar(side=12) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackUnexpectedKeyword] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict, total=False): name: str @@ -852,9 +876,11 @@ def foo(**kwargs: Unpack[Person]) -> None: # N: "foo" defined here foo(name='John', age=42, department='Sales') # E: Unexpected keyword argument "department" for "foo" foo(name='Jennifer', age=38) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKeywordTypes] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -865,9 +891,11 @@ def foo(**kwargs: Unpack[Person]): foo(name='John', age='42') # E: Argument "age" to "foo" has incompatible type "str"; expected "int" foo(name='Jennifer', age=38) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKeywordTypesTypedDict] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -882,9 +910,11 @@ def foo(**kwargs: Unpack[Person]) -> None: lp = LegacyPerson(name="test", age="42") foo(**lp) # E: Argument "age" to "foo" has incompatible type "str"; expected "int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testFunctionBodyWithUnpackedKwargs] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -896,9 +926,11 @@ def foo(**kwargs: Unpack[Person]) -> int: department: str = kwargs['department'] # E: TypedDict "Person" has no key "department" return kwargs['age'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsOverrides] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -913,9 +945,11 @@ class SubBad(Base): # N: This violates the Liskov substitution principle \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsOverridesTypedDict] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -938,10 +972,11 @@ class SubBad(Base): # N: Subclass: \ # N: def foo(self, *, baz: int) -> None [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsGeneric] -from typing import Generic, TypeVar -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar +from typing_extensions import Unpack T = TypeVar("T") class Person(TypedDict, Generic[T]): @@ -951,10 +986,11 @@ class Person(TypedDict, Generic[T]): def foo(**kwargs: Unpack[Person[T]]) -> T: ... reveal_type(foo(name="test", value=42)) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsInference] -from typing import Generic, TypeVar, Protocol -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar, Protocol +from typing_extensions import Unpack T_contra = TypeVar("T_contra", contravariant=True) class CBPerson(Protocol[T_contra]): @@ -970,10 +1006,11 @@ def test(cb: CBPerson[T]) -> T: ... def foo(*, name: str, value: int) -> None: ... reveal_type(test(foo)) # N: Revealed type is "builtins.int" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsOverload] -from typing import Any, overload -from typing_extensions import Unpack, TypedDict +from typing import TypedDict, Any, overload +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -992,9 +1029,11 @@ def foo(**kwargs: Any) -> Any: reveal_type(foo(sort="test", taste=999)) # N: Revealed type is "builtins.str" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsJoin] -from typing_extensions import Unpack, TypedDict +from typing import TypedDict +from typing_extensions import Unpack class Person(TypedDict): name: str @@ -1006,10 +1045,11 @@ def bar(**kwargs: Unpack[Person]) -> None: ... reveal_type([foo, bar]) # N: Revealed type is "builtins.list[def (*, name: builtins.str, age: builtins.int)]" reveal_type([bar, foo]) # N: Revealed type is "builtins.list[def (*, name: builtins.str, age: builtins.int)]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackKwargsParamSpec] -from typing import Callable, Any, TypeVar, List -from typing_extensions import ParamSpec, Unpack, TypedDict +from typing import Callable, Any, TypedDict, TypeVar, List +from typing_extensions import ParamSpec, Unpack class Person(TypedDict): name: str @@ -1025,10 +1065,11 @@ def g(**kwargs: Unpack[Person]) -> int: ... reveal_type(g) # N: Revealed type is "def (*, name: builtins.str, age: builtins.int) -> builtins.list[builtins.int]" [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackGenericTypedDictImplicitAnyEnabled] -from typing import Generic, TypeVar -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar +from typing_extensions import Unpack T = TypeVar("T") class TD(TypedDict, Generic[T]): @@ -1039,11 +1080,12 @@ def foo(**kwds: Unpack[TD]) -> None: ... # Same as `TD[Any]` foo(key="yes", value=42) foo(key="yes", value="ok") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackGenericTypedDictImplicitAnyDisabled] # flags: --disallow-any-generics -from typing import Generic, TypeVar -from typing_extensions import Unpack, TypedDict +from typing import Generic, TypedDict, TypeVar +from typing_extensions import Unpack T = TypeVar("T") class TD(TypedDict, Generic[T]): @@ -1054,6 +1096,7 @@ def foo(**kwds: Unpack[TD]) -> None: ... # E: Missing type parameters for gener foo(key="yes", value=42) foo(key="yes", value="ok") [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [case testUnpackNoCrashOnEmpty] from typing_extensions import Unpack @@ -1065,8 +1108,8 @@ class D: [builtins fixtures/dict.pyi] [case testUnpackInCallableType] -from typing import Callable -from typing_extensions import Unpack, TypedDict +from typing import Callable, TypedDict +from typing_extensions import Unpack class TD(TypedDict): key: str @@ -1078,3 +1121,4 @@ foo(key="yes", value="ok") bad: Callable[[*TD], None] # E: "TD" cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index 90f40777d6b7a..a2d201fa301d9 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -42,6 +42,32 @@ a: Any b = cast(Any, a) [builtins fixtures/list.pyi] +[case testCastToObjectNotRedunant] +# flags: --warn-redundant-casts +from typing import cast + +a = 1 +b = cast(object, 1) + +[case testCastFromLiteralRedundant] +# flags: --warn-redundant-casts +from typing import cast + +cast(int, 1) +[out] +main:4: error: Redundant cast to "int" + +[case testCastFromUnionOfAnyOk] +# flags: --warn-redundant-casts +from typing import Any, cast, Union + +x = Any +y = Any +z = Any + +def f(q: Union[x, y, z]) -> None: + cast(Union[x, y], q) + -- Unused 'type: ignore' comments -- ------------------------------ @@ -181,7 +207,7 @@ def g() -> Any: pass def f() -> typ: return g() [builtins fixtures/tuple.pyi] [out] -main:11: error: Returning Any from function declared to return "Tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int]" +main:11: error: Returning Any from function declared to return "tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int]" [case testReturnAnySilencedFromTypedFunction] # flags: --warn-return-any diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test index 831bce2eb63d1..68dfacb372fba 100644 --- a/test-data/unit/cmdline.pyproject.test +++ b/test-data/unit/cmdline.pyproject.test @@ -92,7 +92,7 @@ exclude = '''(?x)( [file x/__init__.py] i: int = 0 [file x/_skipme_please.py] -This isn't even syntatically valid! +This isn't even syntactically valid! [file x/please_skipme_.py] Neither is this! @@ -107,7 +107,7 @@ exclude = """(?x)( [file x/__init__.py] i: int = 0 [file x/_skipme_please.py] -This isn't even syntatically valid! +This isn't even syntactically valid! [file x/please_skipme_.py] Neither is this! @@ -122,7 +122,7 @@ exclude = [ [file x/__init__.py] i: int = 0 [file x/_skipme_please.py] -This isn't even syntatically valid! +This isn't even syntactically valid! [file x/please_skipme_.py] Neither is this! @@ -133,3 +133,107 @@ Neither is this! description = "Factory ⸻ A code generator 🏭" \[tool.mypy] [file x.py] + +[case testPyprojectFilesTrailingComma] +# cmd: mypy +[file pyproject.toml] +\[tool.mypy] +# We combine multiple tests in a single one here, because these tests are slow. +files = """ + a.py, + b.py, +""" +always_true = """ + FLAG_A1, + FLAG_B1, +""" +always_false = """ + FLAG_A2, + FLAG_B2, +""" +[file a.py] +x: str = 'x' # ok' + +# --always-true +FLAG_A1 = False +FLAG_B1 = False +if not FLAG_A1: # unreachable + x: int = 'x' +if not FLAG_B1: # unreachable + y: int = 'y' + +# --always-false +FLAG_A2 = True +FLAG_B2 = True +if FLAG_A2: # unreachable + x: int = 'x' +if FLAG_B2: # unreachable + y: int = 'y' +[file b.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + +[case testPyprojectModulesTrailingComma] +# cmd: mypy +[file pyproject.toml] +\[tool.mypy] +# We combine multiple tests in a single one here, because these tests are slow. +modules = """ + a, + b, +""" +disable_error_code = """ + operator, + import, +""" +enable_error_code = """ + redundant-expr, + ignore-without-code, +""" +[file a.py] +x: str = 'x' # ok + +# --enable-error-code +a: int = 'a' # type: ignore + +# --disable-error-code +'a' + 1 +[file b.py] +y: int = 'y' +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] +b.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int") +a.py:4: error: "type: ignore" comment without error code (consider "type: ignore[assignment]" instead) + +[case testPyprojectPackagesTrailingComma] +# cmd: mypy +[file pyproject.toml] +\[tool.mypy] +packages = """ + a, + b, +""" +[file a/__init__.py] +x: str = 'x' # ok +[file b/__init__.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c/__init__.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + +[case testPyprojectTOMLSettingOfWrongType] +# cmd: mypy a.py +[file pyproject.toml] +\[tool.mypy] +enable_error_code = true +[file a.py] +x: int = 1 +[out] +pyproject.toml: [mypy]: enable_error_code: Expected a list or a stringified version thereof, but got: 'True', of type bool. +== Return code: 0 diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 2262b7e7280c2..bed4134bff727 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 @@ -592,7 +385,7 @@ main.py:1: error: Cannot find implementation or library stub for module named "a \[tool.mypy] python_version = 3.10 [out] -pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.8 or higher). You may need to put quotes around your Python version +pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.9 or higher). You may need to put quotes around your Python version == Return code: 0 [case testPythonVersionTooOld10] @@ -604,13 +397,13 @@ python_version = 1.0 mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be 3) == Return code: 0 -[case testPythonVersionTooOld37] +[case testPythonVersionTooOld38] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.7 +python_version = 3.8 [out] -mypy.ini: [mypy]: python_version: Python 3.7 is not supported (must be 3.8 or higher) +mypy.ini: [mypy]: python_version: Python 3.8 is not supported (must be 3.9 or higher) == Return code: 0 [case testPythonVersionTooNew40] @@ -633,20 +426,34 @@ usage: mypy [-h] [-v] [-V] [more options; see below] mypy: error: Mypy no longer supports checking Python 2 code. Consider pinning to mypy<0.980 if you need to check Python 2 code. == Return code: 2 -[case testPythonVersionAccepted38] +[case testPythonVersionAccepted39] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.8 +python_version = 3.9 [out] -[case testPythonVersionAccepted311] +[case testPythonVersionAccepted314] # cmd: mypy -c pass [file mypy.ini] \[mypy] -python_version = 3.11 +python_version = 3.14 [out] +[case testPythonVersionFallback] +# cmd: mypy main.py +[file main.py] +import sys +if sys.version_info == (3, 9): # Update here when bumping the min Python version! + reveal_type("good") +[file mypy.ini] +\[mypy] +python_version = 3.8 +[out] +mypy.ini: [mypy]: python_version: Python 3.8 is not supported (must be 3.9 or higher) +main.py:3: note: Revealed type is "Literal['good']?" +== Return code: 0 + -- This should be a dumping ground for tests of plugins that are sensitive to -- typeshed changes. [case testTypeshedSensitivePlugins] @@ -676,28 +483,16 @@ int_pow.py:10: note: Revealed type is "builtins.int" int_pow.py:11: note: Revealed type is "Any" == Return code: 0 -[case testDisallowAnyGenericsBuiltinCollectionsPre39] +[case testDisallowAnyGenericsBuiltinCollections] # cmd: mypy m.py [file mypy.ini] \[mypy] -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: Missing type parameters for generic type "frozenset" [case testDisallowAnyGenericsTypingCollections] # cmd: mypy m.py @@ -705,21 +500,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 @@ -754,19 +539,7 @@ strict_optional = True ignore_errors = False [out] 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 +a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None"; expected "list[Any]" [case testMissingFile] # cmd: mypy nope.py @@ -775,26 +548,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] @@ -911,9 +664,26 @@ def foo() -> str: return 9 [out] 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") +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] @@ -1074,8 +844,8 @@ x = [] # type: List[float] y = [] # type: List[int] x = y [out] -bad.py:4: error: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") -bad.py:4: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +bad.py:4: error: Incompatible types in assignment (expression has type "list[int]", variable has type "list[float]") +bad.py:4: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance bad.py:4: note: Consider using "Sequence" instead, which is covariant Found 1 error in 1 file (checked 1 source file) @@ -1139,7 +909,7 @@ some_file.py:11: error: Argument 1 to "some_interesting_method" of [file some_file.py] it_looks_like_we_started_typing_something_but_then. = did_not_notice(an_extra_dot) [out] -some_file.py:1: error: invalid syntax [syntax] +some_file.py:1: error: Invalid syntax [syntax] ...ooks_like_we_started_typing_something_but_then. = did_not_notice(an_ex... ^ == Return code: 2 @@ -1190,8 +960,6 @@ src/foo/bar.py: note: Common resolutions include: a) adding `__init__.py` somewh [file test.py] x = 1 [out] -usage: mypy [-h] [-v] [-V] [more options; see below] - [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] mypy: error: Invalid error code(s): YOLO == Return code: 2 @@ -1200,8 +968,6 @@ mypy: error: Invalid error code(s): YOLO [file test.py] x = 1 [out] -usage: mypy [-h] [-v] [-V] [more options; see below] - [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] mypy: error: Invalid error code(s): YOLO == Return code: 2 @@ -1210,8 +976,6 @@ mypy: error: Invalid error code(s): YOLO [file test.py] x = 1 [out] -usage: mypy [-h] [-v] [-V] [more options; see below] - [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] mypy: error: Invalid error code(s): YOLO, YOLO2 == Return code: 2 @@ -1220,8 +984,6 @@ mypy: error: Invalid error code(s): YOLO, YOLO2 [file test.py] x = 1 [out] -usage: mypy [-h] [-v] [-V] [more options; see below] - [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] mypy: error: Invalid error code(s): YOLO == Return code: 2 @@ -1230,8 +992,6 @@ mypy: error: Invalid error code(s): YOLO [file test.py] x = 1 [out] -usage: mypy [-h] [-v] [-V] [more options; see below] - [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] mypy: error: Invalid error code(s): YOLO == Return code: 2 @@ -1278,15 +1038,15 @@ public static void main(String[] args) [file pkg/y.py] x: str = 0 [out] -pkg/x.py:1: error: invalid syntax +pkg/x.py:1: error: Invalid syntax Found 1 error in 1 file (errors prevented further checking) == Return code: 2 [out version>=3.10] -pkg/x.py:1: error: invalid syntax. Perhaps you forgot a comma? +pkg/x.py:1: error: Invalid syntax. Perhaps you forgot a comma? Found 1 error in 1 file (errors prevented further checking) == Return code: 2 [out version>=3.10.3] -pkg/x.py:1: error: invalid syntax +pkg/x.py:1: error: Invalid syntax Found 1 error in 1 file (errors prevented further checking) == Return code: 2 @@ -1378,6 +1138,21 @@ b/bpkg.py:1: error: "int" not callable [out] c/cpkg.py:1: error: "int" not callable +[case testCmdlineExcludeGitignore] +# cmd: mypy --exclude-gitignore . +[file .gitignore] +abc +[file abc/apkg.py] +1() +[file b/.gitignore] +bpkg.* +[file b/bpkg.py] +1() +[file c/cpkg.py] +1() +[out] +c/cpkg.py:1: error: "int" not callable + [case testCmdlineCfgExclude] # cmd: mypy . [file mypy.ini] @@ -1464,23 +1239,6 @@ note: A user-defined top-level module with name "typing" is not supported Failed to find builtin module mypy_extensions, perhaps typeshed is broken? == Return code: 2 -[case testNewTypeInferenceFlagDeprecated] -# cmd: mypy --new-type-inference a.py -[file a.py] -pass -[out] -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] @@ -1552,6 +1310,22 @@ foo.py:1: error: "int" not callable [out] foo/m.py:1: error: "int" not callable +[case testCmdlineCfgFilesTrailingComma] +# cmd: mypy +[file mypy.ini] +\[mypy] +files = + a.py, + b.py, +[file a.py] +x: str = 'x' # ok +[file b.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + [case testCmdlineCfgEnableErrorCodeTrailingComma] # cmd: mypy . [file mypy.ini] @@ -1578,6 +1352,38 @@ always_true = MY_VAR, [out] +[case testCmdlineCfgModulesTrailingComma] +# cmd: mypy +[file mypy.ini] +\[mypy] +modules = + a, + b, +[file a.py] +x: str = 'x' # ok +[file b.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + +[case testCmdlineCfgPackagesTrailingComma] +# cmd: mypy +[file mypy.ini] +\[mypy] +packages = + a, + b, +[file a/__init__.py] +x: str = 'x' # ok +[file b/__init__.py] +y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[file c/__init__.py] +# This should not trigger any errors, because it is not included: +z: int = 'z' +[out] + [case testTypeVarTupleUnpackEnabled] # cmd: mypy --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack a.py [file a.py] @@ -1587,3 +1393,21 @@ Ts = TypeVarTuple("Ts") Warning: TypeVarTuple is already enabled by default Warning: Unpack is already enabled by default == Return code: 0 + +[case testImportlibImportCannotBeResolved] +# cmd: mypy a.py +# This test is here because it needs use_builtins_fixtures off. + +[file a.py] +from typing import NamedTuple + +class CodecKey(NamedTuple): + def foo(self) -> "CodecKey": + ... + +[file mypy.ini] +\[mypy] + +\[mypy-importlib.*] +follow_imports = skip +follow_imports_for_stubs = True diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index ca2c969d2f5e9..c02f78be18340 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -122,7 +122,7 @@ Daemon stopped Daemon started foo.py:1: error: Function is missing a return type annotation def f(): - ^ + ^~~~~~~~ foo.py:1: note: Use "-> None" if function does not return a value Found 1 error in 1 file (checked 1 source file) == Return code: 1 @@ -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 @@ -388,7 +420,7 @@ a: int a: str [case testDaemonGetType] -$ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary --python-version 3.8 +$ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary --python-version 3.9 Daemon started $ dmypy inspect foo:1:2:3:4 Command "inspect" is only valid after a "check" command (that produces no parse errors) @@ -615,3 +647,158 @@ b: str from demo.test import a [file demo/test.py] a: int + +[case testUnusedTypeIgnorePreservedOnRerun] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --warn-unused-ignores --no-error-summary --hide-error-codes +Daemon started +$ dmypy check -- bar.py +bar.py:2: error: Unused "type: ignore" comment +== Return code: 1 +$ dmypy check -- bar.py +bar.py:2: error: Unused "type: ignore" comment +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +a = 1 # type: ignore + +[case testTypeIgnoreWithoutCodePreservedOnRerun] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --enable-error-code ignore-without-code --no-error-summary +Daemon started +$ dmypy check -- bar.py +bar.py:2: error: "type: ignore" comment without error code [ignore-without-code] +== Return code: 1 +$ dmypy check -- bar.py +bar.py:2: error: "type: ignore" comment without error code [ignore-without-code] +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +a = 1 # type: ignore + +[case testPossiblyUndefinedVarsPreservedAfterRerun] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --enable-error-code possibly-undefined --no-error-summary +Daemon started +$ dmypy check -- bar.py +bar.py:4: error: Name "a" may be undefined [possibly-undefined] +== Return code: 1 +$ dmypy check -- bar.py +bar.py:4: error: Name "a" may be undefined [possibly-undefined] +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +if False: + a = 1 +a + +[case testUnusedTypeIgnorePreservedOnRerunWithIgnoredMissingImports] +$ dmypy start -- --no-error-summary --ignore-missing-imports --warn-unused-ignores +Daemon started +$ dmypy check foo +foo/main.py:3: error: Unused "type: ignore" comment [unused-ignore] +== Return code: 1 +$ dmypy check foo +foo/main.py:3: error: Unused "type: ignore" comment [unused-ignore] +== Return code: 1 + +[file unused/__init__.py] +[file unused/submodule.py] +[file foo/empty.py] +[file foo/__init__.py] +from foo.main import * +from unused.submodule import * +[file foo/main.py] +from foo import empty +from foo.does_not_exist import * +a = 1 # type: ignore + +[case testModuleDoesNotExistPreservedOnRerun] +$ dmypy start -- --no-error-summary --ignore-missing-imports +Daemon started +$ dmypy check foo +foo/main.py:1: error: Module "foo" has no attribute "does_not_exist" [attr-defined] +== Return code: 1 +$ dmypy check foo +foo/main.py:1: error: Module "foo" has no attribute "does_not_exist" [attr-defined] +== Return code: 1 + +[file unused/__init__.py] +[file unused/submodule.py] +[file foo/__init__.py] +from foo.main import * +[file foo/main.py] +from foo import does_not_exist +from unused.submodule import * + +[case testReturnTypeIgnoreAfterUnknownImport] +-- Return type ignores after unknown imports and unused modules are respected on the second pass. +$ dmypy start -- --warn-unused-ignores --no-error-summary +Daemon started +$ dmypy check -- foo.py +foo.py:2: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist" [import-not-found] +foo.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +== Return code: 1 +$ dmypy check -- foo.py +foo.py:2: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist" [import-not-found] +foo.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +== Return code: 1 + +[file unused/__init__.py] +[file unused/empty.py] +[file foo.py] +from unused.empty import * +import a_module_which_does_not_exist +def is_foo() -> str: + return True # type: ignore + +[case testAttrsTypeIgnoreAfterUnknownImport] +$ dmypy start -- --warn-unused-ignores --no-error-summary +Daemon started +$ dmypy check -- foo.py +foo.py:3: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist" [import-not-found] +foo.py:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +== Return code: 1 +$ dmypy check -- foo.py +foo.py:3: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist" [import-not-found] +foo.py:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +== Return code: 1 + +[file unused/__init__.py] +[file unused/empty.py] +[file foo.py] +import attr +from unused.empty import * +import a_module_which_does_not_exist + +@attr.frozen +class A: + def __init__(self) -> None: + self.__attrs_init__() # type: ignore[attr-defined] + +[case testDaemonImportAncestors] +$ dmypy run test.py +Daemon started +test.py:2: error: Unsupported operand types for + ("int" and "str") [operator] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy run test.py +test.py:2: error: Unsupported operand types for + ("int" and "str") [operator] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy run test.py +test.py:2: error: Unsupported operand types for + ("int" and "str") [operator] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +[file test.py] +from xml.etree.ElementTree import Element +1 + 'a' 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-expressions.test b/test-data/unit/deps-expressions.test index ff8c875b66f0a..fd5a4fe0ff9f8 100644 --- a/test-data/unit/deps-expressions.test +++ b/test-data/unit/deps-expressions.test @@ -420,7 +420,7 @@ def g() -> None: -> m.g [case testLiteralDepsExpr] -from typing_extensions import Literal +from typing import Literal Alias = Literal[1] diff --git a/test-data/unit/deps-types.test b/test-data/unit/deps-types.test index def117fe04df0..7642e6d7a14c7 100644 --- a/test-data/unit/deps-types.test +++ b/test-data/unit/deps-types.test @@ -818,7 +818,7 @@ class I: pass -> a [case testAliasDepsTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict from mod import I A = I class P(TypedDict): @@ -826,6 +826,7 @@ class P(TypedDict): [file mod.py] class I: pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m.P @@ -836,7 +837,7 @@ class I: pass [case testAliasDepsTypedDictFunctional] # __dump_all__ -from mypy_extensions import TypedDict +from typing import TypedDict import a P = TypedDict('P', {'x': a.A}) [file a.py] @@ -845,6 +846,7 @@ A = I [file mod.py] class I: pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m @@ -905,6 +907,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 84cea99bf2f62..2c231c9afff6e 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -432,7 +432,7 @@ def f(x: A) -> None: x.y [builtins fixtures/isinstancelist.pyi] [out] -.y> -> m.f +.y> -> m.f -> , m.A, m.f -> m.B, m.f @@ -597,6 +597,7 @@ class C: -> m.C.__init__ [case testPartialNoneTypeAttributeCrash1] +# flags: --no-local-partial-types class C: pass class A: @@ -612,6 +613,7 @@ class A: -> , m.A.f, m.C [case testPartialNoneTypeAttributeCrash2] +# flags: --no-local-partial-types class C: pass class A: @@ -642,12 +644,13 @@ x = 1 -> m, pkg, pkg.mod [case testTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) def foo(x: Point) -> int: return x['x'] + x['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m @@ -655,13 +658,14 @@ def foo(x: Point) -> int: -> m [case testTypedDict2] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass Point = TypedDict('Point', {'x': int, 'y': A}) p = Point(dict(x=42, y=A())) def foo(x: Point) -> int: return x['x'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m @@ -672,7 +676,7 @@ def foo(x: Point) -> int: -> m [case testTypedDict3] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass class Point(TypedDict): x: int @@ -681,6 +685,7 @@ p = Point(dict(x=42, y=A())) def foo(x: Point) -> int: return x['x'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] -> m -> m @@ -749,7 +754,7 @@ class C: -> m.outer -> m, m.outer -[case testDecoratorDepsDeeepNested] +[case testDecoratorDepsDeepNested] import mod def outer() -> None: diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 10fb0a80cf38f..1f1987183fe46 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 @@ -615,57 +617,61 @@ __main__.E __main__.F [case testTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': str}) p = Point(dict(x=42, y='lurr')) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p [case testTypedDict2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: int p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: str p = Point(dict(x=42, y='lurr')) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p [case testTypedDict3] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int}) p = Point(dict(x=42)) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p [case testTypedDict4] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file next.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}, total=False) p = Point(dict(x=42, y=1337)) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] __main__.Point __main__.p @@ -1147,7 +1153,7 @@ __main__.Diff __main__.Diff.x [case testLiteralTriggersVar] -from typing_extensions import Literal +from typing import Literal x: Literal[1] = 1 y = 1 @@ -1165,7 +1171,7 @@ class C: self.same_instance: Literal[1] = 1 [file next.py] -from typing_extensions import Literal +from typing import Literal x = 1 y: Literal[1] = 1 @@ -1194,7 +1200,7 @@ __main__.y __main__.z [case testLiteralTriggersFunctions] -from typing_extensions import Literal +from typing import Literal def function_1() -> int: pass def function_2() -> Literal[1]: pass @@ -1258,7 +1264,7 @@ class C: def staticmethod_same_2(x: Literal[1]) -> None: pass [file next.py] -from typing_extensions import Literal +from typing import Literal def function_1() -> Literal[1]: pass def function_2() -> int: pass @@ -1348,7 +1354,7 @@ __main__.function_5 __main__.function_6 [case testLiteralTriggersProperty] -from typing_extensions import Literal +from typing import Literal class C: @property @@ -1361,7 +1367,7 @@ class C: def same(self) -> Literal[1]: pass [file next.py] -from typing_extensions import Literal +from typing import Literal class C: @property @@ -1378,8 +1384,7 @@ __main__.C.p1 __main__.C.p2 [case testLiteralsTriggersOverload] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func(x: str) -> str: ... @@ -1411,8 +1416,7 @@ class C: pass [file next.py] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def func(x: str) -> str: ... @@ -1448,10 +1452,10 @@ __main__.C.method __main__.func [case testUnionOfLiterals] -from typing_extensions import Literal +from typing import Literal x: Literal[1, '2'] [file next.py] -from typing_extensions import Literal +from typing import Literal x: Literal[1, 2] [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/exportjson.test b/test-data/unit/exportjson.test new file mode 100644 index 0000000000000..14295281a48f8 --- /dev/null +++ b/test-data/unit/exportjson.test @@ -0,0 +1,280 @@ +-- Test cases for exporting mypy cache files to JSON (mypy.exportjson). +-- +-- The tool is maintained on a best effort basis so we don't attempt to have +-- full test coverage. +-- +-- Some tests only ensure that *some* JSON is generated successfully. These +-- have as [out]. + +[case testExportVar] +x = 0 +[out] +{ + ".class": "MypyFile", + "_fullname": "main", + "names": { + ".class": "SymbolTable", + "x": { + ".class": "SymbolTableNode", + "kind": "Gdef", + "node": { + ".class": "Var", + "name": "x", + "fullname": "main.x", + "type": "builtins.int", + "setter_type": null, + "flags": [ + "is_ready", + "is_inferred", + "has_explicit_value" + ] + } + } + }, + "is_stub": false, + "path": ..., + "is_partial_stub_package": false, + "future_import_flags": [] +} + +[case testExportClass] +class C: + x: int +[out] +{ + ".class": "MypyFile", + "_fullname": "main", + "names": { + ".class": "SymbolTable", + "C": { + ".class": "SymbolTableNode", + "kind": "Gdef", + "node": { + ".class": "TypeInfo", + "module_name": "main", + "fullname": "main.C", + "names": { + ".class": "SymbolTable", + "x": { + ".class": "SymbolTableNode", + "kind": "Mdef", + "node": { + ".class": "Var", + "name": "x", + "fullname": "main.C.x", + "type": "builtins.int", + "setter_type": null, + "flags": [ + "is_initialized_in_class", + "is_ready" + ] + } + } + }, + "defn": { + ".class": "ClassDef", + "name": "C", + "fullname": "main.C", + "type_vars": [] + }, + "abstract_attributes": [], + "type_vars": [], + "has_param_spec_type": false, + "bases": [ + "builtins.object" + ], + "mro": [ + "main.C", + "builtins.object" + ], + "_promote": [], + "alt_promote": null, + "declared_metaclass": null, + "metaclass_type": null, + "tuple_type": null, + "typeddict_type": null, + "flags": [], + "metadata": {}, + "slots": null, + "deletable_attributes": [], + "self_type": null, + "dataclass_transform_spec": null, + "deprecated": null + } + } + }, + "is_stub": false, + "path": ..., + "is_partial_stub_package": false, + "future_import_flags": [] +} + +[case testExportCrossRef] +from typing import Any +[out] +{ + ".class": "MypyFile", + "_fullname": "main", + "names": { + ".class": "SymbolTable", + "Any": { + ".class": "SymbolTableNode", + "kind": "Gdef", + "cross_ref": "typing.Any" + } + }, + "is_stub": false, + "path": ..., + "is_partial_stub_package": false, + "future_import_flags": [] +} + +[case testExportFuncDef] +def foo(a: int) -> None: ... +[out] +{ + ".class": "MypyFile", + "_fullname": "main", + "names": { + ".class": "SymbolTable", + "foo": { + ".class": "SymbolTableNode", + "kind": "Gdef", + "node": { + ".class": "FuncDef", + "name": "foo", + "fullname": "main.foo", + "arg_names": [ + "a" + ], + "arg_kinds": [ + 0 + ], + "type": { + ".class": "CallableType", + "arg_types": [ + "builtins.int" + ], + "arg_kinds": [ + 0 + ], + "arg_names": [ + "a" + ], + "ret_type": { + ".class": "NoneType" + }, + "fallback": "builtins.function", + "name": "foo", + "variables": [], + "is_ellipsis_args": false, + "implicit": false, + "is_bound": false, + "type_guard": null, + "type_is": null, + "from_concatenate": false, + "imprecise_arg_kinds": false, + "unpack_kwargs": false + }, + "flags": [], + "abstract_status": 0, + "dataclass_transform_spec": null, + "deprecated": null, + "original_first_arg": "a" + } + } + }, + "is_stub": false, + "path": ..., + "is_partial_stub_package": false, + "future_import_flags": [] +} + +[case testExportDifferentTypes] +from __future__ import annotations + +from typing import Callable, Any, Literal, NoReturn, TypedDict, NamedTuple + +list_ann: list[int] +any_ann: Any +tuple_ann: tuple[int, str] +union_ann: int | None +callable_ann: Callable[[int], str] +type_type_ann: type[int] +literal_ann: Literal['x', 5, False] + +def f() -> NoReturn: + assert False + +BadType = 1 +x: BadType # type: ignore + +class TD(TypedDict): + x: int + +td = TD(x=1) + +NT = NamedTuple("NT", [("x", int)]) + +nt = NT(x=1) + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-medium.pyi] +[out] + + +[case testExportGenericTypes] +from __future__ import annotations + +from typing import TypeVar, Callable +from typing_extensions import TypeVarTuple, ParamSpec, Unpack, Concatenate + +T = TypeVar("T") + +def ident(x: T) -> T: + return x + +Ts = TypeVarTuple("Ts") + +def ts(t: tuple[Unpack[Ts]]) -> tuple[Unpack[Ts]]: + return t + +P = ParamSpec("P") + +def pspec(f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: + f(*args, **kwargs) + +def concat(f: Callable[Concatenate[int, P], None], *args: P.args, **kwargs: P.kwargs) -> None: + f(1, *args, **kwargs) + +[builtins fixtures/tuple.pyi] +[out] + + +[case testExportDifferentNodes] +from __future__ import annotations + +import typing + +from typing import overload, TypeVar + +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x: int | str) -> int | str: ... + +T = TypeVar("T") + +def deco(f: T) -> T: + return f + +@deco +def foo(x: int) -> int: ... + +X = int +x: X = 2 + +[builtins fixtures/tuple.pyi] +[out] + diff --git a/test-data/unit/fine-grained-blockers.test b/test-data/unit/fine-grained-blockers.test index 33dedd8871142..8e16da053d6aa 100644 --- a/test-data/unit/fine-grained-blockers.test +++ b/test-data/unit/fine-grained-blockers.test @@ -19,13 +19,13 @@ def f(x: int) -> None: pass def f() -> None: pass [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == main:2: error: Missing positional argument "x" in call to "f" == [out version>=3.10] == -a.py:1: error: expected ':' +a.py:1: error: Expected ':' == main:2: error: Missing positional argument "x" in call to "f" == @@ -44,7 +44,7 @@ def f(x: int) -> None: pass def f() -> None: pass [out] == -a.py:1: error: invalid syntax [syntax] +a.py:1: error: Invalid syntax [syntax] def f(x: int) -> ^ == @@ -54,7 +54,7 @@ main:3: error: Missing positional argument "x" in call to "f" [call-arg] == [out version>=3.10] == -a.py:1: error: expected ':' [syntax] +a.py:1: error: Expected ':' [syntax] def f(x: int) -> ^ == @@ -77,16 +77,16 @@ def f(x: int def f(x: int) -> None: pass [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == -a.py:2: error: invalid syntax +a.py:2: error: Invalid syntax == main:2: error: Missing positional argument "x" in call to "f" [out version>=3.10] == -a.py:1: error: expected ':' +a.py:1: error: Expected ':' == -a.py:2: error: expected ':' +a.py:2: error: Expected ':' == main:2: error: Missing positional argument "x" in call to "f" @@ -124,7 +124,7 @@ def f() -> None: pass main:3: error: Too many arguments for "f" main:5: error: Too many arguments for "f" == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == main:3: error: Too many arguments for "f" main:5: error: Too many arguments for "f" @@ -132,7 +132,7 @@ main:5: error: Too many arguments for "f" main:3: error: Too many arguments for "f" main:5: error: Too many arguments for "f" == -a.py:1: error: expected ':' +a.py:1: error: Expected ':' == main:3: error: Too many arguments for "f" main:5: error: Too many arguments for "f" @@ -153,12 +153,12 @@ class C: def f(self, x: int) -> None: pass [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == main:5: error: Missing positional argument "x" in call to "f" of "C" [out version==3.10.0] == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == main:5: error: Missing positional argument "x" in call to "f" of "C" @@ -173,14 +173,14 @@ def f() -> None: pass main:1: error: Cannot find implementation or library stub for module named "a" main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == main:2: error: Too many arguments for "f" [out version==3.10.0] main:1: error: Cannot find implementation or library stub for module named "a" main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == main:2: error: Too many arguments for "f" @@ -208,7 +208,7 @@ a.f() def g() -> None: pass [out] == -b.py:1: error: invalid syntax +b.py:1: error: Invalid syntax == [case testModifyTwoFilesOneWithBlockingError2] @@ -235,7 +235,7 @@ def f() -> None: pass b.g() [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == [case testBlockingErrorRemainsUnfixed] @@ -254,16 +254,16 @@ import b b.f() [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == a.py:2: error: Missing positional argument "x" in call to "f" [out version==3.10.0] == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == a.py:2: error: Missing positional argument "x" in call to "f" @@ -303,9 +303,9 @@ def g() -> None: pass a.f(1) [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == a.py:3: error: Too many arguments for "g" b.py:3: error: Too many arguments for "f" @@ -325,14 +325,14 @@ x x [delete a.py.3] [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == main:1: error: Cannot find implementation or library stub for module named "a" main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports b.py:1: error: Cannot find implementation or library stub for module named "a" [out version==3.10.0] == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == main:1: error: Cannot find implementation or library stub for module named "a" main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports @@ -353,14 +353,14 @@ x x [delete a.py.3] [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == b.py:1: error: Cannot find implementation or library stub for module named "a" b.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports main:1: error: Cannot find implementation or library stub for module named "a" [out version==3.10.0] == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == b.py:1: error: Cannot find implementation or library stub for module named "a" b.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports @@ -382,17 +382,17 @@ a.f() [builtins fixtures/module.pyi] [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == b.py:2: error: Module has no attribute "f" b.py:3: error: "int" not callable [out version==3.10.0] == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == b.py:2: error: Module has no attribute "f" b.py:3: error: "int" not callable @@ -408,12 +408,12 @@ import blocker def f() -> None: pass [out] == -/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax +/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax == a.py:1: error: "int" not callable [out version==3.10.0] == -/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax. Perhaps you forgot a comma? +/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax. Perhaps you forgot a comma? == a.py:1: error: "int" not callable @@ -485,16 +485,16 @@ import sys [builtins fixtures/tuple.pyi] [out] == -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == -/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax +/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax == a.py:2: error: "int" not callable [out version==3.10.0] == -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == -/test-data/unit/lib-stub/blocker.pyi:2: error: invalid syntax. Perhaps you forgot a comma? +/test-data/unit/lib-stub/blocker.pyi:2: error: Invalid syntax. Perhaps you forgot a comma? == a.py:2: error: "int" not callable @@ -511,12 +511,12 @@ x = 1 def f() -> int: return 0 [out] -a.py:1: error: invalid syntax +a.py:1: error: Invalid syntax == b.py:2: error: Incompatible return value type (got "str", expected "int") == [out version==3.10.0] -a.py:1: error: invalid syntax. Perhaps you forgot a comma? +a.py:1: error: Invalid syntax. Perhaps you forgot a comma? == b.py:2: error: Incompatible return value type (got "str", expected "int") == diff --git a/test-data/unit/fine-grained-cache-incremental.test b/test-data/unit/fine-grained-cache-incremental.test index 00157333efd7e..f622cefc5b8eb 100644 --- a/test-data/unit/fine-grained-cache-incremental.test +++ b/test-data/unit/fine-grained-cache-incremental.test @@ -202,7 +202,7 @@ a.py:8: note: x: expected "int", got "str" [file b.py] -- This is a heinous hack, but we simulate having a invalid cache by clobbering -- the proto deps file with something with mtime mismatches. -[file ../.mypy_cache/3.8/@deps.meta.json.2] +[file ../.mypy_cache/3.9/@deps.meta.json.2] {"snapshot": {"__main__": "a7c958b001a45bd6a2a320f4e53c4c16", "a": "d41d8cd98f00b204e9800998ecf8427e", "b": "d41d8cd98f00b204e9800998ecf8427e", "builtins": "c532c89da517a4b779bcf7a964478d67"}, "deps_meta": {"@root": {"path": "@root.deps.json", "mtime": 0}, "__main__": {"path": "__main__.deps.json", "mtime": 0}, "a": {"path": "a.deps.json", "mtime": 0}, "b": {"path": "b.deps.json", "mtime": 0}, "builtins": {"path": "builtins.deps.json", "mtime": 0}}} [file b.py.2] @@ -234,8 +234,8 @@ x = 10 [file p/c.py] class C: pass -[delete ../.mypy_cache/3.8/b.meta.json.2] -[delete ../.mypy_cache/3.8/p/c.meta.json.2] +[delete ../.mypy_cache/3.9/b.meta.json.2] +[delete ../.mypy_cache/3.9/p/c.meta.json.2] [out] == diff --git a/test-data/unit/fine-grained-dataclass-transform.test b/test-data/unit/fine-grained-dataclass-transform.test index 89628256fda5b..76ffeeb347c71 100644 --- a/test-data/unit/fine-grained-dataclass-transform.test +++ b/test-data/unit/fine-grained-dataclass-transform.test @@ -88,7 +88,6 @@ class A(Dataclass): main:7: error: Unexpected keyword argument "x" for "B" builtins.pyi:14: note: "B" defined here main:7: error: Unexpected keyword argument "y" for "B" -builtins.pyi:14: note: "B" defined here == [case frozenInheritanceViaDefault] diff --git a/test-data/unit/fine-grained-follow-imports.test b/test-data/unit/fine-grained-follow-imports.test index 22f2a7895cf96..d716a57123dcc 100644 --- a/test-data/unit/fine-grained-follow-imports.test +++ b/test-data/unit/fine-grained-follow-imports.test @@ -831,8 +831,7 @@ class A: ... import trio [file trio/__init__.py.2] -from typing import TypeVar -from typing_extensions import TypedDict +from typing import TypedDict, TypeVar import trio from . import abc as abc @@ -844,5 +843,6 @@ class C(TypedDict): import trio class A: ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == diff --git a/test-data/unit/fine-grained-inspect.test b/test-data/unit/fine-grained-inspect.test index ed89f2f099f94..5caa1a94387b1 100644 --- a/test-data/unit/fine-grained-inspect.test +++ b/test-data/unit/fine-grained-inspect.test @@ -23,7 +23,7 @@ NameExpr -> "C[T]" MemberExpr -> "T" NameExpr -> "C[T]" MemberExpr -> "T" -12:5:12:5 -> "Type[foo.C[Any]]" +12:5:12:5 -> "type[foo.C[builtins.int]]" 12:5:12:9 -> "foo.C[builtins.int]" 12:1:12:10 -> "builtins.int" CallExpr:12:5:12:9 -> "C[int]" diff --git a/test-data/unit/fine-grained-modules.test b/test-data/unit/fine-grained-modules.test index f28dbaa1113b9..3ee07a03792f8 100644 --- a/test-data/unit/fine-grained-modules.test +++ b/test-data/unit/fine-grained-modules.test @@ -2244,3 +2244,19 @@ undefined a.py:1: error: Cannot find implementation or library stub for module named "foobar" a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports == + +[case testDaemonImportMapRefresh] +# cmd: mypy main.py +[file main.py] +[file main.py.2] +import a.b +reveal_type(a.b.foo()) +[file a/__init__.pyi] +[file a/b.pyi] +import a.c +def foo() -> a.c.C: ... +[file a/c.pyi] +class C: ... +[out] +== +main.py:2: note: Revealed type is "a.c.C" diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 0e438ca06574c..b85b5bd3e3205 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -74,8 +74,8 @@ from builtins import tuple as B [typing fixtures/typing-full.pyi] [out] == -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]") +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 @@ -95,3 +95,23 @@ 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-suggest.test b/test-data/unit/fine-grained-suggest.test index 02373091ad54c..c2e544baf38bf 100644 --- a/test-data/unit/fine-grained-suggest.test +++ b/test-data/unit/fine-grained-suggest.test @@ -17,8 +17,8 @@ def bar() -> None: [out] bar.py:3: (str) bar.py:4: (arg=str) -bar.py:6: (*typing.List[str]) -bar.py:8: (**typing.Dict[str, str]) +bar.py:6: (*list[str]) +bar.py:8: (**dict[str, str]) == [case testSuggestCallsitesStep2] @@ -41,8 +41,8 @@ def bar() -> None: == bar.py:3: (str) bar.py:4: (arg=str) -bar.py:6: (*typing.List[str]) -bar.py:8: (**typing.Dict[str, str]) +bar.py:6: (*list[str]) +bar.py:8: (**dict[str, str]) [case testMaxGuesses] # suggest: foo.foo @@ -159,13 +159,14 @@ def foo(): [case testSuggestInferTypedDict] # suggest: foo.foo [file foo.py] -from typing_extensions import TypedDict +from typing import TypedDict TD = TypedDict('TD', {'x': int}) def foo(): return bar() def bar() -> TD: ... [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] () -> foo.TD == @@ -206,6 +207,36 @@ foo(B()) (baz.B) -> Tuple[foo.A, foo:A.C] == +[case testSuggestReexportNamingNameMatchesModule1] +# suggest: foo.foo +[file foo.py] +import bar +def foo(): + return bar.bar() + +[file bar.py] +class bar: ... # name matches module name + +[out] +() -> bar.bar +== + +[case testSuggestReexportNamingNameMatchesModule2] +# suggest: foo.foo +[file foo.py] +import bar +import qux +def foo(): + return qux.bar() + +[file bar.py] +[file qux.py] +class bar: ... # name matches another module name + +[out] +() -> qux.bar +== + [case testSuggestInferInit] # suggest: foo.Foo.__init__ [file foo.py] @@ -601,6 +632,87 @@ def bar() -> None: (str) -> str == +[case testSuggestInferFuncDecorator5] +# suggest: foo.foo1 +# suggest: foo.foo2 +# suggest: foo.foo3 +[file foo.py] +from __future__ import annotations + +from typing import TypeVar, Generator, Callable + +F = TypeVar('F') + +# simplified `@contextmanager +class _impl: + def __call__(self, f: F) -> F: return f +def contextmanager(gen: Callable[[], Generator[None, None, None]]) -> Callable[[], _impl]: return _impl + +@contextmanager +def gen() -> Generator[None, None, None]: + yield + +@gen() +def foo1(x): + return x + +foo1('hi') + +inst = gen() + +@inst +def foo2(x): + return x + +foo2('hello') + +ref = gen + +@ref() +def foo3(x): + return x + +foo3('hello hello') + +[builtins fixtures/isinstancelist.pyi] +[out] +(str) -> str +(str) -> str +(str) -> str +== + +[case testSuggestInferFuncDecorator6] +# suggest: foo.f +[file foo.py] +from __future__ import annotations + +from typing import Callable, Protocol, TypeVar +from typing_extensions import ParamSpec + +P = ParamSpec('P') +R = TypeVar('R') +R_co = TypeVar('R_co', covariant=True) + +class Proto(Protocol[P, R_co]): + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R_co: ... + +def dec1(f: Callable[P, R]) -> Callable[P, R]: ... +def dec2(f: Callable[..., R]) -> Callable[..., R]: ... +def dec3(f: Proto[P, R_co]) -> Proto[P, R_co]: ... + +@dec1 +@dec2 +@dec3 +def f(x): + return x + +f('hi') + +[builtins fixtures/isinstancelist.pyi] +[out] +(str) -> str +== + [case testSuggestFlexAny1] # suggest: --flex-any=0.4 m.foo # suggest: --flex-any=0.7 m.foo @@ -641,8 +753,8 @@ No guesses that match criteria! (int, int) -> Any No guesses that match criteria! == -(typing.List[Any]) -> int -(typing.List[Any]) -> int +(list[Any]) -> int +(list[Any]) -> int [case testSuggestFlexAny2] @@ -713,6 +825,26 @@ def bar(iany) -> None: (int) -> None == +[case testSuggestNewInit] +# suggest: foo.F.__init__ +# suggest: foo.F.__new__ +[file foo.py] +class F: + def __new__(cls, t): + return super().__new__(cls) + + def __init__(self, t): + self.t = t + +[file bar.py] +from foo import F +def bar(iany) -> None: + F(0) +[out] +(int) -> None +(int) -> Any +== + [case testSuggestColonBasic] # suggest: tmp/foo.py:1 # suggest: tmp/bar/baz.py:2 @@ -915,7 +1047,7 @@ def g(): ... z = foo(f(), g()) [builtins fixtures/isinstancelist.pyi] [out] -(foo.List[Any], UNKNOWN) -> Tuple[foo.List[Any], Any] +(list[Any], UNKNOWN) -> Tuple[list[Any], Any] == [case testSuggestBadImport] @@ -957,11 +1089,11 @@ spam({'x': 5}) [builtins fixtures/dict.pyi] [out] -() -> typing.Dict[str, int] -() -> typing.Dict[Any, Any] -() -> foo:List[typing.Dict[str, int]] -() -> foo.List[int] -(typing.Dict[str, int]) -> None +() -> dict[str, int] +() -> dict[Any, Any] +() -> list[dict[str, int]] +() -> list[int] +(dict[str, int]) -> None == [case testSuggestWithErrors] @@ -985,10 +1117,10 @@ def foo(): ( [out] -foo.py:4: error: unexpected EOF while parsing +foo.py:4: error: Unexpected EOF while parsing Command 'suggest' is only valid after a 'check' command (that produces no parse errors) == -foo.py:4: error: unexpected EOF while parsing +foo.py:4: error: Unexpected EOF while parsing [out version>=3.10] foo.py:4: error: '(' was never closed Command 'suggest' is only valid after a 'check' command (that produces no parse errors) @@ -1111,18 +1243,18 @@ tuple1(t) [out] (int, int) -> int (int, int) -> int -(int) -> foo.List[int] -(foo.List[int]) -> int +(int) -> list[int] +(list[int]) -> int (Union[int, str]) -> None (Callable[[int], int]) -> int (Callable[[float], int]) -> int (Optional[int]) -> None (Union[None, int, str]) -> None -(Optional[foo.List[int]]) -> int -(Union[foo.Set[int], foo.List[int]]) -> None +(Optional[list[int]]) -> int +(Union[set[int], list[int]]) -> None (Optional[int]) -> None (Optional[Any]) -> None -(foo.Dict[int, int]) -> None +(dict[int, int]) -> None (Tuple[int, int]) -> None == diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index faedd890922d7..4a30c8a3828f0 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -1051,9 +1051,9 @@ class A: [file n.py.3] [out] == -main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A" +main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "m.A" == -main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A" +main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "m.A" [case testModifyBaseClassMethodCausingInvalidOverride] import m @@ -1067,7 +1067,7 @@ class A: def f(self) -> int: pass [out] == -main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A" +main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "m.A" [case testAddBaseClassAttributeCausingErrorInSubclass] import m @@ -1340,7 +1340,7 @@ class A: [out] == -- This is a bad error message -main:7: error: Argument 1 to "use" has incompatible type "Type[A]"; expected "Callable[[], A]" +main:7: error: Argument 1 to "use" has incompatible type "type[A]"; expected "Callable[[], A]" [case testConstructorSignatureChanged3] from a import C @@ -1720,7 +1720,7 @@ from typing import Iterator, Callable, List, Optional from a import f import a -def dec(f: Callable[['A'], Optional[Iterator[int]]]) -> Callable[[int], int]: pass +def dec(f: Callable[['A'], Optional[Iterator[int]]]) -> Callable[['A', int], int]: pass class A: @dec @@ -1974,11 +1974,11 @@ class B: class B: def foo(self) -> int: return 12 [out] -a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B" +a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "b.B" == -a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B" +a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "b.B" == -a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B" +a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "b.B" == [case testPreviousErrorInMethodSemanal1] @@ -2369,7 +2369,7 @@ a.py:7: error: "B" has no attribute "x" == a.py:5: error: Missing positional argument "x" in call to "C" -[case testDecoratorUpdateDeeepNested] +[case testDecoratorUpdateDeepNested] import a [file a.py] import mod @@ -2674,9 +2674,9 @@ def g() -> None: pass def g() -> int: pass [builtins fixtures/dict.pyi] [out] -main:7: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...") +main:7: error: Need type annotation for "x" (hint: "x: dict[, ] = ...") == -main:7: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...") +main:7: error: Need type annotation for "x" (hint: "x: dict[, ] = ...") [case testRefreshPartialTypeInClass] import a @@ -2692,9 +2692,9 @@ def g() -> None: pass def g() -> int: pass [builtins fixtures/dict.pyi] [out] -main:5: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...") +main:5: error: Need type annotation for "x" (hint: "x: dict[, ] = ...") == -main:5: error: Need type annotation for "x" (hint: "x: Dict[, ] = ...") +main:5: error: Need type annotation for "x" (hint: "x: dict[, ] = ...") [case testRefreshPartialTypeInferredAttributeIndex] from c import C @@ -2833,7 +2833,7 @@ class M(type): == a.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "str") == -a.py:4: error: "Type[C]" has no attribute "x" +a.py:4: error: "type[C]" has no attribute "x" == [case testMetaclassAttributesDirect] @@ -2862,7 +2862,7 @@ class M(type): == a.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") == -a.py:3: error: "Type[C]" has no attribute "x" +a.py:3: error: "type[C]" has no attribute "x" == [case testMetaclassOperators] @@ -2886,7 +2886,7 @@ class M(type): pass [out] == -a.py:4: error: Unsupported operand types for + ("Type[C]" and "Type[C]") +a.py:4: error: Unsupported operand types for + ("type[C]" and "type[C]") [case testMetaclassOperatorsDirect] import a @@ -2907,7 +2907,7 @@ class M(type): def __add__(self, other: M) -> M: pass [out] -a.py:3: error: Unsupported operand types for + ("Type[C]" and "Type[C]") +a.py:3: error: Unsupported operand types for + ("type[C]" and "type[C]") == [case testFineMetaclassUpdate] @@ -2931,15 +2931,17 @@ class B(metaclass=c.M): pass class M(type): pass [out] -a.py:6: error: Argument 1 to "f" has incompatible type "Type[B]"; expected "M" +a.py:6: error: Argument 1 to "f" has incompatible type "type[B]"; expected "M" == [case testFineMetaclassRecalculation] import a + [file a.py] from b import B class M2(type): pass class D(B, metaclass=M2): pass + [file b.py] import c class B: pass @@ -2949,27 +2951,31 @@ import c class B(metaclass=c.M): pass [file c.py] -class M(type): - pass +class M(type): pass [out] == a.py:3: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +a.py:3: note: "a.M2" (metaclass of "a.D") conflicts with "c.M" (metaclass of "b.B") [case testFineMetaclassDeclaredUpdate] import a + [file a.py] import b class B(metaclass=b.M): pass class D(B, metaclass=b.M2): pass + [file b.py] class M(type): pass class M2(M): pass + [file b.py.2] class M(type): pass class M2(type): pass [out] == a.py:3: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases +a.py:3: note: "b.M2" (metaclass of "a.D") conflicts with "b.M" (metaclass of "a.B") [case testFineMetaclassRemoveFromClass] import a @@ -2990,7 +2996,7 @@ class M(type): x: int [out] == -a.py:3: error: "Type[B]" has no attribute "x" +a.py:3: error: "type[B]" has no attribute "x" [case testFineMetaclassRemoveFromClass2] import a @@ -3015,7 +3021,7 @@ class M(type): x: int [out] == -a.py:3: error: Argument 1 to "test" has incompatible type "Type[B]"; expected "M" +a.py:3: error: Argument 1 to "test" has incompatible type "type[B]"; expected "M" [case testBadMetaclassCorrected] import a @@ -3052,7 +3058,7 @@ class C(metaclass=c.M): class M(type): x: int [out] -a.py:3: error: "Type[C]" has no attribute "x" +a.py:3: error: "type[C]" has no attribute "x" == [case testIndirectSubclassReferenceMetaclass] @@ -3088,7 +3094,7 @@ class M(type): == a.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") == -b.py:2: error: "Type[D]" has no attribute "x" +b.py:2: error: "type[D]" has no attribute "x" == [case testMetaclassDeletion] @@ -3407,8 +3413,8 @@ lol(b.x) [builtins fixtures/tuple.pyi] [out] == -c.py:7: error: Argument 1 to "lol" has incompatible type "M"; expected "Tuple[Tuple[int]]" -c.py:9: error: Argument 1 to "lol" has incompatible type "M"; expected "Tuple[Tuple[int]]" +c.py:7: error: Argument 1 to "lol" has incompatible type "M"; expected "tuple[tuple[int]]" +c.py:9: error: Argument 1 to "lol" has incompatible type "M"; expected "tuple[tuple[int]]" [case testNamedTupleUpdate4] import b @@ -3522,9 +3528,9 @@ reveal_type(a.n) [out] == == -c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" +c.py:4: note: Revealed type is "tuple[Union[tuple[Union[tuple[Union[..., None], builtins.int, fallback=a.N], None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") -c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" +c.py:7: note: Revealed type is "tuple[Union[tuple[Union[tuple[Union[..., None], builtins.int, fallback=a.N], None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" [case testTupleTypeUpdateNonRecursiveToRecursiveFine] import c @@ -3555,7 +3561,7 @@ def f(x: a.N) -> None: [out] == == -c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" +c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]" c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypeAliasUpdateNonRecursiveToRecursiveFine] @@ -3587,31 +3593,32 @@ def f(x: a.N) -> None: [out] == == -c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int], None], builtins.int]" +c.py:4: note: Revealed type is "tuple[Union[tuple[Union[..., None], builtins.int], None], builtins.int]" c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int") [case testTypedDictRefresh] -[builtins fixtures/dict.pyi] import a [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file a.py.2] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) # dummy change +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == [case testTypedDictUpdate] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(dict(x=42, y=1337)) [file a.py.2] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': str}) p = Point(dict(x=42, y='lurr')) [file b.py] @@ -3619,6 +3626,7 @@ from a import Point def foo(x: Point) -> int: return x['x'] + x['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:3: error: Unsupported operand types for + ("int" and "str") @@ -3626,13 +3634,13 @@ b.py:3: error: Unsupported operand types for + ("int" and "str") [case testTypedDictUpdate2] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: int p = Point(dict(x=42, y=1337)) [file a.py.2] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: str @@ -3642,6 +3650,7 @@ from a import Point def foo(x: Point) -> int: return x['x'] + x['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:3: error: Unsupported operand types for + ("int" and "str") @@ -3649,16 +3658,14 @@ b.py:3: error: Unsupported operand types for + ("int" and "str") [case testTypedDictUpdate3] import b [file a.py] -from mypy_extensions import TypedDict -from typing import Optional +from typing import Optional, TypedDict class Point(TypedDict): x: Optional[Point] y: int z: int p = Point(dict(x=None, y=1337, z=0)) [file a.py.2] -from mypy_extensions import TypedDict -from typing import Optional +from typing import Optional, TypedDict class Point(TypedDict): x: Optional[Point] y: str @@ -3670,6 +3677,7 @@ def foo(x: Point) -> int: assert x['x'] is not None return x['x']['z'] + x['x']['y'] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:4: error: Unsupported operand types for + ("int" and "str") @@ -3677,13 +3685,12 @@ b.py:4: error: Unsupported operand types for + ("int" and "str") [case testTypedDictUpdateGeneric] import b [file a.py] -from mypy_extensions import TypedDict +from typing import TypedDict class Point(TypedDict): x: int y: int [file a.py.2] -from mypy_extensions import TypedDict -from typing import Generic, TypeVar +from typing import Generic, TypedDict, TypeVar T = TypeVar("T") class Point(TypedDict, Generic[T]): @@ -3700,6 +3707,7 @@ def foo() -> None: p = Point(x=0, y="no") i: int = p["y"] [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == == @@ -3708,17 +3716,20 @@ b.py:4: error: Incompatible types in assignment (expression has type "str", vari [case testTypedDictUpdateReadOnly] import b [file a.py] -from typing_extensions import TypedDict, ReadOnly +from typing import TypedDict +from typing_extensions import ReadOnly Point = TypedDict('Point', {'x': int, 'y': int}) p = Point(x=1, y=2) [file a.py.2] -from typing_extensions import TypedDict, ReadOnly +from typing import TypedDict +from typing_extensions import 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 +from typing import TypedDict +from typing_extensions import ReadOnly Point = TypedDict('Point', {'x': ReadOnly[int], 'y': int}) p = Point(x=1, y=2) [file b.py] @@ -3727,6 +3738,7 @@ def foo(x: Point) -> None: x['x'] = 1 x['y'] = 2 [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == b.py:4: error: ReadOnly TypedDict key "y" TypedDict is mutated @@ -4514,9 +4526,9 @@ x = 0 x = '' [builtins fixtures/tuple.pyi] [out] -b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], object], int]") +b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[object], int]") == -b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[Tuple[int, ...], object], int]") +b.py:5: error: Incompatible types in assignment (expression has type "int", base class "tuple" defined the type as "Callable[[object], int]") [case testReprocessEllipses1] import a @@ -5355,6 +5367,7 @@ c: C c = C.X if int(): c = 1 +[builtins fixtures/enum.pyi] [out] == == @@ -5386,6 +5399,7 @@ if int(): n = C.X if int(): n = c +[builtins fixtures/enum.pyi] [out] == == @@ -5410,10 +5424,11 @@ from enum import Enum class C(Enum): X = 0 +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == -a.py:5: error: "Type[C]" has no attribute "Y" +a.py:5: error: "type[C]" has no attribute "Y" [case testClassBasedEnumPropagation2] import a @@ -5432,6 +5447,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" @@ -5456,6 +5472,7 @@ c: C c = C.X if int(): c = 1 +[builtins fixtures/tuple.pyi] [out] == == @@ -5485,6 +5502,7 @@ if int(): n: int n = C.X n = c +[builtins fixtures/enum.pyi] [out] == == @@ -5506,10 +5524,11 @@ C = Enum('C', 'X Y') from enum import Enum C = Enum('C', 'X') +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == -a.py:5: error: "Type[C]" has no attribute "Y" +a.py:5: error: "type[C]" has no attribute "Y" [case testFuncBasedEnumPropagation2] import a @@ -5526,6 +5545,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" @@ -6153,7 +6173,7 @@ class C: pass [out] == -a.py:6: error: Argument 1 to "func" has incompatible type "Type[C]"; expected "Callable[[int], Any]" +a.py:6: error: Argument 1 to "func" has incompatible type "type[C]"; expected "Callable[[int], Any]" [case testDunderNewDefine] import a @@ -6767,7 +6787,7 @@ class M(type): x: int [out] == -a.py:4: error: Argument 1 to "func" has incompatible type "Type[B]"; expected "P" +a.py:4: error: Argument 1 to "func" has incompatible type "type[B]"; expected "P" [case testProtocolVsProtocolSubUpdated] import a @@ -6958,7 +6978,7 @@ class A: == main:3: error: "A" has no attribute "__iter__" (not iterable) -[case testWeAreCarefullWithBuiltinProtocolsBase] +[case testWeAreCarefulWithBuiltinProtocolsBase] import a x: a.A for i in x: @@ -7111,7 +7131,7 @@ class AS: == main:9: error: Incompatible types in assignment (expression has type "int", variable has type "str") -[case testOverloadsUpdatedTypeRechekConsistency] +[case testOverloadsUpdatedTypeRecheckConsistency] from typing import overload import mod class Outer: @@ -7292,9 +7312,7 @@ class C: == mod.py:9: error: Incompatible types in assignment (expression has type "int", variable has type "str") -[case testOverloadedMethodSupertype-only_when_cache] --- Different cache/no-cache tests because --- CallableType.def_extras.first_arg differs ("self"/None) +[case testOverloadedMethodSupertype] from typing import overload, Any import b class Child(b.Parent): @@ -7323,7 +7341,7 @@ class Parent: def f(self, arg: Any) -> Any: ... [out] == -main:4: error: Signature of "f" incompatible with supertype "Parent" +main:4: error: Signature of "f" incompatible with supertype "b.Parent" main:4: note: Superclass: main:4: note: @overload main:4: note: def f(self, arg: int) -> int @@ -7335,49 +7353,6 @@ main:4: note: def f(self, arg: int) -> int main:4: note: @overload main:4: note: def f(self, arg: str) -> str -[case testOverloadedMethodSupertype2-only_when_nocache] --- Different cache/no-cache tests because --- CallableType.def_extras.first_arg differs ("self"/None) -from typing import overload, Any -import b -class Child(b.Parent): - @overload # Fail - def f(self, arg: int) -> int: ... - @overload - def f(self, arg: str) -> str: ... - def f(self, arg: Any) -> Any: ... -[file b.py] -from typing import overload, Any -class C: pass -class Parent: - @overload - def f(self, arg: int) -> int: ... - @overload - def f(self, arg: str) -> str: ... - def f(self, arg: Any) -> Any: ... -[file b.py.2] -from typing import overload, Any -class C: pass -class Parent: - @overload - def f(self, arg: int) -> int: ... - @overload - def f(self, arg: str) -> C: ... - def f(self, arg: Any) -> Any: ... -[out] -== -main:4: error: Signature of "f" incompatible with supertype "Parent" -main:4: note: Superclass: -main:4: note: @overload -main:4: note: def f(self, arg: int) -> int -main:4: note: @overload -main:4: note: def f(self, arg: str) -> C -main:4: note: Subclass: -main:4: note: @overload -main:4: note: def f(arg: int) -> int -main:4: note: @overload -main:4: note: def f(arg: str) -> str - [case testOverloadedInitSupertype] import a [file a.py] @@ -7495,7 +7470,7 @@ def g() -> Tuple[str, str]: pass [builtins fixtures/tuple.pyi] [out] == -main:5: error: Incompatible return value type (got "List[str]", expected "List[int]") +main:5: error: Incompatible return value type (got "list[str]", expected "list[int]") [case testUnpackInExpression1-only_when_nocache] from typing import Tuple, List @@ -7518,8 +7493,8 @@ def t() -> Tuple[str]: ... [builtins fixtures/list.pyi] [out] == -main:5: error: Incompatible return value type (got "Tuple[int, str]", expected "Tuple[int, int]") -main:8: error: List item 1 has incompatible type "Tuple[str]"; expected "int" +main:5: error: Incompatible return value type (got "tuple[int, str]", expected "tuple[int, int]") +main:8: error: List item 1 has incompatible type "tuple[str]"; expected "int" [case testUnpackInExpression2-only_when_nocache] from typing import Set @@ -7539,7 +7514,7 @@ def t() -> Tuple[str]: pass [builtins fixtures/set.pyi] [out] == -main:5: error: Argument 2 to has incompatible type "*Tuple[str]"; expected "int" +main:5: error: Argument 2 to has incompatible type "*tuple[str]"; expected "int" [case testUnpackInExpression3-only_when_nocache] from typing import Dict @@ -7559,7 +7534,7 @@ def d() -> Dict[int, int]: pass [builtins fixtures/dict.pyi] [out] == -main:5: error: Unpacked dict entry 1 has incompatible type "Dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]" +main:5: error: Unpacked dict entry 1 has incompatible type "dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]" [case testAwaitAndAsyncDef-only_when_nocache] from a import g @@ -7751,7 +7726,7 @@ def deco(f: F) -> F: [out] main:7: error: Unsupported operand types for + ("str" and "int") == -main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "B" +main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "b.B" [case testLiskovFineVariableClean-only_when_nocache] import b @@ -7800,7 +7775,7 @@ class B: [builtins fixtures/list.pyi] [out] == -main:6: error: Incompatible types in assignment (expression has type "List[str]", base class "B" defined the type as "List[int]") +main:6: error: Incompatible types in assignment (expression has type "list[str]", base class "B" defined the type as "list[int]") [case testLiskovFineVariableCleanDefInMethodNested-only_when_nocache] from b import B @@ -7856,7 +7831,7 @@ def deco(f: F) -> F: pass [out] == -main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "B" +main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "b.B" [case testAddAbstractMethod] from b import D @@ -8046,7 +8021,7 @@ A = NamedTuple('A', F) # type: ignore [builtins fixtures/list.pyi] [out] == -b.py:3: note: Revealed type is "Tuple[(), fallback=a.A]" +b.py:3: note: Revealed type is "tuple[(), fallback=a.A]" [case testImportOnTopOfAlias1] from a import A @@ -8086,7 +8061,7 @@ def A(x: str) -> str: pass [builtins fixtures/list.pyi] [out] == -a.py:4: error: Incompatible import of "A" (imported name has type "Callable[[str], str]", local name has type "Type[List[Any]]") +a.py:4: error: Incompatible import of "A" (imported name has type "Callable[[str], str]", local name has type "type[list[Any]]") [case testFakeOverloadCrash] import b @@ -8466,9 +8441,7 @@ class D: == a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C") -[case testFinalBodyReprocessedAndStillFinalOverloaded-only_when_cache] --- Different cache/no-cache tests because --- CallableType.def_extras.first_arg differs ("self"/None) +[case testFinalBodyReprocessedAndStillFinalOverloaded] import a [file a.py] from c import C @@ -8504,7 +8477,7 @@ class D: == == a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C") -a.py:3: error: Signature of "meth" incompatible with supertype "C" +a.py:3: error: Signature of "meth" incompatible with supertype "c.C" a.py:3: note: Superclass: a.py:3: note: @overload a.py:3: note: def meth(self, x: int) -> int @@ -8513,53 +8486,6 @@ a.py:3: note: def meth(self, x: str) -> str a.py:3: note: Subclass: a.py:3: note: def meth(self) -> None -[case testFinalBodyReprocessedAndStillFinalOverloaded2-only_when_nocache] --- Different cache/no-cache tests because --- CallableType.def_extras.first_arg differs ("self"/None) -import a -[file a.py] -from c import C -class A: - def meth(self) -> None: ... - -[file a.py.3] -from c import C -class A(C): - def meth(self) -> None: ... - -[file c.py] -from typing import final, overload, Union -from d import D - -class C: - @overload - def meth(self, x: int) -> int: ... - @overload - def meth(self, x: str) -> str: ... - @final - def meth(self, x: Union[int, str]) -> Union[int, str]: - D(int()) - return x -[file d.py] -class D: - def __init__(self, x: int) -> None: ... -[file d.py.2] -from typing import Optional -class D: - def __init__(self, x: Optional[int]) -> None: ... -[out] -== -== -a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C") -a.py:3: error: Signature of "meth" incompatible with supertype "C" -a.py:3: note: Superclass: -a.py:3: note: @overload -a.py:3: note: def meth(x: int) -> int -a.py:3: note: @overload -a.py:3: note: def meth(x: str) -> str -a.py:3: note: Subclass: -a.py:3: note: def meth(self) -> None - [case testIfMypyUnreachableClass] from a import x @@ -8705,10 +8631,10 @@ reveal_type(mod.x) [file mod.py] x = 1 [file mod.py.2] -from typing_extensions import Literal +from typing import Literal x: Literal[1] = 1 [file mod.py.3] -from typing_extensions import Literal +from typing import Literal x: Literal[1] = 2 [builtins fixtures/tuple.pyi] [out] @@ -8725,10 +8651,10 @@ foo(3) [file mod.py] def foo(x: int) -> None: pass [file mod.py.2] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[3]) -> None: pass [file mod.py.3] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[4]) -> None: pass [builtins fixtures/tuple.pyi] [out] @@ -8742,10 +8668,10 @@ a: Alias = 1 [file mod.py] Alias = int [file mod.py.2] -from typing_extensions import Literal +from typing import Literal Alias = Literal[1] [file mod.py.3] -from typing_extensions import Literal +from typing import Literal Alias = Literal[2] [builtins fixtures/tuple.pyi] [out] @@ -8757,16 +8683,14 @@ main:2: error: Incompatible types in assignment (expression has type "Literal[1] from mod import foo reveal_type(foo(4)) [file mod.py] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def foo(x: int) -> str: ... @overload def foo(x: Literal['bar']) -> int: ... def foo(x): pass [file mod.py.2] -from typing import overload -from typing_extensions import Literal +from typing import Literal, overload @overload def foo(x: Literal[4]) -> Literal['foo']: ... @overload @@ -8782,7 +8706,7 @@ main:2: note: Revealed type is "Literal['foo']" [case testLiteralFineGrainedChainedDefinitions] from mod1 import foo -from typing_extensions import Literal +from typing import Literal def expect_3(x: Literal[3]) -> None: pass expect_3(foo) [file mod1.py] @@ -8791,10 +8715,10 @@ foo = bar [file mod2.py] from mod3 import qux as bar [file mod3.py] -from typing_extensions import Literal +from typing import Literal qux: Literal[3] [file mod3.py.2] -from typing_extensions import Literal +from typing import Literal qux: Literal[4] [builtins fixtures/tuple.pyi] [out] @@ -8803,7 +8727,7 @@ main:4: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expe [case testLiteralFineGrainedChainedAliases] from mod1 import Alias1 -from typing_extensions import Literal +from typing import Literal x: Alias1 def expect_3(x: Literal[3]) -> None: pass expect_3(x) @@ -8814,10 +8738,10 @@ Alias1 = Alias2 from mod3 import Alias3 Alias2 = Alias3 [file mod3.py] -from typing_extensions import Literal +from typing import Literal Alias3 = Literal[3] [file mod3.py.2] -from typing_extensions import Literal +from typing import Literal Alias3 = Literal[4] [builtins fixtures/tuple.pyi] [out] @@ -8826,7 +8750,7 @@ main:5: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expe [case testLiteralFineGrainedChainedFunctionDefinitions] from mod1 import func1 -from typing_extensions import Literal +from typing import Literal def expect_3(x: Literal[3]) -> None: pass expect_3(func1()) [file mod1.py] @@ -8835,10 +8759,10 @@ from mod2 import func2 as func1 from mod3 import func3 func2 = func3 [file mod3.py] -from typing_extensions import Literal +from typing import Literal def func3() -> Literal[3]: pass [file mod3.py.2] -from typing_extensions import Literal +from typing import Literal def func3() -> Literal[4]: pass [builtins fixtures/tuple.pyi] [out] @@ -8857,7 +8781,7 @@ foo = func(bar) [file mod2.py] bar = 3 [file mod2.py.2] -from typing_extensions import Literal +from typing import Literal bar: Literal[3] = 3 [builtins fixtures/tuple.pyi] [out] @@ -8867,23 +8791,23 @@ main:2: note: Revealed type is "Literal[3]" [case testLiteralFineGrainedChainedViaFinal] from mod1 import foo -from typing_extensions import Literal +from typing import Literal def expect_3(x: Literal[3]) -> None: pass expect_3(foo) [file mod1.py] -from typing_extensions import Final +from typing import Final from mod2 import bar foo: Final = bar [file mod2.py] from mod3 import qux as bar [file mod3.py] -from typing_extensions import Final +from typing import Final qux: Final = 3 [file mod3.py.2] -from typing_extensions import Final +from typing import Final qux: Final = 4 [file mod3.py.3] -from typing_extensions import Final +from typing import Final qux: Final[int] = 4 [builtins fixtures/tuple.pyi] [out] @@ -8899,13 +8823,13 @@ reveal_type(foo) from mod2 import bar foo = bar() [file mod2.py] -from typing_extensions import Literal +from typing import Literal def bar() -> Literal["foo"]: pass [file mod2.py.2] -from typing_extensions import Literal +from typing import Literal def bar() -> Literal[u"foo"]: pass [file mod2.py.3] -from typing_extensions import Literal +from typing import Literal def bar() -> Literal[b"foo"]: pass [builtins fixtures/tuple.pyi] [out] @@ -9583,7 +9507,7 @@ reveal_type(Foo().x) [builtins fixtures/isinstance.pyi] [out] == -b.py:2: note: Revealed type is "a." +b.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionFineGrainedIncrementalIsInstanceChange] import c @@ -9617,9 +9541,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." == -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." [case testIsInstanceAdHocIntersectionFineGrainedIncrementalUnderlyingObjChang] import c @@ -9645,9 +9569,9 @@ from b import y reveal_type(y) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "b." +c.py:2: note: Revealed type is "b." == -c.py:2: note: Revealed type is "b." +c.py:2: note: Revealed type is "b." [case testIsInstanceAdHocIntersectionFineGrainedIncrementalIntersectionToUnreachable] import c @@ -9678,7 +9602,7 @@ from b import z reveal_type(z) [builtins fixtures/isinstance.pyi] [out] -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." == c.py:2: note: Revealed type is "Any" b.py:2: error: Cannot determine type of "y" @@ -9715,7 +9639,7 @@ reveal_type(z) b.py:2: error: Cannot determine type of "y" c.py:2: note: Revealed type is "Any" == -c.py:2: note: Revealed type is "a." +c.py:2: note: Revealed type is "a." [case testStubFixupIssues] [file a.py] @@ -9925,7 +9849,7 @@ x = 0 # Arbitrary change to trigger reprocessing [builtins fixtures/dict.pyi] [out] == -a.py:3: note: Revealed type is "Tuple[Literal[1]?, Literal['x']?]" +a.py:3: note: Revealed type is "tuple[Literal[1]?, Literal['x']?]" [case testVariadicClassFineUpdateRegularToVariadic] from typing import Any @@ -10052,14 +9976,14 @@ main:4: error: "C" expects no type arguments, but 2 given [case testUnpackKwargsUpdateFine] import m [file shared.py] -from typing_extensions import TypedDict +from typing import TypedDict class Person(TypedDict): name: str age: int [file shared.py.2] -from typing_extensions import TypedDict +from typing import TypedDict class Person(TypedDict): name: str @@ -10076,6 +10000,7 @@ from lib import foo foo(name='Jennifer', age=38) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] == m.py:2: error: Argument "age" to "foo" has incompatible type "int"; expected "str" @@ -10235,14 +10160,14 @@ object + 1 1() [out] -b.py:1: error: Unsupported left operand type for + ("Type[object]") +b.py:1: error: Unsupported left operand type for + ("type[object]") object + 1 ^~~~~~~~~~ a.py:1: error: Unsupported operand types for + ("int" and "str") 1 + '' ^~ == -b.py:1: error: Unsupported left operand type for + ("Type[object]") +b.py:1: error: Unsupported left operand type for + ("type[object]") object + 1 ^~~~~~~~~~ b.py:2: error: "int" not callable @@ -10381,7 +10306,7 @@ import n import m x: m.TD [file m.py] -from typing_extensions import TypedDict +from typing import TypedDict from f import A class TD(TypedDict): @@ -10394,6 +10319,7 @@ A = int [file f.py.2] A = str [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] m.py:5: error: Invalid statement in TypedDict definition; expected "field_name: field_type" == @@ -10422,14 +10348,14 @@ D = "y" C = str D = int [out] -a.py:4: note: Revealed type is "Union[builtins.int, builtins.str]" +a.py:4: note: Revealed type is "builtins.int | builtins.str" == a.py:2: error: Unsupported left operand type for | ("str") a.py:3: error: Variable "a.A" is not valid as a type a.py:3: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases a.py:4: note: Revealed type is "A?" == -a.py:4: note: Revealed type is "Union[builtins.str, builtins.int]" +a.py:4: note: Revealed type is "builtins.str | builtins.int" [case testUnionOfSimilarCallablesCrash] import b @@ -10526,6 +10452,30 @@ from pkg.sub import modb [out] == +[case testUnusedTypeIgnorePreservedAfterChange] +# flags: --warn-unused-ignores --no-error-summary +[file main.py] +a = 1 # type: ignore +[file main.py.2] +a = 1 # type: ignore +# Comment to trigger reload. +[out] +main.py:1: error: Unused "type: ignore" comment +== +main.py:1: error: Unused "type: ignore" comment + +[case testTypeIgnoreWithoutCodePreservedAfterChange] +# flags: --enable-error-code ignore-without-code --no-error-summary +[file main.py] +a = 1 # type: ignore +[file main.py.2] +a = 1 # type: ignore +# Comment to trigger reload. +[out] +main.py:1: error: "type: ignore" comment without error code +== +main.py:1: error: "type: ignore" comment without error code + [case testFineGrainedFunctoolsPartial] import m @@ -10573,3 +10523,998 @@ 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 testDeprecatedAddKeepChangeAndRemoveOverloadedFunctionDeprecation] +# flags: --enable-error-code=deprecated + +from a import f +f(1) +f("y") +import a +a.f(1) +a.f("y") + +[file a.py] +from typing import overload, Union +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.3] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.4] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int, please") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.5] +from typing import overload, Union +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please +== + + +[case testDeprecatedRemoveOverloadedFunctionDeprecation] +# flags: --enable-error-code=deprecated + +from a import f +f(1) +f("y") +import a +a.f(1) +a.f("y") + +[file a.py] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import overload, Union +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +== + + +[case testDeprecatedKeepOverloadedFunctionDeprecation] +# flags: --enable-error-code=deprecated + +from a import f +f(1) +f("y") +import a +a.f(1) +a.f("y") + +[file a.py] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int + + +[case testDeprecatedAddOverloadedFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated + +from b import f +f(1) +f("y") +import b +b.f(1) +b.f("y") + +[file b.py] +from a import f + +[file a.py] +from typing import overload, Union +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int + + +[case testDeprecatedChangeOverloadedFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated + +from b import f +f(1) +f("y") +import b +b.f(1) +b.f("y") + +[file b.py] +from a import f + +[file a.py] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int, please") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int, please + + +[case testDeprecatedRemoveOverloadedFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated + +from b import f +f(1) +f("y") +import b +b.f(1) +b.f("y") + +[file b.py] +from a import f + +[file a.py] +from typing import overload, Union +from typing_extensions import deprecated +@overload +def f(x: int) -> int: ... +@overload +@deprecated("pass int") +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import overload, Union +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: pass int +== + + +[case testDeprecatedOverloadedFunctionAlreadyDecorated] +# flags: --enable-error-code=deprecated + +from b import f +f(1) +f("y") +import b +b.f(1) +b.f("y") + +[file b.py] +from a import f + +[file a.py] +from typing import Callable, overload, Union + +def d(t: Callable[[str], str]) -> Callable[[str], str]: ... + +@overload +def f(x: int) -> int: ... +@overload +@d +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[file a.py.2] +from typing import Callable, overload, Union +from typing_extensions import deprecated + +def d(t: Callable[[str], str]) -> Callable[[str], str]: ... + +@overload +def f(x: int) -> int: ... +@overload +@deprecated("deprecated decorated overload") +@d +def f(x: str) -> str: ... +def f(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:5: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: deprecated decorated overload +main:8: error: overload def (x: builtins.str) -> builtins.str of function a.f is deprecated: deprecated decorated overload + + +[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 + +[case testPropertySetterTypeFineGrained] +from a import A +a = A() +a.f = '' +[file a.py] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: str) -> None: + pass +[file a.py.2] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: int) -> None: + pass +[builtins fixtures/property.pyi] +[out] +== +main:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testPropertyDeleteSetterFineGrained] +from a import A +a = A() +a.f = 1 +[file a.py] +class A: + @property + def f(self) -> int: + return 1 + @f.setter + def f(self, x: int) -> None: + pass +[file a.py.2] +class A: + @property + def f(self) -> int: + return 1 + @f.deleter + def f(self) -> None: + pass +[builtins fixtures/property.pyi] +[out] +== +main:3: error: Property "f" defined in "A" is read-only + +[case testMethodMakeBoundFineGrained] +from a import A +a = A() +a.f() +[file a.py] +class B: + def f(self, s: A) -> int: ... + +def f(s: A) -> int: ... + +class A: + f = f +[file a.py.2] +class B: + def f(self, s: A) -> int: ... + +def f(s: A) -> int: ... + +class A: + f = B().f +[out] +== +main:3: error: Too few arguments + +[case testFineGrainedParamSpecPrefixUpdateMethod] +import impl +[file impl.py] +from typing_extensions import ParamSpec +from lib import Sub + +P = ParamSpec("P") +class Impl(Sub[P]): + def test(self, *args: P.args, **kwargs: P.kwargs) -> None: + self.meth(1, *args, **kwargs) + +[file lib.py] +from typing import Generic +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +class Base(Generic[P]): + def meth(self, *args: P.args, **kwargs: P.kwargs) -> None: ... +class Sub(Base[Concatenate[int, P]]): ... + +[file lib.py.2] +from typing import Generic +from typing_extensions import ParamSpec, Concatenate + +P = ParamSpec("P") +class Base(Generic[P]): + def meth(self, *args: P.args, **kwargs: P.kwargs) -> None: ... +class Sub(Base[Concatenate[str, P]]): ... +[builtins fixtures/paramspec.pyi] +[out] +== +impl.py:7: error: Argument 1 to "meth" of "Base" has incompatible type "int"; expected "str" diff --git a/test-data/unit/fixtures/__new__.pyi b/test-data/unit/fixtures/__new__.pyi index 401de6fb9cd1b..57d3624ce92cf 100644 --- a/test-data/unit/fixtures/__new__.pyi +++ b/test-data/unit/fixtures/__new__.pyi @@ -12,6 +12,7 @@ class object: class type: def __init__(self, x) -> None: pass +class float: pass class int: pass class bool: pass class str: pass diff --git a/test-data/unit/fixtures/enum.pyi b/test-data/unit/fixtures/enum.pyi new file mode 100644 index 0000000000000..22e7193da0415 --- /dev/null +++ b/test-data/unit/fixtures/enum.pyi @@ -0,0 +1,25 @@ +# Minimal set of builtins required to work with Enums +from typing import TypeVar, Generic, Iterator, Sequence, overload, Iterable + +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 + def __iter__(self) -> Iterator[str]: pass + +class dict: pass +class ellipsis: pass + +class list(Sequence[T]): + @overload + def __init__(self) -> None: pass + @overload + def __init__(self, x: Iterable[T]) -> None: pass diff --git a/test-data/unit/fixtures/exception.pyi b/test-data/unit/fixtures/exception.pyi index 08496e4e59345..963192cc86ab3 100644 --- a/test-data/unit/fixtures/exception.pyi +++ b/test-data/unit/fixtures/exception.pyi @@ -12,6 +12,7 @@ class list: pass class dict: pass class function: pass class int: pass +class float: pass class str: pass class bool: pass class ellipsis: pass diff --git a/test-data/unit/fixtures/float.pyi b/test-data/unit/fixtures/float.pyi index 5db4525849c08..9e2d20f04edf4 100644 --- a/test-data/unit/fixtures/float.pyi +++ b/test-data/unit/fixtures/float.pyi @@ -1,8 +1,6 @@ -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Any T = TypeVar('T') -Any = 0 - class object: def __init__(self) -> None: pass diff --git a/test-data/unit/fixtures/floatdict.pyi b/test-data/unit/fixtures/floatdict.pyi index 7baa7ca9206fc..10586218b5512 100644 --- a/test-data/unit/fixtures/floatdict.pyi +++ b/test-data/unit/fixtures/floatdict.pyi @@ -1,11 +1,9 @@ -from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union +from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union, Any T = TypeVar('T') KT = TypeVar('KT') VT = TypeVar('VT') -Any = 0 - class object: def __init__(self) -> None: pass diff --git a/test-data/unit/fixtures/for.pyi b/test-data/unit/fixtures/for.pyi index 694f83e940b2c..80c8242c2a5e7 100644 --- a/test-data/unit/fixtures/for.pyi +++ b/test-data/unit/fixtures/for.pyi @@ -12,9 +12,12 @@ 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 float: 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/isinstancelist.pyi b/test-data/unit/fixtures/isinstancelist.pyi index 0ee5258ff74b0..2a43606f361a3 100644 --- a/test-data/unit/fixtures/isinstancelist.pyi +++ b/test-data/unit/fixtures/isinstancelist.pyi @@ -26,6 +26,7 @@ class bool(int): pass class str: def __add__(self, x: str) -> str: pass def __getitem__(self, x: int) -> str: pass +class bytes: pass T = TypeVar('T') KT = TypeVar('KT') @@ -52,6 +53,7 @@ class dict(Mapping[KT, VT]): def __setitem__(self, k: KT, v: VT) -> None: pass def __iter__(self) -> Iterator[KT]: pass def update(self, a: Mapping[KT, VT]) -> None: pass + def items(self) -> Iterable[Tuple[KT, VT]]: pass class set(Generic[T]): def __iter__(self) -> Iterator[T]: pass diff --git a/test-data/unit/fixtures/literal__new__.pyi b/test-data/unit/fixtures/literal__new__.pyi new file mode 100644 index 0000000000000..971bc39bfff4b --- /dev/null +++ b/test-data/unit/fixtures/literal__new__.pyi @@ -0,0 +1,25 @@ +from typing import Literal, Protocol, overload + +class object: + def __init__(self) -> None: pass + +class type: + def __init__(self, x) -> None: pass + +class str: pass +class dict: pass +class float: pass +class int: + def __new__(cls) -> Literal[0]: pass + +class _Truthy(Protocol): + def __bool__(self) -> Literal[True]: pass + +class _Falsy(Protocol): + def __bool__(self) -> Literal[False]: pass + +class bool(int): + @overload + def __new__(cls, __o: _Truthy) -> Literal[True]: pass + @overload + def __new__(cls, __o: _Falsy) -> Literal[False]: pass diff --git a/test-data/unit/fixtures/module.pyi b/test-data/unit/fixtures/module.pyi index 47408befd5ceb..92f78a42f92fe 100644 --- a/test-data/unit/fixtures/module.pyi +++ b/test-data/unit/fixtures/module.pyi @@ -4,13 +4,14 @@ from types import ModuleType T = TypeVar('T') S = TypeVar('S') -class list(Generic[T], Sequence[T]): pass +class list(Generic[T], Sequence[T]): pass # type: ignore class object: def __init__(self) -> None: pass class type: pass class function: pass class int: pass +class float: pass class str: pass class bool: pass class tuple(Generic[T]): pass diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index 2ca376ea07605..c9e58f0994774 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,17 @@ class function: pass class bool: pass class int: pass class str: pass -NotImplemented = cast(Any, None) class dict: pass +class tuple: pass +class ellipsis: pass + +import sys + +if sys.version_info >= (3, 10): # type: ignore + from types import NotImplementedType + NotImplemented: NotImplementedType +else: + class _NotImplementedType(Any): ... + NotImplemented: _NotImplementedType + +class BaseException: pass diff --git a/test-data/unit/fixtures/ops.pyi b/test-data/unit/fixtures/ops.pyi index df3b163166ad4..67bc74b35c51d 100644 --- a/test-data/unit/fixtures/ops.pyi +++ b/test-data/unit/fixtures/ops.pyi @@ -25,7 +25,7 @@ class tuple(Sequence[Tco]): class function: pass class str: - def __init__(self, x: 'int') -> None: pass + def __init__(self, x: 'int' = ...) -> None: pass def __add__(self, x: 'str') -> 'str': pass def __eq__(self, x: object) -> bool: pass def startswith(self, x: 'str') -> bool: pass diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index 63128a8ae03d7..98e604e9e81ef 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -13,15 +13,19 @@ class object: class type: def __init__(self, x: object) -> None: pass + # Real implementation returns UnionType + def __or__(self, value: object, /) -> object: pass class int: # Note: this is a simplification of the actual signature def __init__(self, x: object = ..., base: int = ...) -> None: pass def __add__(self, i: int) -> int: pass def __rmul__(self, x: int) -> int: pass + def __bool__(self) -> bool: pass class float: def __float__(self) -> float: pass def __add__(self, x: float) -> float: pass + def hex(self) -> str: pass class complex: def __add__(self, x: complex) -> complex: pass class bool(int): pass @@ -48,6 +52,7 @@ class memoryview(Sequence[int]): class tuple(Generic[T]): def __contains__(self, other: object) -> bool: pass class list(Sequence[T]): + def append(self, v: T) -> None: pass def __iter__(self) -> Iterator[T]: pass def __contains__(self, other: object) -> bool: pass def __getitem__(self, item: int) -> T: pass @@ -69,3 +74,5 @@ class range(Sequence[int]): def __contains__(self, other: object) -> bool: pass def isinstance(x: object, t: Union[type, Tuple]) -> bool: pass + +class BaseException: pass diff --git a/test-data/unit/fixtures/property.pyi b/test-data/unit/fixtures/property.pyi index 667bdc02d0f58..933868ac9907b 100644 --- a/test-data/unit/fixtures/property.pyi +++ b/test-data/unit/fixtures/property.pyi @@ -13,7 +13,7 @@ class function: pass property = object() # Dummy definition class classmethod: pass -class list: pass +class list(typing.Generic[_T]): pass class dict: pass class int: pass class float: pass diff --git a/test-data/unit/fixtures/set.pyi b/test-data/unit/fixtures/set.pyi index 71d3bd2eee185..f757679a95f4e 100644 --- a/test-data/unit/fixtures/set.pyi +++ b/test-data/unit/fixtures/set.pyi @@ -13,6 +13,7 @@ class tuple(Generic[T]): pass class function: pass class int: pass +class float: pass class str: pass class bool: pass class ellipsis: 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.pyi b/test-data/unit/fixtures/tuple.pyi index 3b62d7fc1513c..06dfcf5d0fbc1 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -1,18 +1,20 @@ # Builtins stub used in tuple-related test cases. import _typeshed -from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type +from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type, Self _T = TypeVar("_T") _Tco = TypeVar('_Tco', covariant=True) class object: def __init__(self) -> None: pass + def __new__(cls) -> Self: ... class type: def __init__(self, *a: object) -> None: pass def __call__(self, *a: object) -> object: pass class tuple(Sequence[_Tco], Generic[_Tco]): + def __hash__(self) -> int: ... def __new__(cls: Type[_T], iterable: Iterable[_Tco] = ...) -> _T: ... def __iter__(self) -> Iterator[_Tco]: pass def __contains__(self, item: object) -> bool: pass diff --git a/test-data/unit/fixtures/typing-async.pyi b/test-data/unit/fixtures/typing-async.pyi index 03728f8223162..66509a91b82b7 100644 --- a/test-data/unit/fixtures/typing-async.pyi +++ b/test-data/unit/fixtures/typing-async.pyi @@ -28,7 +28,9 @@ Self = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) +R_co = TypeVar('R_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) +S_contra = TypeVar('S_contra', contravariant=True) U = TypeVar('U') V = TypeVar('V') S = TypeVar('S') @@ -49,9 +51,9 @@ class Iterator(Iterable[T_co], Protocol): @abstractmethod def __next__(self) -> T_co: pass -class Generator(Iterator[T], Generic[T, U, V]): +class Generator(Iterator[T_co], Generic[T_co, S_contra, R_co]): @abstractmethod - def send(self, value: U) -> T: pass + def send(self, value: S_contra) -> T_co: pass @abstractmethod def throw(self, typ: Any, val: Any=None, tb: Any=None) -> None: pass @@ -60,34 +62,39 @@ class Generator(Iterator[T], Generic[T, U, V]): def close(self) -> None: pass @abstractmethod - def __iter__(self) -> 'Generator[T, U, V]': pass + def __iter__(self) -> 'Generator[T_co, S_contra, R_co]': pass -class AsyncGenerator(AsyncIterator[T], Generic[T, U]): +class AsyncGenerator(AsyncIterator[T_co], Generic[T_co, S_contra]): @abstractmethod - def __anext__(self) -> Awaitable[T]: pass + def __anext__(self) -> Awaitable[T_co]: pass @abstractmethod - def asend(self, value: U) -> Awaitable[T]: pass + def asend(self, value: S_contra) -> Awaitable[T_co]: pass @abstractmethod - def athrow(self, typ: Any, val: Any=None, tb: Any=None) -> Awaitable[T]: pass + def athrow(self, typ: Any, val: Any=None, tb: Any=None) -> Awaitable[T_co]: pass @abstractmethod - def aclose(self) -> Awaitable[T]: pass + def aclose(self) -> Awaitable[T_co]: pass @abstractmethod - def __aiter__(self) -> 'AsyncGenerator[T, U]': pass + def __aiter__(self) -> 'AsyncGenerator[T_co, S_contra]': pass -class Awaitable(Protocol[T]): +class Awaitable(Protocol[T_co]): @abstractmethod - def __await__(self) -> Generator[Any, Any, T]: pass + def __await__(self) -> Generator[Any, Any, T_co]: pass -class AwaitableGenerator(Generator[T, U, V], Awaitable[V], Generic[T, U, V, S], metaclass=ABCMeta): +class AwaitableGenerator( + Awaitable[R_co], + Generator[T_co, S_contra, R_co], + Generic[T_co, S_contra, R_co, S], + metaclass=ABCMeta +): pass -class Coroutine(Awaitable[V], Generic[T, U, V]): +class Coroutine(Awaitable[R_co], Generic[T_co, S_contra, R_co]): @abstractmethod - def send(self, value: U) -> T: pass + def send(self, value: S_contra) -> T_co: pass @abstractmethod def throw(self, typ: Any, val: Any=None, tb: Any=None) -> None: pass @@ -95,14 +102,14 @@ class Coroutine(Awaitable[V], Generic[T, U, V]): @abstractmethod def close(self) -> None: pass -class AsyncIterable(Protocol[T]): +class AsyncIterable(Protocol[T_co]): @abstractmethod - def __aiter__(self) -> 'AsyncIterator[T]': pass + def __aiter__(self) -> 'AsyncIterator[T_co]': pass -class AsyncIterator(AsyncIterable[T], Protocol): - def __aiter__(self) -> 'AsyncIterator[T]': return self +class AsyncIterator(AsyncIterable[T_co], Protocol): + def __aiter__(self) -> 'AsyncIterator[T_co]': return self @abstractmethod - def __anext__(self) -> Awaitable[T]: pass + def __anext__(self) -> Awaitable[T_co]: pass class Sequence(Iterable[T_co], Container[T_co]): @abstractmethod @@ -116,13 +123,13 @@ class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): @overload def get(self, k: T, default: Union[T_co, V]) -> Union[T_co, V]: pass -class ContextManager(Generic[T]): - def __enter__(self) -> T: pass +class ContextManager(Generic[T_co]): + def __enter__(self) -> T_co: pass # Use Any because not all the precise types are in the fixtures. def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any: pass -class AsyncContextManager(Generic[T]): - def __aenter__(self) -> Awaitable[T]: pass +class AsyncContextManager(Generic[T_co]): + def __aenter__(self) -> Awaitable[T_co]: pass # Use Any because not all the precise types are in the fixtures. def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Awaitable[Any]: pass diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 8e0116aab1c29..87b66c0cd857e 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -30,10 +30,12 @@ Protocol = 0 Tuple = 0 _promote = 0 Type = 0 +TypeForm = 0 no_type_check = 0 ClassVar = 0 Final = 0 TypedDict = 0 +TypeGuard = 0 NoReturn = 0 NewType = 0 Self = 0 @@ -44,7 +46,8 @@ Literal: _SpecialForm T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) -T_contra = TypeVar('T_contra', contravariant=True) +R_co = TypeVar('R_co', covariant=True) +S_contra = TypeVar('S_contra', contravariant=True) U = TypeVar('U') V = TypeVar('V') S = TypeVar('S') @@ -82,9 +85,9 @@ class Iterator(Iterable[T_co], Protocol): @abstractmethod def __next__(self) -> T_co: pass -class Generator(Iterator[T], Generic[T, U, V]): +class Generator(Iterator[T_co], Generic[T_co, S_contra, R_co]): @abstractmethod - def send(self, value: U) -> T: pass + def send(self, value: S_contra) -> T_co: pass @abstractmethod def throw(self, typ: Any, val: Any=None, tb: Any=None) -> None: pass @@ -93,35 +96,40 @@ class Generator(Iterator[T], Generic[T, U, V]): def close(self) -> None: pass @abstractmethod - def __iter__(self) -> 'Generator[T, U, V]': pass + def __iter__(self) -> 'Generator[T_co, S_contra, R_co]': pass -class AsyncGenerator(AsyncIterator[T], Generic[T, U]): +class AsyncGenerator(AsyncIterator[T_co], Generic[T_co, S_contra]): @abstractmethod - def __anext__(self) -> Awaitable[T]: pass + def __anext__(self) -> Awaitable[T_co]: pass @abstractmethod - def asend(self, value: U) -> Awaitable[T]: pass + def asend(self, value: S_contra) -> Awaitable[T_co]: pass @abstractmethod - def athrow(self, typ: Any, val: Any=None, tb: Any=None) -> Awaitable[T]: pass + def athrow(self, typ: Any, val: Any=None, tb: Any=None) -> Awaitable[T_co]: pass @abstractmethod - def aclose(self) -> Awaitable[T]: pass + def aclose(self) -> Awaitable[T_co]: pass @abstractmethod - def __aiter__(self) -> 'AsyncGenerator[T, U]': pass + def __aiter__(self) -> 'AsyncGenerator[T_co, S_contra]': pass @runtime_checkable -class Awaitable(Protocol[T]): +class Awaitable(Protocol[T_co]): @abstractmethod - def __await__(self) -> Generator[Any, Any, T]: pass + def __await__(self) -> Generator[Any, Any, T_co]: pass -class AwaitableGenerator(Generator[T, U, V], Awaitable[V], Generic[T, U, V, S], metaclass=ABCMeta): +class AwaitableGenerator( + Awaitable[R_co], + Generator[T_co, S_contra, R_co], + Generic[T_co, S_contra, R_co, S], + metaclass=ABCMeta +): pass -class Coroutine(Awaitable[V], Generic[T, U, V]): +class Coroutine(Awaitable[R_co], Generic[T_co, S_contra, R_co]): @abstractmethod - def send(self, value: U) -> T: pass + def send(self, value: S_contra) -> T_co: pass @abstractmethod def throw(self, typ: Any, val: Any=None, tb: Any=None) -> None: pass @@ -130,15 +138,15 @@ class Coroutine(Awaitable[V], Generic[T, U, V]): def close(self) -> None: pass @runtime_checkable -class AsyncIterable(Protocol[T]): +class AsyncIterable(Protocol[T_co]): @abstractmethod - def __aiter__(self) -> 'AsyncIterator[T]': pass + def __aiter__(self) -> 'AsyncIterator[T_co]': pass @runtime_checkable -class AsyncIterator(AsyncIterable[T], Protocol): - def __aiter__(self) -> 'AsyncIterator[T]': return self +class AsyncIterator(AsyncIterable[T_co], Protocol): + def __aiter__(self) -> 'AsyncIterator[T_co]': return self @abstractmethod - def __anext__(self) -> Awaitable[T]: pass + def __anext__(self) -> Awaitable[T_co]: pass class Sequence(Iterable[T_co], Container[T_co]): @abstractmethod diff --git a/test-data/unit/fixtures/typing-medium.pyi b/test-data/unit/fixtures/typing-medium.pyi index c722a9ddb12c8..077d4eebf7d39 100644 --- a/test-data/unit/fixtures/typing-medium.pyi +++ b/test-data/unit/fixtures/typing-medium.pyi @@ -32,10 +32,8 @@ Self = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) -T_contra = TypeVar('T_contra', contravariant=True) -U = TypeVar('U') -V = TypeVar('V') -S = TypeVar('S') +R_co = TypeVar('R_co', covariant=True) +S_contra = TypeVar('S_contra', contravariant=True) # Note: definitions below are different from typeshed, variances are declared # to silence the protocol variance checks. Maybe it is better to use type: ignore? @@ -49,8 +47,8 @@ class Iterable(Protocol[T_co]): class Iterator(Iterable[T_co], Protocol): def __next__(self) -> T_co: pass -class Generator(Iterator[T], Generic[T, U, V]): - def __iter__(self) -> 'Generator[T, U, V]': pass +class Generator(Iterator[T_co], Generic[T_co, S_contra, R_co]): + def __iter__(self) -> 'Generator[T_co, S_contra, R_co]': pass class Sequence(Iterable[T_co]): def __getitem__(self, n: Any) -> T_co: pass @@ -65,8 +63,8 @@ class SupportsInt(Protocol): class SupportsFloat(Protocol): def __float__(self) -> float: pass -class ContextManager(Generic[T]): - def __enter__(self) -> T: pass +class ContextManager(Generic[T_co]): + def __enter__(self) -> T_co: pass # Use Any because not all the precise types are in the fixtures. def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any: pass diff --git a/test-data/unit/fixtures/typing-namedtuple.pyi b/test-data/unit/fixtures/typing-namedtuple.pyi index bcdcfc44c3d21..5b0ef0845dadf 100644 --- a/test-data/unit/fixtures/typing-namedtuple.pyi +++ b/test-data/unit/fixtures/typing-namedtuple.pyi @@ -8,6 +8,7 @@ Optional = 0 Self = 0 Tuple = 0 ClassVar = 0 +Final = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -17,8 +18,8 @@ class Iterable(Generic[T_co]): pass class Iterator(Iterable[T_co]): pass class Sequence(Iterable[T_co]): pass class Mapping(Iterable[KT], Generic[KT, T_co]): - def keys(self) -> Iterable[T]: pass # Approximate return type - def __getitem__(self, key: T) -> T_co: pass + def keys(self) -> Iterable[KT]: pass # Approximate return type + def __getitem__(self, key: KT) -> T_co: pass class NamedTuple(tuple[Any, ...]): _fields: ClassVar[tuple[str, ...]] diff --git a/test-data/unit/fixtures/typing-override.pyi b/test-data/unit/fixtures/typing-override.pyi index e9d2dfcf55c45..a0287524c84a8 100644 --- a/test-data/unit/fixtures/typing-override.pyi +++ b/test-data/unit/fixtures/typing-override.pyi @@ -18,8 +18,8 @@ class Iterable(Generic[T_co]): pass class Iterator(Iterable[T_co]): pass class Sequence(Iterable[T_co]): pass class Mapping(Iterable[KT], Generic[KT, T_co]): - def keys(self) -> Iterable[T]: pass # Approximate return type - def __getitem__(self, key: T) -> T_co: pass + def keys(self) -> Iterable[KT]: pass # Approximate return type + def __getitem__(self, key: KT) -> T_co: pass def override(__arg: T) -> T: ... diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index 7e9c642cf261a..29635b6518706 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -24,10 +24,12 @@ Final = 0 Literal = 0 TypedDict = 0 NoReturn = 0 +NewType = 0 Required = 0 NotRequired = 0 ReadOnly = 0 Self = 0 +ClassVar = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -46,8 +48,7 @@ class Iterator(Iterable[T_co], Protocol): def __next__(self) -> T_co: pass class Sequence(Iterable[T_co]): - # misc is for explicit Any. - def __getitem__(self, n: Any) -> T_co: pass # type: ignore[misc] + def __getitem__(self, n: Any) -> T_co: pass # type: ignore[explicit-any] class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): def keys(self) -> Iterable[T]: pass # Approximate return type @@ -55,12 +56,14 @@ class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): @overload def get(self, k: T) -> Optional[T_co]: pass @overload - def get(self, k: T, default: Union[T_co, V]) -> Union[T_co, V]: pass + def get(self, k: T, default: T_co, /) -> Optional[T_co]: pass # type: ignore[misc] + @overload + def get(self, k: T, default: V, /) -> Union[T_co, V]: pass def values(self) -> Iterable[T_co]: pass # Approximate return type def __len__(self) -> int: ... def __contains__(self, arg: object) -> int: pass -class MutableMapping(Mapping[T, T_co], Generic[T, T_co], metaclass=ABCMeta): +class MutableMapping(Mapping[T, V], Generic[T, V], metaclass=ABCMeta): # Other methods are not used in tests. def clear(self) -> None: ... diff --git a/test-data/unit/lib-stub/_weakref.pyi b/test-data/unit/lib-stub/_weakref.pyi new file mode 100644 index 0000000000000..50c59b65e2677 --- /dev/null +++ b/test-data/unit/lib-stub/_weakref.pyi @@ -0,0 +1,11 @@ +from typing import Any, Callable, TypeVar, overload +from weakref import CallableProxyType, ProxyType + +_C = TypeVar("_C", bound=Callable[..., Any]) +_T = TypeVar("_T") + +# Return CallableProxyType if object is callable, ProxyType otherwise +@overload +def proxy(object: _C, callback: Callable[[CallableProxyType[_C]], Any] | None = None, /) -> CallableProxyType[_C]: ... +@overload +def proxy(object: _T, callback: Callable[[ProxyType[_T]], Any] | None = None, /) -> ProxyType[_T]: ... diff --git a/test-data/unit/lib-stub/enum.pyi b/test-data/unit/lib-stub/enum.pyi index 0e0b8e025d9f2..5047f7083804a 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 @@ -27,6 +29,7 @@ class Enum(metaclass=EnumMeta): class IntEnum(int, Enum): value: int + _value_: int def __new__(cls: Type[_T], value: Union[int, _T]) -> _T: ... def unique(enumeration: _T) -> _T: pass @@ -34,6 +37,8 @@ def unique(enumeration: _T) -> _T: pass # In reality Flag and IntFlag are 3.6 only class Flag(Enum): + value: int + _value_: int def __or__(self: _T, other: Union[int, _T]) -> _T: pass @@ -47,6 +52,8 @@ class auto(IntFlag): # It is python-3.11+ only: class StrEnum(str, Enum): + _value_: str + value: str def __new__(cls: Type[_T], value: str | _T) -> _T: ... # It is python-3.11+ only: diff --git a/test-data/unit/lib-stub/native_internal.pyi b/test-data/unit/lib-stub/native_internal.pyi new file mode 100644 index 0000000000000..a47a4849fe204 --- /dev/null +++ b/test-data/unit/lib-stub/native_internal.pyi @@ -0,0 +1,16 @@ +from mypy_extensions import u8 + +class Buffer: + def __init__(self, source: bytes = ...) -> None: ... + def getvalue(self) -> bytes: ... + +def write_bool(data: Buffer, value: bool) -> None: ... +def read_bool(data: Buffer) -> bool: ... +def write_str(data: Buffer, value: str) -> None: ... +def read_str(data: Buffer) -> str: ... +def write_float(data: Buffer, value: float) -> None: ... +def read_float(data: Buffer) -> float: ... +def write_int(data: Buffer, value: int) -> None: ... +def read_int(data: Buffer) -> int: ... +def write_tag(data: Buffer, value: u8) -> None: ... +def read_tag(data: Buffer) -> u8: ... diff --git a/test-data/unit/lib-stub/typing.pyi b/test-data/unit/lib-stub/typing.pyi index 3cb164140883d..00fce56920b75 100644 --- a/test-data/unit/lib-stub/typing.pyi +++ b/test-data/unit/lib-stub/typing.pyi @@ -23,6 +23,7 @@ NamedTuple = 0 Type = 0 ClassVar = 0 Final = 0 +Literal = 0 NoReturn = 0 Never = 0 NewType = 0 @@ -34,8 +35,8 @@ TYPE_CHECKING = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) -U = TypeVar('U') -V = TypeVar('V') +S_contra = TypeVar('S_contra', contravariant=True) +R_co = TypeVar('R_co', covariant=True) class Iterable(Protocol[T_co]): def __iter__(self) -> Iterator[T_co]: pass @@ -43,8 +44,8 @@ class Iterable(Protocol[T_co]): class Iterator(Iterable[T_co], Protocol): def __next__(self) -> T_co: pass -class Generator(Iterator[T], Generic[T, U, V]): - def __iter__(self) -> Generator[T, U, V]: pass +class Generator(Iterator[T_co], Generic[T_co, S_contra, R_co]): + def __iter__(self) -> Generator[T_co, S_contra, R_co]: pass class Sequence(Iterable[T_co]): def __getitem__(self, n: Any) -> T_co: pass @@ -55,10 +56,10 @@ class Mapping(Iterable[T], Generic[T, T_co]): def keys(self) -> Iterable[T]: pass # Approximate return type def __getitem__(self, key: T) -> T_co: pass -class Awaitable(Protocol[T]): - def __await__(self) -> Generator[Any, Any, T]: pass +class Awaitable(Protocol[T_co]): + def __await__(self) -> Generator[Any, Any, T_co]: pass -class Coroutine(Awaitable[V], Generic[T, U, V]): pass +class Coroutine(Awaitable[R_co], Generic[T_co, S_contra, R_co]): pass def final(meth: T) -> T: pass diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index d9d7067efe0fa..71a17a939d41a 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -33,6 +33,8 @@ Concatenate: _SpecialForm TypeAlias: _SpecialForm +TypeForm: _SpecialForm + TypeGuard: _SpecialForm TypeIs: _SpecialForm Never: _SpecialForm @@ -43,6 +45,8 @@ Required: _SpecialForm NotRequired: _SpecialForm ReadOnly: _SpecialForm +Self: _SpecialForm + @final class TypeAliasType: def __init__( @@ -91,5 +95,6 @@ def dataclass_transform( def override(__arg: _T) -> _T: ... def deprecated(__msg: str) -> Callable[[_T], _T]: ... +def disjoint_base(__arg: _T) -> _T: ... _FutureFeatureFixture = 0 diff --git a/test-data/unit/lib-stub/weakref.pyi b/test-data/unit/lib-stub/weakref.pyi new file mode 100644 index 0000000000000..7d11b65d45481 --- /dev/null +++ b/test-data/unit/lib-stub/weakref.pyi @@ -0,0 +1,23 @@ +from _weakref import proxy +from collections.abc import Callable +from typing import Any, ClassVar, Generic, TypeVar, final +from typing_extensions import Self + +_C = TypeVar("_C", bound=Callable[..., Any]) +_T = TypeVar("_T") + +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: ... + +ref = ReferenceType + +@final +class CallableProxyType(Generic[_C]): # "weakcallableproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + __call__: _C + __hash__: ClassVar[None] # type: ignore[assignment] + +__all__ = ["proxy"] diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index 19b1839f86c0d..7463571b76b4e 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -671,12 +671,12 @@ TypeInfo<2>( _NT<6> __annotations__<7> (builtins.dict[builtins.str<8>, Any]<9>) __doc__<10> (builtins.str<8>) - __match_args__<11> (Tuple[Literal['x']]) + __match_args__<11> (tuple[Literal['x']]) __new__<12> _asdict<13> _field_defaults<14> (builtins.dict[builtins.str<8>, Any]<9>) _field_types<15> (builtins.dict[builtins.str<8>, Any]<9>) - _fields<16> (Tuple[builtins.str<8>]) + _fields<16> (tuple[builtins.str<8>]) _make<17> _replace<18> _source<19> (builtins.str<8>) @@ -695,12 +695,12 @@ TypeInfo<2>( _NT<6> __annotations__<7> (builtins.dict[builtins.str<8>, Any]<9>) __doc__<10> (builtins.str<8>) - __match_args__<11> (Tuple[Literal['x'], Literal['y']]) + __match_args__<11> (tuple[Literal['x'], Literal['y']]) __new__<12> _asdict<13> _field_defaults<14> (builtins.dict[builtins.str<8>, Any]<9>) _field_types<15> (builtins.dict[builtins.str<8>, Any]<9>) - _fields<16> (Tuple[builtins.str<8>, builtins.str<8>]) + _fields<16> (tuple[builtins.str<8>, builtins.str<8>]) _make<17> _replace<18> _source<19> (builtins.str<8>) @@ -736,7 +736,7 @@ TypeInfo<2>( _asdict<12> _field_defaults<13> (builtins.dict[builtins.str<8>, Any]<9>) _field_types<14> (builtins.dict[builtins.str<8>, Any]<9>) - _fields<15> (Tuple[builtins.str<8>]) + _fields<15> (tuple[builtins.str<8>]) _make<16> _replace<17> _source<18> (builtins.str<8>) @@ -759,7 +759,7 @@ TypeInfo<2>( _asdict<12> _field_defaults<13> (builtins.dict[builtins.str<8>, Any]<9>) _field_types<14> (builtins.dict[builtins.str<8>, Any]<9>) - _fields<15> (Tuple[builtins.str<8>, builtins.str<8>]) + _fields<15> (tuple[builtins.str<8>, builtins.str<8>]) _make<16> _replace<17> _source<18> (builtins.str<8>) @@ -795,10 +795,10 @@ class A: pass a: Type[A] [out] ## target -NameExpr:3: Type[target.A<0>] +NameExpr:3: type[target.A<0>] ==> ## target -NameExpr:3: Type[target.A<0>] +NameExpr:3: type[target.A<0>] [case testTypeVar_types] import target @@ -1332,23 +1332,25 @@ MypyFile:1<1>( [case testMergeTypedDict_symtable] import target [file target.py] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass D = TypedDict('D', {'a': A}) d: D [file target.py.next] -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass D = TypedDict('D', {'a': A, 'b': int}) d: D [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [out] __main__: target: MypyFile<0> target: A: TypeInfo<1> D: TypeInfo<2> - TypedDict: FuncDef<3> + TypedDict: Var<3> d: Var<4>(TypedDict('target.D', {'a': target.A<1>})) ==> __main__: @@ -1356,7 +1358,7 @@ __main__: target: A: TypeInfo<1> D: TypeInfo<2> - TypedDict: FuncDef<3> + TypedDict: Var<3> d: Var<4>(TypedDict('target.D', {'a': target.A<1>, 'b': builtins.int<5>})) [case testNewType_symtable] @@ -1490,6 +1492,7 @@ from enum import Enum class A(Enum): X = 0 Y = 1 +[builtins fixtures/enum.pyi] [out] TypeInfo<0>( Name(target.A) @@ -1511,11 +1514,11 @@ TypeInfo<0>( [case testLiteralMerge] import target [file target.py] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal[3]) -> Literal['a']: pass bar: Literal[4] = 4 [file target.py.next] -from typing_extensions import Literal +from typing import Literal def foo(x: Literal['3']) -> Literal['b']: pass bar: Literal[5] = 5 [builtins fixtures/tuple.pyi] @@ -1525,7 +1528,7 @@ MypyFile:1<0>( Import:1(target)) MypyFile:1<1>( tmp/target.py - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) FuncDef:2<2>( foo Args( @@ -1543,7 +1546,7 @@ MypyFile:1<0>( Import:1(target)) MypyFile:1<1>( tmp/target.py - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) FuncDef:2<2>( foo Args( diff --git a/test-data/unit/parse-errors.test b/test-data/unit/parse-errors.test index c6b1c00a6169b..a192cc02d0cc2 100644 --- a/test-data/unit/parse-errors.test +++ b/test-data/unit/parse-errors.test @@ -12,102 +12,102 @@ def f() pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testUnexpectedIndent] 1 2 [out] -file:2: error: unexpected indent +file:2: error: Unexpected indent [case testInconsistentIndent] if x: 1 1 [out] -file:3: error: unexpected indent +file:3: error: Unexpected indent [case testInconsistentIndent2] if x: 1 1 [out] -file:3: error: unindent does not match any outer indentation level +file:3: error: Unindent does not match any outer indentation level [case testInvalidBinaryOp] 1> a* a+1* [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testDoubleStar] **a [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testMissingSuperClass] class A(: pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testUnexpectedEof] if 1: [out] -file:1: error: unexpected EOF while parsing +file:1: error: Expected an indented block [case testInvalidKeywordArguments1] f(x=y, z) [out] -file:1: error: positional argument follows keyword argument +file:1: error: Positional argument follows keyword argument [case testInvalidKeywordArguments2] f(**x, y) [out] -file:1: error: positional argument follows keyword argument unpacking +file:1: error: Positional argument follows keyword argument unpacking [case testInvalidBareAsteriskAndVarArgs2] def f(*x: A, *) -> None: pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidBareAsteriskAndVarArgs3] def f(*, *x: A) -> None: pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidBareAsteriskAndVarArgs4] def f(*, **x: A) -> None: pass [out] -file:1: error: named arguments must follow bare * +file:1: error: Named arguments must follow bare * [case testInvalidBareAsterisk1] def f(*) -> None: pass [out] -file:1: error: named arguments must follow bare * +file:1: error: Named arguments must follow bare * [case testInvalidBareAsterisk2] def f(x, *) -> None: pass [out] -file:1: error: named arguments must follow bare * +file:1: error: Named arguments must follow bare * [case testInvalidFuncDefArgs1] def f(x = y, x): pass [out] -file:1: error: non-default argument follows default argument +file:1: error: Non-default argument follows default argument [case testInvalidFuncDefArgs3] def f(**x, y): pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidFuncDefArgs4] def f(**x, y=x): pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidTypeComment] 0 @@ -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 @@ -281,153 +288,153 @@ file:1: error: Missing parentheses in call to 'print'. Did you mean print(1)? [case testInvalidConditionInConditionalExpression] 1 if 2, 3 else 4 [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidConditionInConditionalExpression2] 1 if x for y in z else 4 [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidConditionInConditionalExpression3] 1 if x else for y in z [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testYieldFromNotRightParameter] def f(): yield from [out] -file:2: error: invalid syntax +file:2: error: Invalid syntax [case testYieldFromAfterReturn] def f(): return yield from h() [out] -file:2: error: invalid syntax +file:2: error: Invalid syntax [case testImportDotModule] import .x [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testImportDot] import . [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidFunctionName] def while(): pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testInvalidEllipsis1] ...0 ..._ ...a [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testBlockStatementInSingleLineIf] if 1: if 2: pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testBlockStatementInSingleLineIf2] if 1: while 2: pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testBlockStatementInSingleLineIf3] if 1: for x in y: pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testUnexpectedEllipsis] a = a... [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testParseErrorBeforeUnicodeLiteral] x u'y' [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testParseErrorInExtendedSlicing] x[:, [out] -file:1: error: unexpected EOF while parsing +file:1: error: Unexpected EOF while parsing [case testParseErrorInExtendedSlicing2] x[:,:: [out] -file:1: error: unexpected EOF while parsing +file:1: error: Unexpected EOF while parsing [case testParseErrorInExtendedSlicing3] x[:,: [out] -file:1: error: unexpected EOF while parsing +file:1: error: Unexpected EOF while parsing [case testInvalidEncoding] # foo # coding: uft-8 [out] -file:0: error: unknown encoding: uft-8 +file:0: error: Unknown encoding: uft-8 [case testInvalidEncoding2] # coding=Uft.8 [out] -file:0: error: unknown encoding: Uft.8 +file:0: error: Unknown encoding: Uft.8 [case testInvalidEncoding3] #!/usr/bin python # vim: set fileencoding=uft8 : [out] -file:0: error: unknown encoding: uft8 +file:0: error: Unknown encoding: uft8 [case testDoubleEncoding] # coding: uft8 # coding: utf8 # The first coding cookie should be used and fail. [out] -file:0: error: unknown encoding: uft8 +file:0: error: Unknown encoding: uft8 [case testDoubleEncoding2] # Again the first cookie should be used and fail. # coding: uft8 # coding: utf8 [out] -file:0: error: unknown encoding: uft8 +file:0: error: Unknown encoding: uft8 [case testLongLiteralInPython3] 2L 0x2L [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testPython2LegacyInequalityInPython3] 1 <> 2 [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testLambdaInListComprehensionInPython3] ([ 0 for x in 1, 2 if 3 ]) [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testTupleArgListInPython3] def f(x, (y, z)): pass [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testBackquoteInPython3] `1 + 2` [out] -file:1: error: invalid syntax +file:1: error: Invalid syntax [case testSmartQuotes] foo = ‘bar’ [out] -file:1: error: invalid character in identifier +file:1: error: Invalid character '‘' (U+2018) [case testExceptCommaInPython3] try: @@ -435,4 +442,4 @@ try: except KeyError, IndexError: pass [out] -file:3: error: invalid syntax +file:3: error: Invalid syntax 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/parse-python314.test b/test-data/unit/parse-python314.test new file mode 100644 index 0000000000000..34fe753084f67 --- /dev/null +++ b/test-data/unit/parse-python314.test @@ -0,0 +1,5 @@ +[case testTemplateString] +x = 'mypy' +t'Hello {x}' +[out] +main:2: error: PEP 750 template strings are not yet supported diff --git a/test-data/unit/parse.test b/test-data/unit/parse.test index 10ceaa947fd4f..b1c0918365a69 100644 --- a/test-data/unit/parse.test +++ b/test-data/unit/parse.test @@ -548,7 +548,7 @@ MypyFile:1( NameExpr(x) NameExpr(y)) NameExpr(z) - Tuple[int?, a?[c?]])) + tuple[int?, a?[c?]])) [case testMultipleVarDef2] (xx, z, i) = 1 # type: (a[c], Any, int) @@ -560,7 +560,7 @@ MypyFile:1( NameExpr(z) NameExpr(i)) IntExpr(1) - Tuple[a?[c?], Any?, int?])) + tuple[a?[c?], Any?, int?])) [case testMultipleVarDef3] (xx, (z, i)) = 1 # type: (a[c], (Any, int)) @@ -573,7 +573,7 @@ MypyFile:1( NameExpr(z) NameExpr(i))) IntExpr(1) - Tuple[a?[c?], Tuple[Any?, int?]])) + tuple[a?[c?], tuple[Any?, int?]])) [case testAnnotateAssignmentViaSelf] class A: @@ -617,7 +617,7 @@ MypyFile:1( TupleExpr:2( IntExpr(1) IntExpr(2)) - Tuple[foo?, bar?])) + tuple[foo?, bar?])) [case testWhitespaceAndCommentAnnotation] x = 1#type:int @@ -932,33 +932,32 @@ MypyFile:1( [case testNotAsBinaryOp] x not y [out] -main:1: error: invalid syntax +main:1: error: Invalid syntax [out version==3.10.0] -main:1: error: invalid syntax. Perhaps you forgot a comma? +main:1: error: Invalid syntax. Perhaps you forgot a comma? [case testNotIs] -x not is y # E: invalid syntax +x not is y # E: Invalid syntax [out] [case testBinaryNegAsBinaryOp] 1 ~ 2 [out] -main:1: error: invalid syntax +main:1: error: Invalid syntax [out version==3.10.0] -main:1: error: invalid syntax. Perhaps you forgot a comma? +main:1: error: Invalid syntax. Perhaps you forgot a comma? -[case testSliceInList39] -# flags: --python-version 3.9 +[case testSliceInList] x = [1, 2][1:2] [out] MypyFile:1( - AssignmentStmt:2( + AssignmentStmt:1( NameExpr(x) - IndexExpr:2( - ListExpr:2( + IndexExpr:1( + ListExpr:1( IntExpr(1) IntExpr(2)) - SliceExpr:2( + SliceExpr:1( IntExpr(1) IntExpr(2))))) @@ -3171,10 +3170,10 @@ MypyFile:1( IndexExpr:1( NameExpr(a) TupleExpr:1( - SliceExpr:-1( + SliceExpr:1( ) - SliceExpr:-1( + SliceExpr:1( ))))) @@ -3186,10 +3185,10 @@ MypyFile:1( IndexExpr:1( NameExpr(a) TupleExpr:1( - SliceExpr:-1( + SliceExpr:1( IntExpr(1) IntExpr(2)) - SliceExpr:-1( + SliceExpr:1( ))))) @@ -3201,13 +3200,29 @@ MypyFile:1( IndexExpr:1( NameExpr(a) TupleExpr:1( - SliceExpr:-1( + SliceExpr:1( IntExpr(1) IntExpr(2) IntExpr(3)) Ellipsis IntExpr(1))))) +[case testParseExtendedSlicing4] +m[*index, :] +[out] +main:1: error: Invalid syntax +[out version>=3.11] +MypyFile:1( + ExpressionStmt:1( + IndexExpr:1( + NameExpr(m) + TupleExpr:1( + StarExpr:1( + NameExpr(index)) + SliceExpr:1( + + ))))) + [case testParseIfExprInDictExpr] test = { 'spam': 'eggs' if True else 'bacon' } [out] diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test index 9969c2894c36e..314befa11b94a 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 @@ -206,3 +213,23 @@ from typedpkg_ns.a.bbb import bf [file dummy.py.2] [out] [out2] + +[case testTypedNamespaceSubpackage] +# pkgs: typedpkg_ns_nested +import our +[file our/__init__.py] +import our.bar +import our.foo +[file our/bar.py] +from typedpkg_ns.b import Something +[file our/foo.py] +import typedpkg_ns.a + +[file dummy.py.2] + +[out] +our/bar.py:1: error: Skipping analyzing "typedpkg_ns.b": module is installed, but missing library stubs or py.typed marker +our/bar.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +[out2] +our/bar.py:1: error: Skipping analyzing "typedpkg_ns.b": module is installed, but missing library stubs or py.typed marker +our/bar.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports 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/plugins/dyn_class.py b/test-data/unit/plugins/dyn_class.py index 18e948e3dd2af..1471267b24ee5 100644 --- a/test-data/unit/plugins/dyn_class.py +++ b/test-data/unit/plugins/dyn_class.py @@ -6,7 +6,7 @@ from mypy.plugin import ClassDefContext, DynamicClassDefContext, Plugin from mypy.types import Instance, get_proper_type -DECL_BASES = set() +DECL_BASES: set[str] = set() class DynPlugin(Plugin): diff --git a/test-data/unit/plugins/magic_method.py b/test-data/unit/plugins/magic_method.py new file mode 100644 index 0000000000000..fc220ab44748e --- /dev/null +++ b/test-data/unit/plugins/magic_method.py @@ -0,0 +1,24 @@ +from mypy.types import LiteralType, AnyType, TypeOfAny, Type +from mypy.plugin import Plugin, MethodContext +from typing import Callable, Optional + +# If radd exists, there shouldn't be an error. If it doesn't exist, then there will be an error +def type_add(ctx: MethodContext) -> Type: + ctx.api.fail("fail", ctx.context) + return AnyType(TypeOfAny.from_error) + +def type_radd(ctx: MethodContext) -> Type: + return LiteralType(7, fallback=ctx.api.named_generic_type('builtins.int', [])) + + +class TestPlugin(Plugin): + + def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]: + if fullname == 'builtins.int.__add__': + return type_add + if fullname == 'builtins.int.__radd__': + return type_radd + return None + +def plugin(version: str) -> type[TestPlugin]: + return TestPlugin diff --git a/test-data/unit/pythoneval-asyncio.test b/test-data/unit/pythoneval-asyncio.test index 4a185557495bb..e1f0f861eef3c 100644 --- a/test-data/unit/pythoneval-asyncio.test +++ b/test-data/unit/pythoneval-asyncio.test @@ -25,11 +25,7 @@ async def greet_every_two_seconds() -> None: print('After', n) n += 1 -loop = asyncio.get_event_loop() -try: - loop.run_until_complete(greet_every_two_seconds()) -finally: - loop.close() +asyncio.run(greet_every_two_seconds()) [out] Prev 0 After 0 @@ -56,9 +52,7 @@ async def print_sum(x: int, y: int) -> None: result = await compute(x, y) # The type of result will be int (is extracted from Future[int] print("%s + %s = %s" % (x, y, result)) -loop = asyncio.get_event_loop() -loop.run_until_complete(print_sum(1, 2)) -loop.close() +asyncio.run(print_sum(1, 2)) [out] Compute 1 + 2 ... 1 + 2 = 3 @@ -72,12 +66,13 @@ async def slow_operation(future: 'Future[str]') -> None: await asyncio.sleep(0.01) future.set_result('Future is done!') -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) -loop.run_until_complete(future) -print(future.result()) -loop.close() +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) + await future + print(future.result()) + +asyncio.run(main()) [out] Future is done! @@ -95,10 +90,13 @@ def got_result(future: 'Future[str]') -> None: print(future.result()) loop.stop() -loop = asyncio.get_event_loop() # type: AbstractEventLoop -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) # Here create a task with the function. (The Task need a Future[T] as first argument) -future.add_done_callback(got_result) # and assign the callback to the future +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) # Here create a task with the function. (The Task need a Future[T] as first argument) + future.add_done_callback(got_result) # and assign the callback to the future + +loop = asyncio.new_event_loop() # type: AbstractEventLoop +loop.run_until_complete(main()) try: loop.run_forever() finally: @@ -119,13 +117,14 @@ async def factorial(name, number) -> None: f *= i print("Task %s: factorial(%s) = %s" % (name, number, f)) -loop = asyncio.get_event_loop() -tasks = [ - asyncio.Task(factorial("A", 2)), - asyncio.Task(factorial("B", 3)), - asyncio.Task(factorial("C", 4))] -loop.run_until_complete(asyncio.wait(tasks)) -loop.close() +async def main() -> None: + tasks = [ + asyncio.Task(factorial("A", 2)), + asyncio.Task(factorial("B", 3)), + asyncio.Task(factorial("C", 4))] + await asyncio.wait(tasks) + +asyncio.run(main()) [out] Task A: Compute factorial(2)... Task B: Compute factorial(2)... @@ -144,6 +143,8 @@ from typing import Any import asyncio from asyncio import Future +future: Future[int] + async def h4() -> int: x = await future return x @@ -162,12 +163,14 @@ async def h() -> None: x = await h2() print("h: %s" % x) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[int] -future.set_result(42) -loop.run_until_complete(h()) -print("Outside %s" % future.result()) -loop.close() +async def main() -> None: + global future + future = asyncio.Future() + future.set_result(42) + await h() + print("Outside %s" % future.result()) + +asyncio.run(main()) [out] h3: 42 h2: 42 @@ -182,13 +185,13 @@ from asyncio import Future async def h4() -> "Future[int]": await asyncio.sleep(0.01) - f = asyncio.Future() #type: Future[int] + f = asyncio.Future() # type: Future[int] return f async def h3() -> "Future[Future[int]]": x = await h4() x.set_result(42) - f = asyncio.Future() #type: Future[Future[int]] + f = asyncio.Future() # type: Future[Future[int]] f.set_result(x) return f @@ -205,9 +208,7 @@ async def h() -> None: print(normalize(y)) print(normalize(x)) -loop = asyncio.get_event_loop() -loop.run_until_complete(h()) -loop.close() +asyncio.run(h()) [out] Before 42 @@ -221,6 +222,8 @@ from typing import Any import asyncio from asyncio import Future +future: Future["A"] + class A: def __init__(self, x: int) -> None: self.x = x @@ -229,12 +232,14 @@ async def h() -> None: x = await future print("h: %s" % x.x) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[A] -future.set_result(A(42)) -loop.run_until_complete(h()) -print("Outside %s" % future.result().x) -loop.close() +async def main() -> None: + global future + future = asyncio.Future() + future.set_result(A(42)) + await h() + print("Outside %s" % future.result().x) + +asyncio.run(main()) [out] h: 42 Outside 42 @@ -255,11 +260,7 @@ async def test() -> None: await greet() x = await greet() # Error -loop = asyncio.get_event_loop() -try: - loop.run_until_complete(test()) -finally: - loop.close() +asyncio.run(test()) [out] _program.py:11: error: Function does not return a value (it only ever returns None) @@ -277,10 +278,7 @@ async def print_sum(x: int, y: int) -> None: result = await compute(x, y) print("%s + %s = %s" % (x, y, result)) -loop = asyncio.get_event_loop() -loop.run_until_complete(print_sum(1, 2)) -loop.close() - +asyncio.run(print_sum(1, 2)) [out] _program.py:8: error: Incompatible return value type (got "str", expected "int") @@ -293,12 +291,13 @@ async def slow_operation(future: 'Future[str]') -> None: await asyncio.sleep(1) future.set_result(42) # Error -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) -loop.run_until_complete(future) -print(future.result()) -loop.close() +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) + await future + print(future.result()) + +asyncio.run(main()) [out] _program.py:7: error: Argument 1 to "set_result" of "Future" has incompatible type "int"; expected "str" @@ -312,12 +311,13 @@ async def slow_operation(future: 'Future[int]') -> None: await asyncio.sleep(1) future.set_result(42) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) # Error -loop.run_until_complete(future) -print(future.result()) -loop.close() +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) # Error + await future + print(future.result()) + +asyncio.run(main()) [out] _program.py:11: error: Argument 1 to "slow_operation" has incompatible type "Future[str]"; expected "Future[int]" @@ -328,14 +328,15 @@ from asyncio import Future async def slow_operation(future: 'Future[int]') -> None: await asyncio.sleep(1) - future.set_result('42') #Try to set an str as result to a Future[int] - -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) # Error -loop.run_until_complete(future) -print(future.result()) -loop.close() + future.set_result('42') # Try to set an str as result to a Future[int] + +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) # Error + await future + print(future.result()) + +asyncio.run(main()) [out] _program.py:7: error: Argument 1 to "set_result" of "Future" has incompatible type "str"; expected "int" _program.py:11: error: Argument 1 to "slow_operation" has incompatible type "Future[str]"; expected "Future[int]" @@ -354,11 +355,13 @@ def got_result(future: 'Future[int]') -> None: print(future.result()) loop.stop() -loop = asyncio.get_event_loop() # type: AbstractEventLoop -future = asyncio.Future() # type: Future[str] -asyncio.Task(slow_operation(future)) -future.add_done_callback(got_result) # Error +async def main() -> None: + future = asyncio.Future() # type: Future[str] + asyncio.Task(slow_operation(future)) + future.add_done_callback(got_result) # Error +loop = asyncio.new_event_loop() +loop.run_until_complete(main()) try: loop.run_forever() finally: @@ -374,13 +377,13 @@ from asyncio import Future async def h4() -> Future[int]: await asyncio.sleep(1) - f = asyncio.Future() #type: Future[int] + f = asyncio.Future() # type: Future[int] return f async def h3() -> Future[Future[Future[int]]]: x = await h4() x.set_result(42) - f = asyncio.Future() #type: Future[Future[int]] + f = asyncio.Future() # type: Future[Future[int]] f.set_result(x) return f @@ -393,9 +396,7 @@ async def h() -> None: print(y) print(x) -loop = asyncio.get_event_loop() -loop.run_until_complete(h()) -loop.close() +asyncio.run(h()) [out] _program.py:16: error: Incompatible return value type (got "Future[Future[int]]", expected "Future[Future[Future[int]]]") @@ -407,13 +408,13 @@ from asyncio import Future async def h4() -> Future[int]: await asyncio.sleep(1) - f = asyncio.Future() #type: Future[int] + f = asyncio.Future() # type: Future[int] return f async def h3() -> Future[int]: x = await h4() x.set_result(42) - f = asyncio.Future() #type: Future[Future[int]] + f = asyncio.Future() # type: Future[Future[int]] f.set_result(x) return f @@ -424,9 +425,7 @@ async def h() -> None: print(y) print(x) -loop = asyncio.get_event_loop() -loop.run_until_complete(h()) -loop.close() +asyncio.run(h()) [out] _program.py:16: error: Incompatible return value type (got "Future[Future[int]]", expected "Future[int]") _program.py:16: note: Maybe you forgot to use "await"? @@ -437,6 +436,8 @@ from typing import Any import asyncio from asyncio import Future +future: Future["A"] + class A: def __init__(self, x: int) -> None: self.x = x @@ -446,16 +447,18 @@ class B: self.x = x async def h() -> None: - x = await future # type: B # Error + x = await future # type: B # Error print("h: %s" % x.x) -loop = asyncio.get_event_loop() -future = asyncio.Future() # type: Future[A] -future.set_result(A(42)) -loop.run_until_complete(h()) -loop.close() +async def main() -> None: + global future + future = asyncio.Future() + future.set_result(A(42)) + await h() + +asyncio.run(main()) [out] -_program.py:15: error: Incompatible types in assignment (expression has type "A", variable has type "B") +_program.py:17: error: Incompatible types in assignment (expression has type "A", variable has type "B") [case testForwardRefToBadAsyncShouldNotCrash_newsemanal] from typing import TypeVar diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 89f01bff963ee..8d6a78bfe470f 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 - -[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") +f(abs(-1)) +f(abs(-1.2)) +f(abs(Ab())) -[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 +BB.f(None) +f(BB.x) -[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 - - -[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 @@ -283,7 +242,7 @@ 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: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] @@ -293,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[io._WrappedBuffer]" -_program.py:2: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_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[_io._BufferedReaderStream]" _program.py:5: note: Revealed type is "typing.IO[Any]" [case testOpenReturnTypeInferenceSpecialCases] @@ -304,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[_io._BufferedReaderStream]" +_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "_io.BufferedReader[_io._BufferedReaderStream]" _testOpenReturnTypeInferenceSpecialCases.py:4: note: Revealed type is "typing.IO[Any]" [case testPathOpenReturnTypeInference] @@ -317,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[io._WrappedBuffer]" -_program.py:4: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" -_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[_io._BufferedReaderStream]" _program.py:7: note: Revealed type is "typing.IO[Any]" [case testPathOpenReturnTypeInferenceSpecialCases] @@ -330,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[io._WrappedBuffer]" -_program.py:4: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]" +_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] @@ -657,7 +616,7 @@ def f(*args: str) -> str: return args[0] map(f, ['x']) map(f, [1]) [out] -_program.py:4: error: Argument 1 to "map" has incompatible type "Callable[[VarArg(str)], str]"; expected "Callable[[int], str]" +_program.py:4: error: Argument 1 to "map" has incompatible type "def f(*args: str) -> str"; expected "Callable[[int], str]" [case testMapStr] import typing @@ -667,8 +626,8 @@ a + 1 [out] _testMapStr.py:4: error: No overload variant of "__add__" of "list" matches argument type "int" _testMapStr.py:4: note: Possible overload variants: -_testMapStr.py:4: note: def __add__(self, List[str], /) -> List[str] -_testMapStr.py:4: note: def [_S] __add__(self, List[_S], /) -> List[Union[_S, str]] +_testMapStr.py:4: note: def __add__(self, list[str], /) -> list[str] +_testMapStr.py:4: note: def [_S] __add__(self, list[_S], /) -> list[Union[_S, str]] [case testRelativeImport] import typing @@ -803,7 +762,7 @@ def p(t: Tuple[str, ...]) -> None: ''.startswith(('x', b'y')) [out] _program.py:6: error: "str" not callable -_program.py:8: error: Argument 1 to "startswith" of "str" has incompatible type "Tuple[str, bytes]"; expected "Union[str, Tuple[str, ...]]" +_program.py:8: error: Argument 1 to "startswith" of "str" has incompatible type "tuple[str, bytes]"; expected "Union[str, tuple[str, ...]]" [case testMultiplyTupleByInteger] n = 4 @@ -812,8 +771,8 @@ t + 1 [out] _program.py:3: error: No overload variant of "__add__" of "tuple" matches argument type "int" _program.py:3: note: Possible overload variants: -_program.py:3: note: def __add__(self, Tuple[str, ...], /) -> Tuple[str, ...] -_program.py:3: note: def [_T] __add__(self, Tuple[_T, ...], /) -> Tuple[Union[str, _T], ...] +_program.py:3: note: def __add__(self, tuple[str, ...], /) -> tuple[str, ...] +_program.py:3: note: def [_T] __add__(self, tuple[_T, ...], /) -> tuple[Union[str, _T], ...] [case testMultiplyTupleByIntegerReverse] n = 4 @@ -822,8 +781,8 @@ t + 1 [out] _program.py:3: error: No overload variant of "__add__" of "tuple" matches argument type "int" _program.py:3: note: Possible overload variants: -_program.py:3: note: def __add__(self, Tuple[str, ...], /) -> Tuple[str, ...] -_program.py:3: note: def [_T] __add__(self, Tuple[_T, ...], /) -> Tuple[Union[str, _T], ...] +_program.py:3: note: def __add__(self, tuple[str, ...], /) -> tuple[str, ...] +_program.py:3: note: def [_T] __add__(self, tuple[_T, ...], /) -> tuple[Union[str, _T], ...] [case testDictWithKeywordArgs] from typing import Dict, Any, List @@ -835,10 +794,9 @@ d4 = dict(a=1, b='') # type: Dict[str, Any] result = dict(x=[], y=[]) # type: Dict[str, List[str]] [out] _program.py:3: error: Dict entry 1 has incompatible type "str": "str"; expected "str": "int" -_program.py:5: error: "Dict[str, int]" has no attribute "xyz" +_program.py:5: error: "dict[str, int]" has no attribute "xyz" [case testDefaultDict] -# flags: --new-type-inference import typing as t from collections import defaultdict @@ -864,11 +822,11 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]): MyDDict(dict)['0'] MyDDict(dict)[0] [out] -_program.py:7: error: Argument 1 to "defaultdict" has incompatible type "Type[List[Any]]"; expected "Optional[Callable[[], str]]" -_program.py:10: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" -_program.py:10: error: Incompatible types in assignment (expression has type "int", target has type "str") -_program.py:20: error: Argument 1 to "tst" has incompatible type "defaultdict[str, List[Never]]"; expected "defaultdict[int, List[Never]]" -_program.py:24: error: Invalid index type "str" for "MyDDict[Dict[Never, Never]]"; expected type "int" +_program.py:6: error: Argument 1 to "defaultdict" has incompatible type "type[list[_T]]"; expected "Optional[Callable[[], str]]" +_program.py:9: error: Invalid index type "str" for "defaultdict[int, str]"; expected type "int" +_program.py:9: error: Incompatible types in assignment (expression has type "int", target has type "str") +_program.py:19: error: Argument 1 to "tst" has incompatible type "defaultdict[str, list[Never]]"; expected "defaultdict[int, list[Never]]" +_program.py:23: error: Invalid index type "str" for "MyDDict[dict[Never, Never]]"; expected type "int" [case testCollectionsAliases] import typing as t @@ -954,7 +912,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 @@ -1046,7 +1004,7 @@ a[0] = 'x', 1 a[1] = 2, 'y' a[:] = [('z', 3)] [out] -_program.py:4: error: Incompatible types in assignment (expression has type "Tuple[int, str]", target has type "Tuple[str, int]") +_program.py:4: error: Incompatible types in assignment (expression has type "tuple[int, str]", target has type "tuple[str, int]") [case testContextManager] import contextlib @@ -1067,35 +1025,55 @@ reveal_type(g) with f('') as s: reveal_type(s) [out] -_program.py:13: note: Revealed type is "def (x: builtins.int) -> contextlib._GeneratorContextManager[builtins.str]" -_program.py:14: note: Revealed type is "def (*x: builtins.str) -> contextlib._GeneratorContextManager[builtins.int]" +_program.py:13: note: Revealed type is "def (x: builtins.int) -> contextlib._GeneratorContextManager[builtins.str, None, None]" +_program.py:14: note: Revealed type is "def (*x: builtins.str) -> contextlib._GeneratorContextManager[builtins.int, None, None]" _program.py:16: error: Argument 1 to "f" has incompatible type "str"; expected "int" _program.py:17: note: Revealed type is "builtins.str" [case testTypedDictGet] # Test that TypedDict get plugin works with typeshed stubs -from mypy_extensions import TypedDict +from typing import TypedDict class A: pass -D = TypedDict('D', {'x': int, 'y': str}) -d: D -reveal_type(d.get('x')) -reveal_type(d.get('y')) -reveal_type(d.get('z')) -d.get() -s = '' -reveal_type(d.get(s)) -[out] -_testTypedDictGet.py:6: note: Revealed type is "Union[builtins.int, None]" -_testTypedDictGet.py:7: note: Revealed type is "Union[builtins.str, None]" -_testTypedDictGet.py:8: note: Revealed type is "builtins.object" -_testTypedDictGet.py:9: error: All overload variants of "get" of "Mapping" require at least one argument -_testTypedDictGet.py:9: note: Possible overload variants: -_testTypedDictGet.py:9: note: def get(self, str, /) -> object -_testTypedDictGet.py:9: note: def [_T] get(self, str, /, default: object) -> object -_testTypedDictGet.py:11: note: Revealed type is "builtins.object" +D_total = TypedDict('D_total', {'x': int, 'y': str}, total=True) +D_not_total = TypedDict('D_not_total', {'x': int, 'y': str}, total=False) + +def test_total(d: D_total) -> None: + reveal_type(d.get('x')) + reveal_type(d.get('y')) + reveal_type(d.get('z')) + d.get() + s = '' + reveal_type(d.get(s)) + +def test_not_total(d: D_not_total) -> None: + reveal_type(d.get('x')) + reveal_type(d.get('y')) + reveal_type(d.get('z')) + d.get() + s = '' + reveal_type(d.get(s)) +[out] +_testTypedDictGet.py:8: note: Revealed type is "builtins.int" +_testTypedDictGet.py:9: note: Revealed type is "builtins.str" +_testTypedDictGet.py:10: note: Revealed type is "builtins.object" +_testTypedDictGet.py:11: error: All overload variants of "get" of "Mapping" require at least one argument +_testTypedDictGet.py:11: note: Possible overload variants: +_testTypedDictGet.py:11: note: def get(self, str, /) -> object +_testTypedDictGet.py:11: note: def get(self, str, /, default: object) -> object +_testTypedDictGet.py:11: note: def [_T] get(self, str, /, default: _T) -> object +_testTypedDictGet.py:13: note: Revealed type is "builtins.object" +_testTypedDictGet.py:16: note: Revealed type is "Union[builtins.int, None]" +_testTypedDictGet.py:17: note: Revealed type is "Union[builtins.str, None]" +_testTypedDictGet.py:18: note: Revealed type is "builtins.object" +_testTypedDictGet.py:19: error: All overload variants of "get" of "Mapping" require at least one argument +_testTypedDictGet.py:19: note: Possible overload variants: +_testTypedDictGet.py:19: note: def get(self, str, /) -> object +_testTypedDictGet.py:19: note: def get(self, str, /, default: object) -> object +_testTypedDictGet.py:19: note: def [_T] get(self, str, /, default: _T) -> object +_testTypedDictGet.py:21: note: Revealed type is "builtins.object" [case testTypedDictMappingMethods] -from mypy_extensions import TypedDict +from typing import TypedDict Cell = TypedDict('Cell', {'value': int}) c = Cell(value=42) for x in c: @@ -1139,8 +1117,7 @@ def foo(mymap) -> Optional[MyNamedTuple]: [out] [case testCanConvertTypedDictToAnySuperclassOfMapping] -from mypy_extensions import TypedDict -from typing import Sized, Iterable, Container +from typing import Sized, TypedDict, Iterable, Container Point = TypedDict('Point', {'x': int, 'y': int}) @@ -1151,12 +1128,12 @@ c: Container[str] = p o: object = p it2: Iterable[int] = p [out] -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: error: Incompatible types in assignment (expression has type "Point", variable has type "Iterable[int]") -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: Following member(s) of "Point" have conflicts: -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: Expected: -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: def __iter__(self) -> Iterator[int] -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: Got: -_testCanConvertTypedDictToAnySuperclassOfMapping.py:11: note: def __iter__(self) -> Iterator[str] +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: error: Incompatible types in assignment (expression has type "Point", variable has type "Iterable[int]") +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: Following member(s) of "Point" have conflicts: +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: Expected: +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: def __iter__(self) -> Iterator[int] +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: Got: +_testCanConvertTypedDictToAnySuperclassOfMapping.py:10: note: def __iter__(self) -> Iterator[str] [case testAsyncioGatherPreciseType-xfail] # Mysteriously regressed in #11905 @@ -1236,8 +1213,8 @@ other = 4 + get_c_type() + 5 reveal_type(res) reveal_type(other) [out] -_testMetaclassOpAccess.py:21: note: Revealed type is "Type[_testMetaclassOpAccess.A]" -_testMetaclassOpAccess.py:22: note: Revealed type is "Type[_testMetaclassOpAccess.C]" +_testMetaclassOpAccess.py:21: note: Revealed type is "type[_testMetaclassOpAccess.A]" +_testMetaclassOpAccess.py:22: note: Revealed type is "type[_testMetaclassOpAccess.C]" [case testMetaclassOpAccessUnion] from typing import Type, Union @@ -1327,8 +1304,8 @@ class C: __slots__: List[int] = [] [out] _testInvalidSlots.py:3: error: Invalid type for "__slots__" (actual type "int", expected type "Union[str, Iterable[str]]") -_testInvalidSlots.py:5: error: Invalid type for "__slots__" (actual type "Tuple[int, int]", expected type "Union[str, Iterable[str]]") -_testInvalidSlots.py:7: error: Invalid type for "__slots__" (actual type "List[int]", expected type "Union[str, Iterable[str]]") +_testInvalidSlots.py:5: error: Invalid type for "__slots__" (actual type "tuple[int, int]", expected type "Union[str, Iterable[str]]") +_testInvalidSlots.py:7: error: Invalid type for "__slots__" (actual type "list[int]", expected type "Union[str, Iterable[str]]") [case testDictWithStarStarSpecialCase] from typing import Dict @@ -1339,7 +1316,7 @@ def f() -> Dict[int, str]: def d() -> Dict[int, int]: return {} [out] -_testDictWithStarStarSpecialCase.py:4: error: Unpacked dict entry 1 has incompatible type "Dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]" +_testDictWithStarStarSpecialCase.py:4: error: Unpacked dict entry 1 has incompatible type "dict[int, int]"; expected "SupportsKeysAndGetItem[int, str]" [case testLoadsOfOverloads] from typing import overload, Any, TypeVar, Iterable, List, Dict, Callable, Union @@ -1399,7 +1376,7 @@ X = namedtuple('X', ['a', 'b']) x = X(a=1, b='s') [out] -_testNamedTupleNew.py:12: note: Revealed type is "Tuple[builtins.int, fallback=_testNamedTupleNew.Child]" +_testNamedTupleNew.py:12: note: Revealed type is "tuple[builtins.int, fallback=_testNamedTupleNew.Child]" [case testNamedTupleTypeInheritanceSpecialCase] from typing import NamedTuple, Tuple @@ -1425,7 +1402,7 @@ _testNamedTupleTypeInheritanceSpecialCase.py:8: note: Revealed type is "builtins _testNamedTupleTypeInheritanceSpecialCase.py:9: note: Revealed type is "builtins.tuple[builtins.str, ...]" _testNamedTupleTypeInheritanceSpecialCase.py:10: note: Revealed type is "builtins.dict[builtins.str, Any]" _testNamedTupleTypeInheritanceSpecialCase.py:17: error: Argument 1 to "accepts_named_tuple" has incompatible type "int"; expected "NamedTuple" -_testNamedTupleTypeInheritanceSpecialCase.py:18: error: Argument 1 to "accepts_named_tuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple" +_testNamedTupleTypeInheritanceSpecialCase.py:18: error: Argument 1 to "accepts_named_tuple" has incompatible type "tuple[int, int]"; expected "NamedTuple" [case testNewAnalyzerBasicTypeshed_newsemanal] from typing import Dict, List, Tuple @@ -1466,8 +1443,8 @@ frozenset({1}) == [1] # Error {1: 2}.values() == {2} # Error {1: 2}.keys() == [1] # OK [out] -_testStrictEqualityAllowlist.py:5: error: Non-overlapping equality check (left operand type: "FrozenSet[int]", right operand type: "List[int]") -_testStrictEqualityAllowlist.py:12: error: Non-overlapping equality check (left operand type: "dict_values[int, int]", right operand type: "Set[int]") +_testStrictEqualityAllowlist.py:5: error: Non-overlapping equality check (left operand type: "frozenset[int]", right operand type: "list[int]") +_testStrictEqualityAllowlist.py:12: error: Non-overlapping equality check (left operand type: "dict_values[int, int]", right operand type: "set[int]") [case testUnreachableWithStdlibContextManagers] # mypy: warn-unreachable, strict-optional @@ -1521,9 +1498,9 @@ y: str if isinstance(x, int): reveal_type(x) [out] -_testIsInstanceAdHocIntersectionWithStrAndBytes.py:3: error: Subclass of "str" and "bytes" cannot exist: would have incompatible method signatures +_testIsInstanceAdHocIntersectionWithStrAndBytes.py:3: error: Subclass of "str" and "bytes" cannot exist: have distinct disjoint bases _testIsInstanceAdHocIntersectionWithStrAndBytes.py:4: error: Statement is unreachable -_testIsInstanceAdHocIntersectionWithStrAndBytes.py:6: error: Subclass of "str" and "int" cannot exist: would have incompatible method signatures +_testIsInstanceAdHocIntersectionWithStrAndBytes.py:6: error: Subclass of "str" and "int" cannot exist: have distinct disjoint bases _testIsInstanceAdHocIntersectionWithStrAndBytes.py:7: error: Statement is unreachable [case testAsyncioFutureWait] @@ -1548,24 +1525,24 @@ note: A user-defined top-level module with name "typing" is not supported # flags: --ignore-missing-imports import scribe # No Python 3 stubs available for scribe from scribe import x -import python2 # Python 3 stubs available for python2 +import pytz # Python 3 stubs available for pytz import foobar_asdf import jack # This has a stubs package but was never bundled with mypy, so ignoring works [out] -_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "python2" -_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-six" +_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "pytz" +_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-pytz" _testIgnoreImportIfNoPython3StubAvailable.py:4: note: (or run "mypy --install-types" to install all missing stub packages) _testIgnoreImportIfNoPython3StubAvailable.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports [case testNoPython3StubAvailable] import scribe from scribe import x -import python2 +import pytz [out] _testNoPython3StubAvailable.py:1: error: Cannot find implementation or library stub for module named "scribe" _testNoPython3StubAvailable.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "python2" -_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-six" +_testNoPython3StubAvailable.py:3: error: Library stubs not installed for "pytz" +_testNoPython3StubAvailable.py:3: note: Hint: "python3 -m pip install types-pytz" _testNoPython3StubAvailable.py:3: note: (or run "mypy --install-types" to install all missing stub packages) @@ -1593,21 +1570,9 @@ if isinstance(obj, Hashable): if isinstance(obj, Awaitable): reveal_type(obj) [out] -_testSpecialTypingProtocols.py:6: note: Revealed type is "Tuple[builtins.int]" +_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 @@ -1686,8 +1651,8 @@ def foo(x: T) -> T: return x [out] _testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm" -_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "Union[Type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnion.py:25: note: Revealed type is "type[builtins.int] | builtins.str" +_testTypeAliasWithNewStyleUnion.py:28: note: Revealed type is "type[builtins.int] | builtins.str" [case testTypeAliasWithNewStyleUnionInStub] import m @@ -1705,8 +1670,8 @@ f: m.F reveal_type(f) [file m.pyi] -from typing import Type, Callable -from typing_extensions import Literal, TypeAlias +from typing import Type, Callable, Literal +from typing_extensions import TypeAlias Foo = Literal[1, 2] reveal_type(Foo) @@ -1740,12 +1705,12 @@ CU4: TypeAlias = int | Callable[[str | bool], str] [out] m.pyi:5: note: Revealed type is "typing._SpecialForm" m.pyi:22: note: Revealed type is "typing._SpecialForm" -_testTypeAliasWithNewStyleUnionInStub.py:3: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:5: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:7: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:9: note: Revealed type is "Union[Type[builtins.int], builtins.str]" -_testTypeAliasWithNewStyleUnionInStub.py:11: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" -_testTypeAliasWithNewStyleUnionInStub.py:13: note: Revealed type is "Union[builtins.str, Type[builtins.int]]" +_testTypeAliasWithNewStyleUnionInStub.py:3: note: Revealed type is "Union[type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:5: note: Revealed type is "Union[type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:7: note: Revealed type is "Union[type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:9: note: Revealed type is "Union[type[builtins.int], builtins.str]" +_testTypeAliasWithNewStyleUnionInStub.py:11: note: Revealed type is "Union[builtins.str, type[builtins.int]]" +_testTypeAliasWithNewStyleUnionInStub.py:13: note: Revealed type is "Union[builtins.str, type[builtins.int]]" [case testEnumNameWorkCorrectlyOn311] # flags: --python-version 3.11 @@ -1765,7 +1730,7 @@ reveal_type(e.foo) reveal_type(E.Y.foo) [out] _testEnumNameWorkCorrectlyOn311.py:11: note: Revealed type is "builtins.str" -_testEnumNameWorkCorrectlyOn311.py:12: note: Revealed type is "Union[Literal[1]?, Literal[2]?]" +_testEnumNameWorkCorrectlyOn311.py:12: note: Revealed type is "Literal[1]? | Literal[2]?" _testEnumNameWorkCorrectlyOn311.py:13: note: Revealed type is "Literal['X']?" _testEnumNameWorkCorrectlyOn311.py:14: note: Revealed type is "builtins.int" _testEnumNameWorkCorrectlyOn311.py:15: note: Revealed type is "builtins.int" @@ -1781,11 +1746,11 @@ D: TypeAlias = str | int _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type _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:4: error: Unsupported left operand type for | ("type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("Type[str]") +_testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Unsupported left operand type for | ("type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Unsupported left operand type for | ("Type[str]") +_testTypeAliasNotSupportedWithNewStyleUnion.py:6: error: Unsupported left operand type for | ("type[str]") [case testTypedDictUnionGetFull] from typing import Dict @@ -1834,15 +1799,15 @@ WrongEllipsis = tuple[float, float, ...] | str # Error reveal_type(tuple[int, str]((1, "x"))) [out] -_testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "Union[builtins.str, Tuple[builtins.float, builtins.float, builtins.str]]" -_testTupleWithDifferentArgsPy310.py:16: note: Revealed type is "Union[Tuple[builtins.float], builtins.str]" -_testTupleWithDifferentArgsPy310.py:17: note: Revealed type is "Union[builtins.tuple[builtins.float, ...], builtins.str]" -_testTupleWithDifferentArgsPy310.py:18: note: Revealed type is "Tuple[builtins.float, builtins.str]" +_testTupleWithDifferentArgsPy310.py:15: note: Revealed type is "builtins.str | tuple[builtins.float, builtins.float, builtins.str]" +_testTupleWithDifferentArgsPy310.py:16: note: Revealed type is "tuple[builtins.float] | builtins.str" +_testTupleWithDifferentArgsPy310.py:17: note: Revealed type is "builtins.tuple[builtins.float, ...] | builtins.str" +_testTupleWithDifferentArgsPy310.py:18: note: Revealed type is "tuple[builtins.float, builtins.str]" _testTupleWithDifferentArgsPy310.py:19: note: Revealed type is "builtins.tuple[builtins.float, ...]" -_testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[Tuple[builtins.int, builtins.str]]" +_testTupleWithDifferentArgsPy310.py:20: note: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]" _testTupleWithDifferentArgsPy310.py:26: error: Invalid type: try using Literal[1] instead? _testTupleWithDifferentArgsPy310.py:27: error: Unexpected "..." -_testTupleWithDifferentArgsPy310.py:29: note: Revealed type is "Tuple[builtins.int, builtins.str]" +_testTupleWithDifferentArgsPy310.py:29: note: Revealed type is "tuple[builtins.int, builtins.str]" [case testEnumIterMetaInference] import socket @@ -1983,8 +1948,8 @@ Foo().__dict__ = {} [out] _testInferenceOfDunderDictOnClassObjects.py:2: note: Revealed type is "types.MappingProxyType[builtins.str, Any]" _testInferenceOfDunderDictOnClassObjects.py:3: note: Revealed type is "builtins.dict[builtins.str, Any]" -_testInferenceOfDunderDictOnClassObjects.py:4: error: Property "__dict__" defined in "type" is read-only -_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "Dict[Never, Never]", variable has type "MappingProxyType[str, Any]") +_testInferenceOfDunderDictOnClassObjects.py:4: error: Cannot assign to final attribute "__dict__" +_testInferenceOfDunderDictOnClassObjects.py:4: error: Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "MappingProxyType[str, Any]") [case testTypeVarTuple] # flags: --python-version=3.11 @@ -2024,13 +1989,13 @@ a2 = replace() a2 = replace(a, x='spam') a2 = replace(a, x=42, q=42) [out] +_testDataclassReplace.py:4: note: "replace" of "A" defined here _testDataclassReplace.py:9: note: Revealed type is "_testDataclassReplace.A" _testDataclassReplace.py:10: error: Too few arguments for "replace" _testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int" _testDataclassReplace.py:12: error: Unexpected keyword argument "q" for "replace" of "A" [case testGenericInferenceWithTuple] -# flags: --new-type-inference from typing import TypeVar, Callable, Tuple T = TypeVar("T") @@ -2042,7 +2007,6 @@ x: Tuple[str, ...] = f(tuple) [out] [case testGenericInferenceWithDataclass] -# flags: --new-type-inference from typing import Any, Collection, List from dataclasses import dataclass, field @@ -2055,7 +2019,6 @@ class A: [out] [case testGenericInferenceWithItertools] -# flags: --new-type-inference from typing import TypeVar, Tuple from itertools import groupby K = TypeVar("K") @@ -2216,3 +2179,47 @@ class C3[T1, T2](tuple[T1, ...]): 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]" + +[case testReturnFallbackInferenceDict] +# Requires full dict stubs. +from typing import Dict, Mapping, TypeVar, Union + +K = TypeVar("K") +V = TypeVar("V") +K2 = TypeVar("K2") +V2 = TypeVar("V2") + +def func(one: Dict[K, V], two: Mapping[K2, V2]) -> Dict[Union[K, K2], Union[V, V2]]: + ... + +def caller(arg1: Mapping[K, V], arg2: Mapping[K2, V2]) -> Dict[Union[K, K2], Union[V, V2]]: + _arg1 = arg1 if isinstance(arg1, dict) else dict(arg1) + return func(_arg1, arg2) diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index 6e0fdba8aaa34..6e80683ad957b 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -118,7 +118,7 @@ class A(object): [outfile report/html/n.py.html] - + @@ -172,7 +172,7 @@ T = TypeVar('T') [outfile report/html/n.py.html] - + @@ -214,7 +214,7 @@ def bar(x): [outfile report/html/n.py.html] - + @@ -255,7 +255,7 @@ old_stdout = sys.stdout [outfile report/html/n.py.html] - + @@ -306,10 +306,7 @@ Total 1 11 90.91% [file i.py] from enum import Enum -from mypy_extensions import TypedDict -from typing import NewType, NamedTuple, TypeVar - -from typing import TypeVar +from typing import NewType, NamedTuple, TypedDict, TypeVar T = TypeVar('T') # no error @@ -490,7 +487,7 @@ DisplayToSource = Callable[[int], int] [outfile report/html/n.py.html] - + @@ -532,7 +529,7 @@ namespace_packages = True [outfile report/html/folder/subfolder/something.py.html] - + @@ -550,3 +547,52 @@ namespace_packages = True + +[case testLinecountReportCrashOnNamespacePackages] +# cmd: mypy --linecount-report report -p folder +-- Regression test for https://github.com/python/mypy/issues/15979 +[file folder/subfolder/something.py] +class Something: + pass + +[case testReportIsADirectoryErrorCrashOnNamespacePackages] +# cmd: mypy --linecoverage-report report -p folder +-- Regression test for https://github.com/python/mypy/issues/18128 +-- "IsADirectoryError for namespace packages when using --linecoverage-report" +[file folder/subfolder/something.py] +class Something: + pass + +[case testReportCoberturaCrashOnNamespacePackages] +# cmd: mypy --cobertura-xml-report report -p folder +-- Regression test for https://github.com/python/mypy/issues/19843 +[file folder/subfolder/something.py] +-- This output is not important, but due to the way tests are run we need to check it. +[outfile report/cobertura.xml] + + + $PWD + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-data/unit/semanal-basic.test b/test-data/unit/semanal-basic.test index 169769f06a00b..773092cab6238 100644 --- a/test-data/unit/semanal-basic.test +++ b/test-data/unit/semanal-basic.test @@ -316,7 +316,7 @@ MypyFile:1( NameExpr(x* [l]) NameExpr(None [builtins.None]))))) -[case testGlobaWithinMethod] +[case testGlobalWithinMethod] x = None class A: def f(self): @@ -521,7 +521,7 @@ MypyFile:1( NameExpr(True [builtins.True]) Literal[True]?) AssignmentStmt:8( - NameExpr(n* [__main__.n] = None) + NameExpr(n* [__main__.n]) CallExpr:8( NameExpr(func [__main__.func]) Args()))) diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index b14358509f85a..7022da01eeaf3 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -472,7 +472,7 @@ MypyFile:1( Args( Var(cls) Var(z)) - def (cls: Type[__main__.A], z: builtins.int) -> builtins.str + def (cls: type[__main__.A], z: builtins.int) -> builtins.str Class Block:3( PassStmt:3()))))) @@ -492,7 +492,7 @@ MypyFile:1( f Args( Var(cls)) - def (cls: Type[__main__.A]) -> builtins.str + def (cls: type[__main__.A]) -> builtins.str Class Block:3( PassStmt:3()))))) @@ -583,7 +583,7 @@ MypyFile:1( ClassDef:2( A TupleType( - Tuple[builtins.int, builtins.str]) + tuple[builtins.int, builtins.str]) BaseType( builtins.tuple[Union[builtins.int, builtins.str], ...]) PassStmt:2())) diff --git a/test-data/unit/semanal-classvar.test b/test-data/unit/semanal-classvar.test index a7bcec0324dc2..62151666c011a 100644 --- a/test-data/unit/semanal-classvar.test +++ b/test-data/unit/semanal-classvar.test @@ -52,7 +52,7 @@ MypyFile:1( AssignmentStmt:3( NameExpr(x [m]) IntExpr(1) - Any))) + builtins.int))) [case testClassVarWithTypeVar] @@ -207,14 +207,3 @@ class B: pass [out] main:4: error: ClassVar can only be used for assignments in class body - -[case testClassVarWithTypeVariable] -from typing import ClassVar, TypeVar, Generic, List - -T = TypeVar('T') - -class Some(Generic[T]): - error: ClassVar[T] # E: ClassVar cannot contain type variables - nested: ClassVar[List[List[T]]] # E: ClassVar cannot contain type variables - ok: ClassVar[int] -[out] diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 33c8f9b80aa01..8053b33b94fdc 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -361,84 +361,84 @@ main:2: error: "yield" outside function [case testInvalidLvalues1] 1 = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [out version>=3.10] -main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues2] (1) = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [out version>=3.10] -main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues3] (1, 1) = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [case testInvalidLvalues4] [1, 1] = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [case testInvalidLvalues6] x = y = z = 1 # ok x, (y, 1) = 1 [out] -main:2: error: cannot assign to literal +main:2: error: Cannot assign to literal [case testInvalidLvalues7] x, [y, 1] = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [case testInvalidLvalues8] x, [y, [z, 1]] = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [case testInvalidLvalues9] x, (y) = 1 # ok x, (y, (z, z)) = 1 # ok x, (y, (z, 1)) = 1 [out] -main:3: error: cannot assign to literal +main:3: error: Cannot assign to literal [case testInvalidLvalues10] x + x = 1 [out] -main:1: error: cannot assign to operator +main:1: error: Cannot assign to operator [out version>=3.10] -main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to expression here. Maybe you meant '==' instead of '='? [case testInvalidLvalues11] -x = 1 [out] -main:1: error: cannot assign to operator +main:1: error: Cannot assign to operator [out version>=3.10] -main:1: error: cannot assign to expression here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to expression here. Maybe you meant '==' instead of '='? [case testInvalidLvalues12] 1.1 = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [out version>=3.10] -main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues13] 'x' = 1 [out] -main:1: error: cannot assign to literal +main:1: error: Cannot assign to literal [out version>=3.10] -main:1: error: cannot assign to literal here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to literal here. Maybe you meant '==' instead of '='? [case testInvalidLvalues14] x() = 1 [out] -main:1: error: cannot assign to function call +main:1: error: Cannot assign to function call [out version>=3.10] -main:1: error: cannot assign to function call here. Maybe you meant '==' instead of '='? +main:1: error: Cannot assign to function call here. Maybe you meant '==' instead of '='? [case testTwoStarExpressions] a, *b, *c = 1 @@ -492,15 +492,15 @@ main:2: error: can't use starred expression here x = 1 del x(1) [out] -main:2: error: cannot delete function call +main:2: error: Cannot delete function call [case testInvalidDel2] x = 1 del x + 1 [out] -main:2: error: cannot delete operator +main:2: error: Cannot delete operator [out version>=3.10] -main:2: error: cannot delete expression +main:2: error: Cannot delete expression [case testInvalidDel3] del z # E: Name "z" is not defined @@ -537,13 +537,6 @@ def f(y: t): x = t main:4: error: "t" is a type variable and only valid in type context main:5: error: "t" is a type variable and only valid in type context -[case testMissingSelf] -import typing -class A: - def f(): pass -[out] -main:3: error: Method must have at least one argument. Did you forget the "self" argument? - [case testInvalidBaseClass] import typing class A(B): pass @@ -558,15 +551,6 @@ def f() -> None: super().y main:2: error: "super" used outside class main:3: error: "super" used outside class -[case testMissingSelfInMethod] -import typing -class A: - def f() -> None: pass - def g(): pass -[out] -main:3: error: Method must have at least one argument. Did you forget the "self" argument? -main:4: error: Method must have at least one argument. Did you forget the "self" argument? - [case testMultipleMethodDefinition] import typing class A: @@ -897,9 +881,9 @@ import typing def f(): pass f() = 1 # type: int [out] -main:3: error: cannot assign to function call +main:3: error: Cannot assign to function call [out version>=3.10] -main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='? +main:3: error: Cannot assign to function call here. Maybe you meant '==' instead of '='? [case testIndexedAssignmentWithTypeDeclaration] import typing @@ -975,9 +959,9 @@ x, y = 1, 2 # type: int # E: Tuple type expected for multiple variables a = 1 a() = None # type: int [out] -main:2: error: cannot assign to function call +main:2: error: Cannot assign to function call [out version>=3.10] -main:2: error: cannot assign to function call here. Maybe you meant '==' instead of '='? +main:2: error: Cannot assign to function call here. Maybe you meant '==' instead of '='? [case testInvalidLvalueWithExplicitType2] a = 1 @@ -1015,6 +999,12 @@ class A(Generic[T]): # E: Free type variable expected in Generic[...] [out] +[case testRedeclaredTypeVarWithinNestedGenericClass] +from typing import Generic, Iterable, TypeVar +T = TypeVar('T') +class A(Generic[T]): + class B(Iterable[T]): pass # E: Type variable "T" is bound by an outer class + [case testIncludingGenericTwiceInBaseClassList] from typing import Generic, TypeVar T = TypeVar('T') @@ -1060,6 +1050,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()" @@ -1214,7 +1220,7 @@ class A: @overload # E: Decorators on top of @property are not supported @property def f(self) -> int: pass - @property # E: Only supported top decorator is @f.setter + @property # E: Only supported top decorators are "@f.setter" and "@f.deleter" @overload def f(self) -> int: pass [builtins fixtures/property.pyi] @@ -1283,7 +1289,7 @@ main:2: note: Did you forget to import it from "typing"? (Suggestion: "from typi def f(): pass with f() as 1: pass [out] -main:2: error: cannot assign to literal +main:2: error: Cannot assign to literal [case testInvalidTypeAnnotation] import typing @@ -1297,9 +1303,9 @@ import typing def f() -> None: f() = 1 # type: int [out] -main:3: error: cannot assign to function call +main:3: error: Cannot assign to function call [out version>=3.10] -main:3: error: cannot assign to function call here. Maybe you meant '==' instead of '='? +main:3: error: Cannot assign to function call here. Maybe you meant '==' instead of '='? [case testInvalidReferenceToAttributeOfOuterClass] class A: @@ -1395,12 +1401,13 @@ class N: # E: Name "N" already defined on line 2 [out] [case testDuplicateDefTypedDict] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) class Point: # E: Name "Point" already defined on line 2 pass [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] @@ -1459,7 +1466,7 @@ from typing_extensions import Unpack from typing import Tuple heterogenous_tuple: Tuple[Unpack[Tuple[int, str]]] -homogenous_tuple: Tuple[Unpack[Tuple[int, ...]]] +homogeneous_tuple: Tuple[Unpack[Tuple[int, ...]]] bad: Tuple[Unpack[int]] # E: "int" cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/semanal-literal.test b/test-data/unit/semanal-literal.test index 4c100add6ec04..53191f692c8c7 100644 --- a/test-data/unit/semanal-literal.test +++ b/test-data/unit/semanal-literal.test @@ -1,9 +1,9 @@ [case testLiteralSemanalBasicAssignment] -from typing_extensions import Literal +from typing import Literal foo: Literal[3] [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) AssignmentStmt:2( NameExpr(foo [__main__.foo]) TempNode:2( @@ -11,12 +11,12 @@ MypyFile:1( Literal[3])) [case testLiteralSemanalInFunction] -from typing_extensions import Literal +from typing import Literal def foo(a: Literal[1], b: Literal[" foo "]) -> Literal[True]: pass [builtins fixtures/bool.pyi] [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Literal]) + ImportFrom:1(typing, [Literal]) FuncDef:2( foo Args( diff --git a/test-data/unit/semanal-namedtuple.test b/test-data/unit/semanal-namedtuple.test index 16944391da862..62bd87f1995a6 100644 --- a/test-data/unit/semanal-namedtuple.test +++ b/test-data/unit/semanal-namedtuple.test @@ -10,10 +10,10 @@ MypyFile:1( ImportFrom:1(collections, [namedtuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[Any])) + NamedTupleExpr:2(N, tuple[Any])) FuncDef:3( f - def () -> Tuple[Any, fallback=__main__.N] + def () -> tuple[Any, fallback=__main__.N] Block:3( PassStmt:3()))) @@ -27,10 +27,10 @@ MypyFile:1( ImportFrom:1(collections, [namedtuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[Any, Any])) + NamedTupleExpr:2(N, tuple[Any, Any])) FuncDef:3( f - def () -> Tuple[Any, Any, fallback=__main__.N] + def () -> tuple[Any, Any, fallback=__main__.N] Block:3( PassStmt:3()))) @@ -44,10 +44,10 @@ MypyFile:1( ImportFrom:1(collections, [namedtuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[Any, Any])) + NamedTupleExpr:2(N, tuple[Any, Any])) FuncDef:3( f - def () -> Tuple[Any, Any, fallback=__main__.N] + def () -> tuple[Any, Any, fallback=__main__.N] Block:3( PassStmt:3()))) @@ -61,10 +61,10 @@ MypyFile:1( ImportFrom:1(collections, [namedtuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[Any, Any])) + NamedTupleExpr:2(N, tuple[Any, Any])) FuncDef:3( f - def () -> Tuple[Any, Any, fallback=__main__.N] + def () -> tuple[Any, Any, fallback=__main__.N] Block:3( PassStmt:3()))) @@ -78,7 +78,7 @@ MypyFile:1( ImportFrom:1(typing, [NamedTuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[builtins.int, builtins.str]))) + NamedTupleExpr:2(N, tuple[builtins.int, builtins.str]))) [case testNamedTupleWithTupleFieldNamesWithItemTypes] from typing import NamedTuple @@ -90,7 +90,7 @@ MypyFile:1( ImportFrom:1(typing, [NamedTuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[builtins.int, builtins.str]))) + NamedTupleExpr:2(N, tuple[builtins.int, builtins.str]))) [case testNamedTupleBaseClass] from collections import namedtuple @@ -102,11 +102,11 @@ MypyFile:1( ImportFrom:1(collections, [namedtuple]) AssignmentStmt:2( NameExpr(N* [__main__.N]) - NamedTupleExpr:2(N, Tuple[Any])) + NamedTupleExpr:2(N, tuple[Any])) ClassDef:3( A TupleType( - Tuple[Any, fallback=__main__.N]) + tuple[Any, fallback=__main__.N]) BaseType( __main__.N) PassStmt:3())) @@ -121,7 +121,7 @@ MypyFile:1( ClassDef:2( A TupleType( - Tuple[Any, fallback=__main__.N@2]) + tuple[Any, fallback=__main__.N@2]) BaseType( __main__.N@2) PassStmt:2())) @@ -136,7 +136,7 @@ MypyFile:1( ClassDef:2( A TupleType( - Tuple[builtins.int, fallback=__main__.N@2]) + tuple[builtins.int, fallback=__main__.N@2]) BaseType( __main__.N@2) PassStmt:2())) @@ -239,7 +239,7 @@ MypyFile:1( ClassDef:4( A TupleType( - Tuple[builtins.int, fallback=__main__.N@4]) + tuple[builtins.int, fallback=__main__.N@4]) Decorators( NameExpr(final [typing.final])) BaseType( diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index c143805f4564d..a2e8691733efa 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -557,9 +557,9 @@ MypyFile:1( def f(x, y) -> None: del x, y + 1 [out] -main:2: error: cannot delete operator +main:2: error: Cannot delete operator [out version>=3.10] -main:2: error: cannot delete expression +main:2: error: Cannot delete expression [case testTry] class c: pass @@ -1127,7 +1127,7 @@ MypyFile:1( IntExpr(1))))) [case testConstantFold1] -from typing_extensions import Final +from typing import Final add: Final = 15 + 47 add_mul: Final = (2 + 3) * 5 sub: Final = 7 - 11 @@ -1140,7 +1140,7 @@ lshift0: Final = 5 << 0 rshift0: Final = 13 >> 0 [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Final]) + ImportFrom:1(typing, [Final]) AssignmentStmt:2( NameExpr(add [__main__.add] = 62) OpExpr:2( @@ -1216,7 +1216,7 @@ MypyFile:1( Literal[13]?)) [case testConstantFold2] -from typing_extensions import Final +from typing import Final neg1: Final = -5 neg2: Final = --1 neg3: Final = -0 @@ -1231,7 +1231,7 @@ p3: Final = 0**0 s: Final = 'x' + 'y' [out] MypyFile:1( - ImportFrom:1(typing_extensions, [Final]) + ImportFrom:1(typing, [Final]) AssignmentStmt:2( NameExpr(neg1 [__main__.neg1] = -5) UnaryExpr:2( diff --git a/test-data/unit/semanal-typealiases.test b/test-data/unit/semanal-typealiases.test index 88d2341343508..e2c1c48631574 100644 --- a/test-data/unit/semanal-typealiases.test +++ b/test-data/unit/semanal-typealiases.test @@ -177,12 +177,12 @@ MypyFile:1( ImportFrom:1(typing, [Tuple]) AssignmentStmt:2( NameExpr(T* [__main__.T]) - TypeAliasExpr(Tuple[builtins.int, builtins.str])) + TypeAliasExpr(tuple[builtins.int, builtins.str])) FuncDef:3( f Args( Var(x)) - def (x: Tuple[builtins.int, builtins.str]) + def (x: tuple[builtins.int, builtins.str]) Block:3( PassStmt:3()))) @@ -439,8 +439,8 @@ MypyFile:1( ImportFrom:1(typing, [Union, Tuple, Any]) AssignmentStmt:2( NameExpr(A* [__main__.A]) - TypeAliasExpr(Union[builtins.int, Tuple[builtins.int, Any]])) + TypeAliasExpr(Union[builtins.int, tuple[builtins.int, Any]])) AssignmentStmt:3( NameExpr(a [__main__.a]) IntExpr(1) - Union[builtins.int, Tuple[builtins.int, Any]])) + Union[builtins.int, tuple[builtins.int, Any]])) diff --git a/test-data/unit/semanal-typeddict.test b/test-data/unit/semanal-typeddict.test index 9ce89155c3081..936ed1aed3ee6 100644 --- a/test-data/unit/semanal-typeddict.test +++ b/test-data/unit/semanal-typeddict.test @@ -2,40 +2,43 @@ -- TODO: Implement support for this syntax. --[case testCanCreateTypedDictTypeWithDictCall] ---from mypy_extensions import TypedDict +--from typing import TypedDict --Point = TypedDict('Point', dict(x=int, y=int)) --[builtins fixtures/dict.pyi] +--[typing fixtures/typing-typeddict.pyi] --[out] --MypyFile:1( --- ImportFrom:1(mypy_extensions, [TypedDict]) +-- ImportFrom:1(typing, [TypedDict]) -- AssignmentStmt:2( -- NameExpr(Point* [__main__.Point]) -- TypedDictExpr:2(Point))) [case testCanCreateTypedDictTypeWithDictLiteral] -from mypy_extensions import TypedDict +from typing import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] MypyFile:1( - ImportFrom:1(mypy_extensions, [TypedDict]) + ImportFrom:1(typing, [TypedDict]) AssignmentStmt:2( NameExpr(Point* [__main__.Point]) TypedDictExpr:2(Point))) [case testTypedDictWithDocString] -from mypy_extensions import TypedDict +from typing import TypedDict class A(TypedDict): """foo""" x: str [builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] [out] MypyFile:1( - ImportFrom:1(mypy_extensions, [TypedDict]) + ImportFrom:1(typing, [TypedDict]) ClassDef:2( A BaseType( - mypy_extensions._TypedDict) + typing._TypedDict) ExpressionStmt:3( StrExpr(foo)) AssignmentStmt:4( diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 83c44738f055a..a91d334af146a 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -163,7 +163,7 @@ MypyFile:1( TupleExpr:4( NameExpr(None [builtins.None]) NameExpr(None [builtins.None])) - Tuple[__main__.A, __main__.B]) + tuple[__main__.A, __main__.B]) AssignmentStmt:5( NameExpr(x* [__main__.x]) TupleExpr:5( @@ -366,7 +366,7 @@ MypyFile:1( ExpressionStmt:2( CastExpr:2( NameExpr(None [builtins.None]) - Tuple[builtins.int, builtins.str]))) + tuple[builtins.int, builtins.str]))) [case testCastToFunctionType] from typing import Callable, cast @@ -493,7 +493,7 @@ MypyFile:1( f Args( Var(x)) - def [t] (x: Tuple[builtins.int, t`-1]) + def [t] (x: tuple[builtins.int, t`-1]) Block:4( PassStmt:4()))) @@ -694,11 +694,11 @@ MypyFile:1( AssignmentStmt:3( NameExpr(t1 [__main__.t1]) NameExpr(None [builtins.None]) - Tuple[builtins.object]) + tuple[builtins.object]) AssignmentStmt:4( NameExpr(t2 [__main__.t2]) NameExpr(None [builtins.None]) - Tuple[builtins.int, builtins.object])) + tuple[builtins.int, builtins.object])) [case testVariableLengthTuple] from typing import Tuple diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 69781e9d21436..161f14e8aea77 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -30,12 +30,10 @@ def g(b=-1, c=0): ... def f(a, b: int = 2) -> None: ... def g(b: int = -1, c: int = 0) -> None: ... -[case testDefaultArgNone] +[case testFuncDefaultArgNone] def f(x=None): ... [out] -from _typeshed import Incomplete - -def f(x: Incomplete | None = None) -> None: ... +def f(x=None) -> None: ... [case testDefaultArgBool] def f(x=True, y=False): ... @@ -238,13 +236,24 @@ class C: def __init__(self, x: str) -> None: ... [case testSelfAssignment] +from mod import A +from typing import Any, Dict, Union class C: def __init__(self): + self.a: A = A() self.x = 1 x.y = 2 + self.y: Dict[str, Any] = {} + self.z: Union[int, str, bool, None] = None [out] +from mod import A +from typing import Any + class C: + a: A x: int + y: dict[str, Any] + z: int | str | bool | None def __init__(self) -> None: ... [case testSelfAndClassBodyAssignment] @@ -327,10 +336,24 @@ class A: ... class B(A): ... [case testDecoratedFunction] +import x + @decorator def foo(x): ... + +@x.decorator +def bar(x): ... + +@decorator(x=1, y={"a": 1}) +def foo_bar(x): ... [out] +import x + +@decorator def foo(x) -> None: ... +@x.decorator +def bar(x) -> None: ... +def foo_bar(x) -> None: ... [case testMultipleAssignment] x, y = 1, 2 @@ -1225,6 +1248,7 @@ from typing import Generic from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... [out] from typing import Generic from typing_extensions import TypeVarTuple, Unpack @@ -1233,11 +1257,14 @@ Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... + [case testGenericClassTypeVarTuple_semanal] from typing import Generic from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... [out] from typing import Generic from typing_extensions import TypeVarTuple, Unpack @@ -1246,11 +1273,14 @@ Ts = TypeVarTuple('Ts') class D(Generic[Unpack[Ts]]): ... +def callback(func: Callable[[Unpack[Ts]], None], *args: Unpack[Ts]) -> None: ... + [case testGenericClassTypeVarTuplePy311] # flags: --python-version=3.11 from typing import Generic, TypeVarTuple Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... [out] from typing import Generic, TypeVarTuple @@ -1258,11 +1288,14 @@ Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... + [case testGenericClassTypeVarTuplePy311_semanal] # flags: --python-version=3.11 from typing import Generic, TypeVarTuple Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... [out] from typing import Generic, TypeVarTuple @@ -1270,6 +1303,8 @@ Ts = TypeVarTuple('Ts') class D(Generic[*Ts]): ... +def callback(func: Callable[[*Ts], None], *args: *Ts) -> None: ... + [case testObjectBaseClass] class A(object): ... [out] @@ -1342,7 +1377,7 @@ async def f(a): [out] async def f(a) -> None: ... -[case testInferOptionalOnlyFunc] +[case testMethodDefaultArgNone] class A: x = None def __init__(self, a=None): @@ -1354,8 +1389,8 @@ from _typeshed import Incomplete class A: x: Incomplete - def __init__(self, a: Incomplete | None = None) -> None: ... - def method(self, a: Incomplete | None = None) -> None: ... + def __init__(self, a=None) -> None: ... + def method(self, a=None) -> None: ... [case testAnnotationImportsFrom] import foo @@ -1507,6 +1542,19 @@ from typing import TypeVar T = TypeVar('T') alias = Union[T, List[T]] +[case testExplicitTypeAlias] +from typing import TypeAlias + +explicit_alias: TypeAlias = tuple[int, str] +implicit_alias = list[int] + +[out] +from typing import TypeAlias + +explicit_alias: TypeAlias = tuple[int, str] +implicit_alias = list[int] + + [case testEllipsisAliasPreserved] alias = Tuple[int, ...] @@ -2568,32 +2616,29 @@ class A(metaclass=abc.ABCMeta): @abc.abstractmethod def x(self): ... -[case testClassWithNameIncompleteOrOptional] +[case testClassWithNameIncomplete] Y = object() -def g(x=None): pass +def g(): + yield 1 x = g() class Incomplete: pass -def Optional(): - return 0 - [out] from _typeshed import Incomplete as _Incomplete +from collections.abc import Generator Y: _Incomplete -def g(x: _Incomplete | None = None) -> None: ... +def g() -> Generator[_Incomplete]: ... x: _Incomplete class Incomplete: ... -def Optional(): ... - [case testExportedNameImported] # modules: main a b from a import C @@ -3078,18 +3123,14 @@ import attrs @attrs.define class C: - x = attrs.field() + x: int = attrs.field() [out] -from _typeshed import Incomplete +import attrs +@attrs.define class C: - x: Incomplete - def __init__(self, x) -> None: ... - def __lt__(self, other): ... - def __le__(self, other): ... - def __gt__(self, other): ... - def __ge__(self, other): ... + x: int = attrs.field() [case testNamedTupleInClass] from collections import namedtuple @@ -3736,6 +3777,8 @@ class MatchNames: def __exit__(self, type, value, traceback): ... [out] +import types + class MismatchNames: def __exit__(self, tp: type[BaseException] | None, val: BaseException | None, tb: types.TracebackType | None) -> None: ... @@ -4030,8 +4073,9 @@ def i(x=..., y=..., z=...) -> None: ... [case testDataclass] import dataclasses import dataclasses as dcs -from dataclasses import dataclass, InitVar, KW_ONLY +from dataclasses import dataclass, field, Field, InitVar, KW_ONLY from dataclasses import dataclass as dc +from datetime import datetime from typing import ClassVar @dataclasses.dataclass @@ -4046,6 +4090,10 @@ class X: h: int = 1 i: InitVar[str] j: InitVar = 100 + # Lambda not supported yet -> marked as Incomplete instead + k: str = Field( + default_factory=lambda: datetime.utcnow().isoformat(" ", timespec="seconds") + ) non_field = None @dcs.dataclass @@ -4063,7 +4111,8 @@ class V: ... [out] import dataclasses import dataclasses as dcs -from dataclasses import InitVar, KW_ONLY, dataclass, dataclass as dc +from _typeshed import Incomplete +from dataclasses import Field, InitVar, KW_ONLY, dataclass, dataclass as dc, field from typing import ClassVar @dataclasses.dataclass @@ -4072,12 +4121,13 @@ class X: b: str = ... c: ClassVar d: ClassVar = ... - f: list[int] = ... - g: int = ... + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) _: KW_ONLY h: int = ... i: InitVar[str] j: InitVar = ... + k: str = Field(default_factory=Incomplete) non_field = ... @dcs.dataclass @@ -4090,8 +4140,9 @@ class W: ... class V: ... [case testDataclass_semanal] -from dataclasses import InitVar, dataclass, field +from dataclasses import Field, InitVar, dataclass, field from typing import ClassVar +from datetime import datetime @dataclass class X: @@ -4105,13 +4156,18 @@ class X: h: int = 1 i: InitVar = 100 j: list[int] = field(default_factory=list) + # Lambda not supported yet -> marked as Incomplete instead + k: str = Field( + default_factory=lambda: datetime.utcnow().isoformat(" ", timespec="seconds") + ) non_field = None @dataclass(init=False, repr=False, frozen=True) class Y: ... [out] -from dataclasses import InitVar, dataclass +from _typeshed import Incomplete +from dataclasses import Field, InitVar, dataclass, field from typing import ClassVar @dataclass @@ -4121,13 +4177,13 @@ class X: c: str = ... d: ClassVar e: ClassVar = ... - f: list[int] = ... - g: int = ... + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) h: int = ... i: InitVar = ... - j: list[int] = ... + j: list[int] = field(default_factory=list) + k: str = Field(default_factory=Incomplete) non_field = ... - def __init__(self, a, b, c=..., *, g=..., h=..., i=..., j=...) -> None: ... @dataclass(init=False, repr=False, frozen=True) class Y: ... @@ -4155,7 +4211,7 @@ class X: class Y: ... [out] -from dataclasses import InitVar, KW_ONLY, dataclass +from dataclasses import InitVar, KW_ONLY, dataclass, field from typing import ClassVar @dataclass @@ -4164,14 +4220,13 @@ class X: b: str = ... c: ClassVar d: ClassVar = ... - f: list[int] = ... - g: int = ... + f: list[int] = field(init=False, default_factory=list) + g: int = field(default=2, kw_only=True) _: KW_ONLY h: int = ... i: InitVar[str] j: InitVar = ... non_field = ... - def __init__(self, a, b=..., *, g=..., h=..., i, j=...) -> None: ... @dataclass(init=False, repr=False, frozen=True) class Y: ... @@ -4216,7 +4271,6 @@ from dataclasses import dataclass @dataclass class X(missing.Base): a: int - def __init__(self, *generated_args, a, **generated_kwargs) -> None: ... @dataclass class Y(missing.Base): @@ -4224,7 +4278,171 @@ class Y(missing.Base): generated_args_: str generated_kwargs: float generated_kwargs_: float - def __init__(self, *generated_args__, generated_args, generated_args_, generated_kwargs, generated_kwargs_, **generated_kwargs__) -> None: ... + +[case testDataclassAliasPrinterVariations_semanal] +from dataclasses import dataclass, field + +@dataclass +class X: + a: int = field(default=-1) + b: set[int] = field(default={0}) + c: list[int] = field(default=[x for x in range(5)]) + d: dict[int, int] = field(default={x: x for x in range(5)}) + e: tuple[int, int] = field(default=(1, 2, 3)[1:]) + f: tuple[int, int] = field(default=(1, 2, 3)[:2]) + g: tuple[int, int] = field(default=(1, 2, 3)[::2]) + h: tuple[int] = field(default=(1, 2, 3)[1::2]) + +[out] +from _typeshed import Incomplete +from dataclasses import dataclass, field + +@dataclass +class X: + a: int = field(default=-1) + b: set[int] = field(default={0}) + c: list[int] = field(default=Incomplete) + d: dict[int, int] = field(default=Incomplete) + e: tuple[int, int] = field(default=(1, 2, 3)[1:]) + f: tuple[int, int] = field(default=(1, 2, 3)[:2]) + g: tuple[int, int] = field(default=(1, 2, 3)[::2]) + h: tuple[int] = field(default=(1, 2, 3)[1::2]) + +[case testDataclassTransform] +# dataclass_transform detection only works with semantic analysis. +# Test stubgen doesn't break too badly without it. +from typing_extensions import dataclass_transform + +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): + return cls + +@create_model +class X: + a: int + b: str = "hello" + +@typing_extensions.dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class Y(ModelBase): + a: int + b: str = "hello" + +@typing_extensions.dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class Z(metaclass=DCMeta): + a: int + b: str = "hello" + +[out] +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): ... + +class X: + a: int + b: str + +@typing_extensions.dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class Y(ModelBase): + a: int + b: str + +@typing_extensions.dataclass_transform(kw_only_default=True) +class DCMeta(type): ... + +class Z(metaclass=DCMeta): + a: int + b: str + +[case testDataclassTransformDecorator_semanal] +import typing_extensions +from dataclasses import field + +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): + return cls + +@create_model +class X: + a: int + b: str = "hello" + c: bool = field(default=True) + +[out] +import typing_extensions +from dataclasses import field + +@typing_extensions.dataclass_transform(kw_only_default=True) +def create_model(cls): ... + +@create_model +class X: + a: int + b: str = ... + c: bool = field(default=True) + +[case testDataclassTransformClass_semanal] +from dataclasses import field +from typing_extensions import dataclass_transform + +@dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class X(ModelBase): + a: int + b: str = "hello" + c: bool = field(default=True) + +[out] +from dataclasses import field +from typing_extensions import dataclass_transform + +@dataclass_transform(kw_only_default=True) +class ModelBase: ... + +class X(ModelBase): + a: int + b: str = ... + c: bool = field(default=True) + +[case testDataclassTransformMetaclass_semanal] +from dataclasses import field +from typing import Any +from typing_extensions import dataclass_transform + +def custom_field(*, default: bool, kw_only: bool) -> Any: ... + +@dataclass_transform(kw_only_default=True, field_specifiers=(custom_field,)) +class DCMeta(type): ... + +class X(metaclass=DCMeta): + a: int + b: str = "hello" + c: bool = field(default=True) # should be ignored, not field_specifier here + +class Y(X): + d: str = custom_field(default="Hello") + +[out] +from typing import Any +from typing_extensions import dataclass_transform + +def custom_field(*, default: bool, kw_only: bool) -> Any: ... + +@dataclass_transform(kw_only_default=True, field_specifiers=(custom_field,)) +class DCMeta(type): ... + +class X(metaclass=DCMeta): + a: int + b: str = ... + c: bool = ... + +class Y(X): + d: str = custom_field(default='Hello') [case testAlwaysUsePEP604Union] import typing @@ -4415,3 +4633,126 @@ class Test(Whatever, a=1, b='b', c=True, d=1.5, e=None, f=1j, g=b'123'): ... 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 + +@dataclass_transform() +class DCMeta(type): ... +class DC(metaclass=DCMeta): + x: str + +[out] +from typing_extensions import dataclass_transform + +@dataclass_transform() +class DCMeta(type): ... + +class DC(metaclass=DCMeta): + x: str + + +[case testIncompleteReturn] +from _typeshed import Incomplete + +def polar(*args, **kwargs) -> Incomplete: + ... + +[out] +from _typeshed import Incomplete + +def polar(*args, **kwargs) -> Incomplete: ... diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index d78cf0f179f2b..77e7763824d64 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -214,7 +214,7 @@ f( B()) [builtins fixtures/tuple-simple.pyi] [out] -CallExpr(6) : Tuple[A, B] +CallExpr(6) : tuple[A, B] CallExpr(7) : A CallExpr(8) : B @@ -255,7 +255,7 @@ NameExpr(6) : A NameExpr(6) : A MemberExpr(7) : A MemberExpr(7) : A -MemberExpr(7) : A +MemberExpr(7) : Any NameExpr(7) : A NameExpr(7) : A @@ -294,8 +294,8 @@ import typing x = () [builtins fixtures/primitives.pyi] [out] -NameExpr(2) : Tuple[()] -TupleExpr(2) : Tuple[()] +NameExpr(2) : tuple[()] +TupleExpr(2) : tuple[()] [case testInferTwoTypes] ## NameExpr @@ -313,8 +313,8 @@ def f() -> None: x = () [builtins fixtures/primitives.pyi] [out] -NameExpr(3) : Tuple[()] -TupleExpr(3) : Tuple[()] +NameExpr(3) : tuple[()] +TupleExpr(3) : tuple[()] -- Basic generics diff --git a/test-requirements.in b/test-requirements.in index 5a888811bfcda..556edf5077d2b 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -4,14 +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' +filelock>=3.3.0,<3.20.0 # latest version is not available on 3.9 that we still support +lxml>=5.3.0; python_version<'3.15' 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 -tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 +setuptools>=75.1.0 +tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.9 +pre_commit>=3.5.0 +platformdirs<4.5.0 # latest version is not available on 3.9 that we still support diff --git a/test-requirements.txt b/test-requirements.txt index f4fb4a20cce73..d8334108fc1d0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,61 +1,75 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.13 # by the following command: # # pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in # -attrs==23.1.0 +attrs==25.4.0 # via -r test-requirements.in -black==24.3.0 - # via -r test-requirements.in -click==8.1.7 - # via black -coverage==7.3.2 +cfgv==3.4.0 + # via pre-commit +coverage==7.10.7 # via pytest-cov -execnet==2.0.2 +distlib==0.4.0 + # via virtualenv +execnet==2.1.1 # via pytest-xdist -filelock==3.12.4 - # via -r test-requirements.in -iniconfig==2.0.0 +filelock==3.19.1 + # via + # -r test-requirements.in + # virtualenv +identify==2.6.15 + # via pre-commit +iniconfig==2.1.0 # via pytest -lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" +librt==0.7.3 ; platform_python_implementation != 'PyPy' + # via -r mypy-requirements.txt +lxml==6.0.2 ; python_version < "3.15" # via -r test-requirements.in -mypy-extensions==1.0.0 +mypy-extensions==1.1.0 + # via -r mypy-requirements.txt +nodeenv==1.9.1 + # via pre-commit +packaging==25.0 + # via pytest +pathspec==0.12.1 + # via -r mypy-requirements.txt +platformdirs==4.4.0 # via - # -r mypy-requirements.txt - # black -packaging==23.2 + # -r test-requirements.in + # virtualenv +pluggy==1.6.0 # via - # black # pytest -pathspec==0.11.2 - # via black -platformdirs==3.11.0 - # via black -pluggy==1.4.0 - # via pytest -psutil==5.9.6 + # pytest-cov +pre-commit==4.3.0 # via -r test-requirements.in -pytest==8.1.1 +psutil==7.1.0 + # via -r test-requirements.in +pygments==2.19.2 + # via pytest +pytest==8.4.2 # via # -r test-requirements.in # pytest-cov # pytest-xdist -pytest-cov==4.1.0 - # via -r test-requirements.in -pytest-xdist==3.3.1 +pytest-cov==7.0.0 # via -r test-requirements.in -ruff==0.2.0 +pytest-xdist==3.8.0 # via -r test-requirements.in -tomli==2.0.1 +pyyaml==6.0.3 + # via pre-commit +tomli==2.3.0 # via -r test-requirements.in -types-psutil==5.9.5.17 +types-psutil==7.0.0.20251001 # via -r build-requirements.txt -types-setuptools==68.2.0.0 +types-setuptools==80.9.0.20250822 # via -r build-requirements.txt -typing-extensions==4.12.2 +typing-extensions==4.15.0 # via -r mypy-requirements.txt +virtualenv==20.34.0 + # via pre-commit # The following packages are considered to be unsafe in a requirements file: -setuptools==70.0.0 +setuptools==80.9.0 # via -r test-requirements.in diff --git a/tox.ini b/tox.ini index c2abd05d7b6cb..65f67aba42a2f 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,8 @@ envlist = py310, py311, py312, + py313, + py314, docs, lint, type, @@ -15,19 +17,23 @@ isolated_build = true [testenv] description = run the test driver with {basepython} passenv = - PYTEST_XDIST_WORKER_COUNT PROGRAMDATA PROGRAMFILES(X86) PYTEST_ADDOPTS -deps = -rtest-requirements.txt + PYTEST_XDIST_WORKER_COUNT + PYTHON_COLORS +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 +43,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"))'